From 9cf1521775d2eda1df49c63bfe1485777efde945 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 18 Apr 2017 20:44:36 +0100 Subject: [PATCH 001/698] Hello world! --- .hgignore | 2 + Cargo.toml | 7 + src/data_forms.rs | 148 +++++++++++++++++++ src/disco.rs | 193 +++++++++++++++++++++++++ src/ecaps2.rs | 330 +++++++++++++++++++++++++++++++++++++++++++ src/error.rs | 23 +++ src/lib.rs | 9 ++ src/media_element.rs | 177 +++++++++++++++++++++++ src/ns.rs | 3 + 9 files changed, 892 insertions(+) create mode 100644 .hgignore create mode 100644 Cargo.toml create mode 100644 src/data_forms.rs create mode 100644 src/disco.rs create mode 100644 src/ecaps2.rs create mode 100644 src/error.rs create mode 100644 src/lib.rs create mode 100644 src/media_element.rs create mode 100644 src/ns.rs diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000000000000000000000000000000000000..a9d37c560c6ab8d4afbf47eda643e8c42e857716 --- /dev/null +++ b/.hgignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..10c55dd53824d3fe4df43a74c6b6b8975951ee11 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "xmpp-parsers" +version = "0.1.0" +authors = ["Emmanuel Gil Peyrot "] + +[dependencies] +minidom = "0.1.1" diff --git a/src/data_forms.rs b/src/data_forms.rs new file mode 100644 index 0000000000000000000000000000000000000000..f4eb70c834487de4db9b088974e2b782db1d0aa5 --- /dev/null +++ b/src/data_forms.rs @@ -0,0 +1,148 @@ +extern crate minidom; + +use std::str::FromStr; + +use minidom::Element; + +use error::Error; +use ns::{DATA_FORMS_NS, MEDIA_ELEMENT_NS}; + +use media_element::{MediaElement, parse_media_element}; + +#[derive(Debug)] +pub struct Field { + pub var: String, + pub type_: String, // TODO: use an enum here. + pub label: Option, + pub values: Vec, + pub media: Vec, +} + +#[derive(Debug, PartialEq)] +pub enum DataFormType { + Cancel, + Form, + Result_, + Submit, +} + +impl FromStr for DataFormType { + type Err = Error; + + fn from_str(s: &str) -> Result { + if s == "cancel" { + Ok(DataFormType::Cancel) + } else if s == "form" { + Ok(DataFormType::Form) + } else if s == "result" { + Ok(DataFormType::Result_) + } else if s == "submit" { + Ok(DataFormType::Submit) + } else { + Err(Error::ParseError("Unknown data form type.")) + } + } +} + +#[derive(Debug)] +pub struct DataForm { + pub type_: DataFormType, + pub form_type: Option, + pub fields: Vec, +} + +pub fn parse_data_form(root: &Element) -> Result { + assert!(root.is("x", DATA_FORMS_NS)); + let type_: DataFormType = match root.attr("type") { + Some(type_) => type_.parse()?, + None => return Err(Error::ParseError("Type attribute on data form is mandatory.")), + }; + let mut fields = vec!(); + let mut form_type = None; + for field in root.children() { + if field.is("field", DATA_FORMS_NS) { + let var = field.attr("var").ok_or(Error::ParseError("Field must have a 'var' attribute."))?; + let field_type = field.attr("type").unwrap_or("text-single"); + let label = field.attr("label").and_then(|label| label.parse().ok()); + let mut values = vec!(); + let mut media = vec!(); + for element in field.children() { + if element.is("value", DATA_FORMS_NS) { + values.push(element.text()); + } else if element.is("media", MEDIA_ELEMENT_NS) { + match parse_media_element(element) { + Ok(media_element) => media.push(media_element), + Err(_) => (), // TODO: is it really nice to swallow this error? + } + } else { + return Err(Error::ParseError("Field child isn’t a value or media element.")); + } + } + if var == "FORM_TYPE" && field_type == "hidden" { + if form_type != None { + return Err(Error::ParseError("More than one FORM_TYPE in a data form.")); + } + if values.len() != 1 { + return Err(Error::ParseError("Wrong number of values in FORM_TYPE.")); + } + form_type = Some(values[0].clone()); + } + fields.push(Field { + var: var.to_owned(), + type_: field_type.to_owned(), + label: label, + values: values, + media: media, + }); + } else { + return Err(Error::ParseError("Unknown field type in data form.")); + } + } + Ok(DataForm { type_: type_, form_type: form_type, fields: fields }) +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use data_forms; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let form = data_forms::parse_data_form(&elem).unwrap(); + assert_eq!(form.type_, data_forms::DataFormType::Result_); + assert!(form.form_type.is_none()); + assert!(form.fields.is_empty()); + } + + #[test] + fn test_invalid() { + let elem: Element = "".parse().unwrap(); + let error = data_forms::parse_data_form(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Type attribute on data form is mandatory."); + + let elem: Element = "".parse().unwrap(); + let error = data_forms::parse_data_form(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown data form type."); + } + + #[test] + fn test_wrong_child() { + let elem: Element = "".parse().unwrap(); + let error = data_forms::parse_data_form(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown field type in data form."); + } +} diff --git a/src/disco.rs b/src/disco.rs new file mode 100644 index 0000000000000000000000000000000000000000..7b2232ecd92c706b7bbdddc5abb564f8e9c6c387 --- /dev/null +++ b/src/disco.rs @@ -0,0 +1,193 @@ +extern crate minidom; + +use minidom::Element; + +use error::Error; +use ns::{DISCO_INFO_NS, DATA_FORMS_NS}; + +use data_forms::{DataForm, DataFormType, parse_data_form}; + +#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] +pub struct Feature { + pub var: String, +} + +#[derive(Debug)] +pub struct Identity { + pub category: String, // TODO: use an enum here. + pub type_: String, // TODO: use an enum here. + pub xml_lang: String, + pub name: Option, +} + +#[derive(Debug)] +pub struct Disco { + pub node: Option, + pub identities: Vec, + pub features: Vec, + pub extensions: Vec, +} + +pub fn parse_disco(root: &Element) -> Result { + assert!(root.is("query", DISCO_INFO_NS)); + let mut identities: Vec = vec!(); + let mut features: Vec = vec!(); + let mut extensions: Vec = vec!(); + + let node = root.attr("node") + .and_then(|node| node.parse().ok()); + + for child in root.children() { + if child.is("feature", DISCO_INFO_NS) { + let feature = child.attr("var") + .ok_or(Error::ParseError("Feature must have a 'var' attribute."))?; + features.push(Feature { + var: feature.to_owned(), + }); + } else if child.is("identity", DISCO_INFO_NS) { + let category = child.attr("category") + .ok_or(Error::ParseError("Identity must have a 'category' attribute."))?; + if category == "" { + return Err(Error::ParseError("Identity must have a non-empty 'category' attribute.")) + } + + let type_ = child.attr("type") + .ok_or(Error::ParseError("Identity must have a 'type' attribute."))?; + if type_ == "" { + return Err(Error::ParseError("Identity must have a non-empty 'type' attribute.")) + } + + // TODO: this must check for the namespace of the attribute, but minidom doesn’t support that yet, see issue #2. + let xml_lang = child.attr("lang").unwrap_or(""); + let name = child.attr("name") + .and_then(|node| node.parse().ok()); + identities.push(Identity { + category: category.to_owned(), + type_: type_.to_owned(), + xml_lang: xml_lang.to_owned(), + name: name, + }); + } else if child.is("x", DATA_FORMS_NS) { + let data_form = parse_data_form(child)?; + match data_form.type_ { + DataFormType::Result_ => (), + _ => return Err(Error::ParseError("Data form must have a 'result' type in disco#info.")), + } + match data_form.form_type { + Some(_) => extensions.push(data_form), + None => return Err(Error::ParseError("Data form found without a FORM_TYPE.")), + } + } else { + return Err(Error::ParseError("Unknown element in disco#info.")); + } + } + + if identities.is_empty() { + return Err(Error::ParseError("There must be at least one identity in disco#info.")); + } + if features.is_empty() { + return Err(Error::ParseError("There must be at least one feature in disco#info.")); + } + if !features.contains(&Feature { var: DISCO_INFO_NS.to_owned() }) { + return Err(Error::ParseError("disco#info feature not present in disco#info.")); + } + + return Ok(Disco { + node: node, + identities: identities, + features: features, + extensions: extensions + }); +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use disco; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let query = disco::parse_disco(&elem).unwrap(); + assert!(query.node.is_none()); + assert_eq!(query.identities.len(), 1); + assert_eq!(query.features.len(), 1); + assert!(query.extensions.is_empty()); + } + + #[test] + fn test_invalid() { + let elem: Element = "".parse().unwrap(); + let error = disco::parse_disco(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown element in disco#info."); + + let elem: Element = "".parse().unwrap(); + let error = disco::parse_disco(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "There must be at least one identity in disco#info."); + + let elem: Element = "".parse().unwrap(); + let error = disco::parse_disco(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Identity must have a 'category' attribute."); + + let elem: Element = "".parse().unwrap(); + let error = disco::parse_disco(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Identity must have a non-empty 'category' attribute."); + + let elem: Element = "".parse().unwrap(); + let error = disco::parse_disco(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Identity must have a 'type' attribute."); + + let elem: Element = "".parse().unwrap(); + let error = disco::parse_disco(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Identity must have a non-empty 'type' attribute."); + + let elem: Element = "".parse().unwrap(); + let error = disco::parse_disco(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Feature must have a 'var' attribute."); + + let elem: Element = "".parse().unwrap(); + let error = disco::parse_disco(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "There must be at least one feature in disco#info."); + + let elem: Element = "".parse().unwrap(); + let error = disco::parse_disco(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "disco#info feature not present in disco#info."); + } +} diff --git a/src/ecaps2.rs b/src/ecaps2.rs new file mode 100644 index 0000000000000000000000000000000000000000..50b94128ee78fed1849251d620e41b9c1b9f47b4 --- /dev/null +++ b/src/ecaps2.rs @@ -0,0 +1,330 @@ +extern crate minidom; + +use minidom::Element; + +use error::Error; + +use disco::{Feature, Identity, Disco, parse_disco}; +use data_forms::DataForm; + +fn compute_item(field: String) -> Vec { + let mut bytes = field.as_bytes().to_vec(); + bytes.push(0x1f); + bytes +} + +fn compute_items Vec>(things: Vec, separator: u8, encode: F) -> Vec { + let mut string: Vec = vec!(); + let mut accumulator: Vec> = vec!(); + for thing in things { + let bytes = encode(thing); + accumulator.push(bytes); + } + // This works using the expected i;octet collation. + accumulator.sort(); + for mut bytes in accumulator { + string.append(&mut bytes); + } + string.push(separator); + string +} + +fn compute_features(features: Vec) -> Vec { + compute_items(features, 0x1c, |feature| compute_item(feature.var)) +} + +fn compute_identities(identities: Vec) -> Vec { + compute_items(identities, 0x1c, |identity| { + let mut bytes = compute_item(identity.category); + bytes.append(&mut compute_item(identity.type_)); + bytes.append(&mut compute_item(identity.xml_lang)); + bytes.append(&mut compute_item(identity.name.unwrap_or(String::new()))); + bytes.push(0x1e); + bytes + }) +} + +fn compute_extensions(extensions: Vec) -> Vec { + compute_items(extensions, 0x1c, |extension| { + compute_items(extension.fields, 0x1d, |field| { + let mut bytes = compute_item(field.var); + bytes.append(&mut compute_items(field.values, 0x1e, + |value| compute_item(value))); + bytes + }) + }) +} + +fn compute_disco(disco: Disco) -> Vec { + let features_string = compute_features(disco.features); + let identities_string = compute_identities(disco.identities); + let extensions_string = compute_extensions(disco.extensions); + + let mut final_string = vec!(); + final_string.extend(features_string); + final_string.extend(identities_string); + final_string.extend(extensions_string); + final_string +} + +pub fn convert_element(root: &Element) -> Result, Error> { + let disco = parse_disco(root)?; + let final_string = compute_disco(disco); + Ok(final_string) +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use ecaps2; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let ecaps2 = ecaps2::convert_element(&elem).unwrap(); + assert_eq!(ecaps2.len(), 54); + } + + #[test] + fn test_xep_ex1() { + let elem: Element = r#" + + + + + + + + + + + + + + + + + + + + +"#.parse().unwrap(); + let expected = vec![104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, + 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, + 108, 47, 98, 121, 116, 101, 115, 116, 114, 101, 97, 109, 115, 31, + 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, + 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 99, 104, + 97, 116, 115, 116, 97, 116, 101, 115, 31, 104, 116, 116, 112, 58, + 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, + 111, 116, 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105, + 110, 102, 111, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, + 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, + 108, 47, 100, 105, 115, 99, 111, 35, 105, 116, 101, 109, 115, 31, + 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, + 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 105, 98, + 98, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, + 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, + 114, 111, 115, 116, 101, 114, 120, 31, 104, 116, 116, 112, 58, 47, + 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, + 111, 116, 111, 99, 111, 108, 47, 115, 105, 31, 104, 116, 116, 112, + 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, + 114, 111, 116, 111, 99, 111, 108, 47, 115, 105, 47, 112, 114, 111, + 102, 105, 108, 101, 47, 102, 105, 108, 101, 45, 116, 114, 97, 110, + 115, 102, 101, 114, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, + 58, 108, 97, 115, 116, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, + 58, 112, 114, 105, 118, 97, 99, 121, 31, 106, 97, 98, 98, 101, 114, + 58, 105, 113, 58, 114, 111, 115, 116, 101, 114, 31, 106, 97, 98, + 98, 101, 114, 58, 105, 113, 58, 116, 105, 109, 101, 31, 106, 97, + 98, 98, 101, 114, 58, 105, 113, 58, 118, 101, 114, 115, 105, 111, + 110, 31, 106, 97, 98, 98, 101, 114, 58, 120, 58, 111, 111, 98, 31, + 117, 114, 110, 58, 120, 109, 112, 112, 58, 112, 105, 110, 103, 31, + 117, 114, 110, 58, 120, 109, 112, 112, 58, 114, 101, 99, 101, 105, + 112, 116, 115, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 116, + 105, 109, 101, 31, 28, 99, 108, 105, 101, 110, 116, 31, 109, 111, + 98, 105, 108, 101, 31, 31, 66, 111, 109, 98, 117, 115, 77, 111, + 100, 31, 30, 28, 28]; + let ecaps2 = ecaps2::convert_element(&elem).unwrap(); + assert_eq!(ecaps2.len(), 0x1d9); + assert_eq!(ecaps2, expected); + + /* + let sha_256 = hash(ecaps2, "sha-256"); + assert_eq!(sha_256, "kzBZbkqJ3ADrj7v08reD1qcWUwNGHaidNUgD7nHpiw8="); + let sha3_256 = hash(ecaps2, "sha3-256"); + assert_eq!(sha3_256, "79mdYAfU9rEdTOcWDO7UEAt6E56SUzk/g6TnqUeuD9Q="); + */ + } + + #[test] + fn test_xep_ex2() { + let elem: Element = r#" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + urn:xmpp:dataforms:softwareinfo + + + Tkabber + + + 0.11.1-svn-20111216-mod (Tcl/Tk 8.6b2) + + + Windows + + + XP + + + +"#.parse().unwrap(); + let expected = vec![103, 97, 109, 101, 115, 58, 98, 111, 97, 114, 100, + 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, + 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 97, + 99, 116, 105, 118, 105, 116, 121, 31, 104, 116, 116, 112, 58, 47, + 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, + 111, 116, 111, 99, 111, 108, 47, 97, 99, 116, 105, 118, 105, 116, + 121, 43, 110, 111, 116, 105, 102, 121, 31, 104, 116, 116, 112, 58, + 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, + 111, 116, 111, 99, 111, 108, 47, 98, 121, 116, 101, 115, 116, 114, + 101, 97, 109, 115, 31, 104, 116, 116, 112, 58,47, 47, 106, 97, 98, + 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, + 111, 108, 47, 99, 104, 97, 116, 115, 116, 97, 116, 101, 115, 31, + 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, + 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 99, 111, + 109, 109, 97, 110, 100, 115, 31,104,116, 116, 112, 58, 47, 47, 106, + 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, + 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105, 110, 102, + 111, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, + 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, + 100, 105, 115, 99, 111, 35, 105, 116, 101, 109, 115, 31, 104, 116, + 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, + 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 101, 118, 105, 108, + 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, + 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 102, + 101, 97, 116, 117, 114, 101, 45, 110, 101, 103, 31, 104, 116, 116, + 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, + 112, 114, 111, 116, 111, 99, 111, 108, 47, 103, 101, 111, 108, 111, + 99, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, + 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99,111, 108, 47, + 103, 101, 111, 108, 111, 99, 43, 110, 111, 116, 105, 102, 121, 31, + 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, + 114, 103,47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 105, 98, + 98, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, + 46, 111, 114, 103, 47, 112, 114, 111,116, 111, 99, 111, 108, 47, + 105, 113, 105, 98, 98, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, + 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116,111, + 99, 111, 108, 47, 109, 111, 111, 100, 31, 104, 116, 116, 112, 58, + 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, + 111, 116, 111, 99, 111,108, 47, 109, 111, 111, 100, 43, 110, 111, + 116, 105, 102, 121, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, + 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, + 99, 111, 108, 47, 114, 111, 115, 116, 101, 114, 120, 31, 104, 116, + 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, + 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 115, 105, 31, 104, + 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, + 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 115, 105, 47, + 112, 114, 111, 102, 105, 108, 101, 47, 102, 105, 108, 101, 45, 116, + 114, 97, 110, 115, 102, 101, 114, 31, 104, 116, 116, 112, 58, 47, + 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, + 111, 116, 111, 99, 111, 108, 47, 116, 117, 110, 101, 31, 104, 116, + 116, 112, 58, 47, 47, 119, 119, 119, 46, 102, 97, 99, 101, 98, 111, + 111, 107, 46, 99, 111, 109, 47, 120, 109, 112, 112, 47, 109, 101, + 115, 115, 97, 103, 101, 115, 31, 104, 116, 116, 112, 58, 47, 47, + 119, 119, 119, 46, 120, 109, 112, 112, 46, 111, 114, 103, 47, 101, + 120, 116, 101, 110, 115, 105, 111, 110, 115, 47, 120, 101, 112, 45, + 48, 48, 56, 52, 46, 104, 116, 109, 108, 35, 110, 115, 45, 109, 101, + 116, 97, 100, 97, 116, 97, 43, 110, 111, 116, 105, 102, 121, 31, + 106, 97, 98, 98, 101, 114,58, 105,113, 58, 97, 118, 97, 116, 97, + 114, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 98, 114, 111, + 119, 115, 101, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, + 100, 116, 99, 112, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, + 102, 105, 108, 101, 120, 102, 101, 114, 31, 106, 97, 98, 98, 101, + 114, 58, 105, 113, 58, 105, 98, 98, 31, 106, 97, 98, 98, 101, 114, + 58, 105, 113, 58, 105, 110, 98, 97, 110, 100, 31, 106, 97, 98, 98, + 101, 114, 58, 105, 113, 58, 106, 105, 100, 108, 105, 110, 107, 31, + 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 108, 97, 115, 116, 31, + 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 111, 111, 98, 31, 106, + 97,98, 98, 101, 114, 58, 105, 113, 58, 112, 114, 105, 118, 97, 99, + 121, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 114, 111, + 115, 116, 101, 114,31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, + 116, 105, 109, 101, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, + 58, 118, 101, 114, 115, 105, 111, 110, 31, 106, 97, 98, 98, 101, + 114, 58, 120, 58, 100, 97, 116, 97, 31, 106, 97, 98, 98, 101, 114, + 58, 120, 58, 101, 118, 101, 110, 116, 31, 106, 97, 98, 98, 101, + 114, 58, 120, 58, 111, 111, 98, 31, 117, 114, 110, 58, 120, 109, + 112, 112, 58, 97, 118, 97, 116, 97, 114, 58, 109, 101, 116, 97, + 100, 97, 116, 97, 43, 110, 111, 116, 105, 102, 121,31, 117, 114, + 110, 58, 120, 109, 112, 112, 58, 112, 105, 110, 103, 31, 117, 114, + 110, 58, 120, 109, 112, 112, 58, 114, 101, 99, 101, 105, 112, 116, + 115, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 116, 105, 109, + 101, 31, 28, 99, 108, 105, 101, 110, 116, 31, 112, 99, 31, 101, + 110, 31, 84, 107, 97, 98, 98, 101, 114,31, 30, 99, 108, 105, 101, + 110, 116, 31, 112, 99, 31, 114, 117, 31, 208, 162, 208, 186, 208, + 176, 208, 177, 208, 177, 208, 181, 209, 128, 31, 30, 28, 70, 79, + 82, 77, 95, 84, 89, 80, 69, 31, 117, 114, 110, 58, 120, 109, 112, + 112, 58, 100, 97, 116, 97, 102, 111, 114, 109, 115, 58, 115, 111, + 102, 116, 119, 97, 114, 101,105, 110, 102, 111, 31, 30, 111, 115, + 31, 87, 105, 110, 100, 111, 119, 115, 31, 30, 111, 115, 95, 118, + 101, 114, 115, 105, 111, 110, 31, 88, 80, 31, 30, 115, 111, 102, + 116, 119, 97, 114, 101, 31, 84, 107, 97, 98, 98, 101, 114, 31, 30, + 115, 111, 102, 116, 119, 97, 114, 101, 95, 118, 101, 114, 115, 105, + 111, 110, 31, 48, 46, 49, 49, 46, 49, 45, 115, 118, 110, 45, 50, + 48, 49, 49, 49, 50, 49, 54, 45, 109, 111, 100, 32, 40, 84, 99, 108, + 47, 84, 107, 32, 56, 46,54, 98, 50, 41, 31, 30, 29, 28]; + let ecaps2 = ecaps2::convert_element(&elem).unwrap(); + assert_eq!(ecaps2.len(), 0x543); + assert_eq!(ecaps2, expected); + + /* + let sha_256 = hash(ecaps2, "sha-256"); + assert_eq!(sha_256, "u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY="); + let sha3_256 = hash(ecaps2, "sha3-256"); + assert_eq!(sha3_256, "XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg="); + */ + } +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000000000000000000000000000000000000..bd7d2d9cb78de8139f056e49809df24309f4e63c --- /dev/null +++ b/src/error.rs @@ -0,0 +1,23 @@ +use std::convert::From; +use std::io; + +use minidom; + +#[derive(Debug)] +pub enum Error { + IoError(io::Error), + XMLError(minidom::Error), + ParseError(&'static str), +} + +impl From for Error { + fn from(err: io::Error) -> Error { + Error::IoError(err) + } +} + +impl From for Error { + fn from(err: minidom::Error) -> Error { + Error::XMLError(err) + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..a6aa19012c75972a40a775a9399d47e266df8377 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,9 @@ +extern crate minidom; + +pub mod error; +pub mod ns; + +pub mod disco; +pub mod data_forms; +pub mod media_element; +pub mod ecaps2; diff --git a/src/media_element.rs b/src/media_element.rs new file mode 100644 index 0000000000000000000000000000000000000000..1fc84507deae634f804ae1c3880b743282a06159 --- /dev/null +++ b/src/media_element.rs @@ -0,0 +1,177 @@ +use minidom::Element; + +use error::Error; + +const MEDIA_ELEMENT_NS: &'static str = "urn:xmpp:media-element"; + +#[derive(Debug)] +pub struct URI { + pub type_: String, + pub uri: String, +} + +#[derive(Debug)] +pub struct MediaElement { + pub width: Option, + pub height: Option, + pub uris: Vec, +} + +pub fn parse_media_element(root: &Element) -> Result { + assert!(root.is("media", MEDIA_ELEMENT_NS)); + let width = root.attr("width").and_then(|width| width.parse().ok()); + let height = root.attr("height").and_then(|height| height.parse().ok()); + let mut uris = vec!(); + for uri in root.children() { + if uri.is("uri", MEDIA_ELEMENT_NS) { + let type_ = uri.attr("type").ok_or(Error::ParseError("Attribute type on uri is mandatory."))?; + let text = uri.text().trim().to_owned(); + if text == "" { + return Err(Error::ParseError("URI missing in uri.")); + } + uris.push(URI { type_: type_.to_owned(), uri: text }); + } else { + return Err(Error::ParseError("Unknown child in media element.")); + } + } + Ok(MediaElement { width: width, height: height, uris: uris }) +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use media_element; + use data_forms; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let media = media_element::parse_media_element(&elem).unwrap(); + assert!(media.width.is_none()); + assert!(media.height.is_none()); + assert!(media.uris.is_empty()); + } + + #[test] + fn test_width_height() { + let elem: Element = "".parse().unwrap(); + let media = media_element::parse_media_element(&elem).unwrap(); + assert_eq!(media.width.unwrap(), 32); + assert_eq!(media.height.unwrap(), 32); + } + + #[test] + fn test_uri() { + let elem: Element = "https://example.org/".parse().unwrap(); + let media = media_element::parse_media_element(&elem).unwrap(); + assert_eq!(media.uris.len(), 1); + assert_eq!(media.uris[0].type_, "text/html"); + assert_eq!(media.uris[0].uri, "https://example.org/"); + } + + #[test] + fn test_invalid_width_height() { + let elem: Element = "".parse().unwrap(); + let media = media_element::parse_media_element(&elem).unwrap(); + assert!(media.width.is_none()); + + let elem: Element = "".parse().unwrap(); + let media = media_element::parse_media_element(&elem).unwrap(); + assert!(media.width.is_none()); + + let elem: Element = "".parse().unwrap(); + let media = media_element::parse_media_element(&elem).unwrap(); + assert!(media.height.is_none()); + + let elem: Element = "".parse().unwrap(); + let media = media_element::parse_media_element(&elem).unwrap(); + assert!(media.height.is_none()); + } + + #[test] + fn test_unknown_child() { + let elem: Element = "".parse().unwrap(); + let error = media_element::parse_media_element(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in media element."); + } + + #[test] + fn test_bad_uri() { + let elem: Element = "https://example.org/".parse().unwrap(); + let error = media_element::parse_media_element(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Attribute type on uri is mandatory."); + + let elem: Element = "".parse().unwrap(); + let error = media_element::parse_media_element(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "URI missing in uri."); + } + + #[test] + fn test_xep_ex1() { + let elem: Element = r#" + + + http://victim.example.com/challenges/speech.wav?F3A6292C + + + cid:sha1+a15a505e360702b79c75a5f67773072ed392f52a@bob.xmpp.org + + + http://victim.example.com/challenges/speech.mp3?F3A6292C + +"#.parse().unwrap(); + let media = media_element::parse_media_element(&elem).unwrap(); + assert!(media.width.is_none()); + assert!(media.height.is_none()); + assert_eq!(media.uris.len(), 3); + assert_eq!(media.uris[0].type_, "audio/x-wav"); + assert_eq!(media.uris[0].uri, "http://victim.example.com/challenges/speech.wav?F3A6292C"); + assert_eq!(media.uris[1].type_, "audio/ogg; codecs=speex"); + assert_eq!(media.uris[1].uri, "cid:sha1+a15a505e360702b79c75a5f67773072ed392f52a@bob.xmpp.org"); + assert_eq!(media.uris[2].type_, "audio/mpeg"); + assert_eq!(media.uris[2].uri, "http://victim.example.com/challenges/speech.mp3?F3A6292C"); + } + + #[test] + fn test_xep_ex2() { + let elem: Element = r#" + + [ ... ] + + + + http://www.victim.com/challenges/ocr.jpeg?F3A6292C + + + cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org + + + + [ ... ] +"#.parse().unwrap(); + let form = data_forms::parse_data_form(&elem).unwrap(); + assert_eq!(form.fields.len(), 1); + assert_eq!(form.fields[0].var, "ocr"); + assert_eq!(form.fields[0].media[0].width, Some(290)); + assert_eq!(form.fields[0].media[0].height, Some(80)); + assert_eq!(form.fields[0].media[0].uris[0].type_, "image/jpeg"); + assert_eq!(form.fields[0].media[0].uris[0].uri, "http://www.victim.com/challenges/ocr.jpeg?F3A6292C"); + assert_eq!(form.fields[0].media[0].uris[1].type_, "image/jpeg"); + assert_eq!(form.fields[0].media[0].uris[1].uri, "cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org"); + } +} diff --git a/src/ns.rs b/src/ns.rs new file mode 100644 index 0000000000000000000000000000000000000000..36bd127c0837df33ee0a1c8067d2914893567a86 --- /dev/null +++ b/src/ns.rs @@ -0,0 +1,3 @@ +pub const DISCO_INFO_NS: &'static str = "http://jabber.org/protocol/disco#info"; +pub const DATA_FORMS_NS: &'static str = "jabber:x:data"; +pub const MEDIA_ELEMENT_NS: &'static str = "urn:xmpp:media-element"; From 82fa22cd0637a9867e25ab10ddf5fc206a16f182 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 19 Apr 2017 02:27:33 +0100 Subject: [PATCH 002/698] disco: Rename misnamed variable. --- src/disco.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/disco.rs b/src/disco.rs index 7b2232ecd92c706b7bbdddc5abb564f8e9c6c387..bc65e3a1115945cbeb54bbf0b33aa63e89372239 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -60,7 +60,7 @@ pub fn parse_disco(root: &Element) -> Result { // TODO: this must check for the namespace of the attribute, but minidom doesn’t support that yet, see issue #2. let xml_lang = child.attr("lang").unwrap_or(""); let name = child.attr("name") - .and_then(|node| node.parse().ok()); + .and_then(|name| name.parse().ok()); identities.push(Identity { category: category.to_owned(), type_: type_.to_owned(), From 9da488f9095a29e514ad1c6eea469beb57c85b90 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 19 Apr 2017 02:27:42 +0100 Subject: [PATCH 003/698] Add a Jingle parser. --- src/jingle.rs | 491 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/ns.rs | 1 + 3 files changed, 493 insertions(+) create mode 100644 src/jingle.rs diff --git a/src/jingle.rs b/src/jingle.rs new file mode 100644 index 0000000000000000000000000000000000000000..e732c6b430b57a8331cae6c6863784da477f3ff6 --- /dev/null +++ b/src/jingle.rs @@ -0,0 +1,491 @@ +extern crate minidom; + +use std::str::FromStr; + +use minidom::Element; + +use error::Error; +use ns::{JINGLE_NS}; + +#[derive(Debug, PartialEq)] +pub enum Action { + ContentAccept, + ContentAdd, + ContentModify, + ContentReject, + ContentRemove, + DescriptionInfo, + SecurityInfo, + SessionAccept, + SessionInfo, + SessionInitiate, + SessionTerminate, + TransportAccept, + TransportInfo, + TransportReject, + TransportReplace, +} + +impl FromStr for Action { + type Err = Error; + + fn from_str(s: &str) -> Result { + if s == "content-accept" { + Ok(Action::ContentAccept) + } else if s == "content-add" { + Ok(Action::ContentAdd) + } else if s == "content-modify" { + Ok(Action::ContentModify) + } else if s == "content-reject" { + Ok(Action::ContentReject) + } else if s == "content-remove" { + Ok(Action::ContentRemove) + } else if s == "description-info" { + Ok(Action::DescriptionInfo) + } else if s == "security-info" { + Ok(Action::SecurityInfo) + } else if s == "session-accept" { + Ok(Action::SessionAccept) + } else if s == "session-info" { + Ok(Action::SessionInfo) + } else if s == "session-initiate" { + Ok(Action::SessionInitiate) + } else if s == "session-terminate" { + Ok(Action::SessionTerminate) + } else if s == "transport-accept" { + Ok(Action::TransportAccept) + } else if s == "transport-info" { + Ok(Action::TransportInfo) + } else if s == "transport-reject" { + Ok(Action::TransportReject) + } else if s == "transport-replace" { + Ok(Action::TransportReplace) + } else { + Err(Error::ParseError("Unknown action.")) + } + } +} + +// TODO: use a real JID type. +type Jid = String; + +#[derive(Debug, PartialEq)] +pub enum Creator { + Initiator, + Responder, +} + +impl FromStr for Creator { + type Err = Error; + + fn from_str(s: &str) -> Result { + if s == "initiator" { + Ok(Creator::Initiator) + } else if s == "responder" { + Ok(Creator::Responder) + } else { + Err(Error::ParseError("Unknown creator.")) + } + } +} + +#[derive(Debug, PartialEq)] +pub enum Senders { + Both, + Initiator, + None_, + Responder, +} + +impl FromStr for Senders { + type Err = Error; + + fn from_str(s: &str) -> Result { + if s == "both" { + Ok(Senders::Both) + } else if s == "initiator" { + Ok(Senders::Initiator) + } else if s == "none" { + Ok(Senders::None_) + } else if s == "responder" { + Ok(Senders::Responder) + } else { + Err(Error::ParseError("Unknown senders.")) + } + } +} + +#[derive(Debug)] +pub struct Content { + pub creator: Creator, + pub disposition: String, + pub name: String, + pub senders: Senders, + pub description: String, + pub transport: String, + pub security: Option, +} + +#[derive(Debug, PartialEq)] +pub enum Reason { + AlternativeSession, //(String), + Busy, + Cancel, + ConnectivityError, + Decline, + Expired, + FailedApplication, + FailedTransport, + GeneralError, + Gone, + IncompatibleParameters, + MediaError, + SecurityError, + Success, + Timeout, + UnsupportedApplications, + UnsupportedTransports, +} + +impl FromStr for Reason { + type Err = Error; + + fn from_str(s: &str) -> Result { + if s == "alternative-session" { + Ok(Reason::AlternativeSession) + } else if s == "busy" { + Ok(Reason::Busy) + } else if s == "cancel" { + Ok(Reason::Cancel) + } else if s == "connectivity-error" { + Ok(Reason::ConnectivityError) + } else if s == "decline" { + Ok(Reason::Decline) + } else if s == "expired" { + Ok(Reason::Expired) + } else if s == "failed-application" { + Ok(Reason::FailedApplication) + } else if s == "failed-transport" { + Ok(Reason::FailedTransport) + } else if s == "general-error" { + Ok(Reason::GeneralError) + } else if s == "gone" { + Ok(Reason::Gone) + } else if s == "incompatible-parameters" { + Ok(Reason::IncompatibleParameters) + } else if s == "media-error" { + Ok(Reason::MediaError) + } else if s == "security-error" { + Ok(Reason::SecurityError) + } else if s == "success" { + Ok(Reason::Success) + } else if s == "timeout" { + Ok(Reason::Timeout) + } else if s == "unsupported-applications" { + Ok(Reason::UnsupportedApplications) + } else if s == "unsupported-transports" { + Ok(Reason::UnsupportedTransports) + } else { + Err(Error::ParseError("Unknown reason.")) + } + } +} + +#[derive(Debug)] +pub struct ReasonElement { + pub reason: Reason, + pub text: Option, +} + +#[derive(Debug)] +pub struct Jingle { + pub action: Action, + pub initiator: Option, + pub responder: Option, + pub sid: String, + pub contents: Vec, + pub reason: Option, + //pub other: Vec, +} + +pub fn parse_jingle(root: &Element) -> Result { + assert!(root.is("jingle", JINGLE_NS)); + let mut contents: Vec = vec!(); + + let action = root.attr("action") + .ok_or(Error::ParseError("Jingle must have an 'action' attribute."))? + .parse()?; + let initiator = root.attr("initiator") + .and_then(|initiator| initiator.parse().ok()); + let responder = root.attr("responder") + .and_then(|responder| responder.parse().ok()); + let sid = root.attr("sid") + .ok_or(Error::ParseError("Jingle must have a 'sid' attribute."))?; + let mut reason_element = None; + + for child in root.children() { + if child.is("content", JINGLE_NS) { + let creator = child.attr("creator") + .ok_or(Error::ParseError("Content must have a 'creator' attribute."))? + .parse()?; + let disposition = child.attr("disposition") + .unwrap_or("session"); + let name = child.attr("name") + .ok_or(Error::ParseError("Content must have a 'name' attribute."))?; + let senders = child.attr("senders") + .unwrap_or("both") + .parse()?; + let mut description = None; + let mut transport = None; + let mut security = None; + for stuff in child.children() { + if stuff.name() == "description" { + if description.is_some() { + return Err(Error::ParseError("Content must not have more than one description.")); + } + description = Some(stuff.ns().ok_or(Error::ParseError("Description without a namespace."))?); + } else if stuff.name() == "transport" { + if transport.is_some() { + return Err(Error::ParseError("Content must not have more than one transport.")); + } + transport = Some(stuff.ns().ok_or(Error::ParseError("Transport without a namespace."))?); + } else if stuff.name() == "security" { + if security.is_some() { + return Err(Error::ParseError("Content must not have more than one security.")); + } + security = stuff.ns().and_then(|ns| ns.parse().ok()); + } + } + if description.is_none() { + return Err(Error::ParseError("Content must have one description.")); + } + if transport.is_none() { + return Err(Error::ParseError("Content must have one transport.")); + } + let description = description.unwrap().to_owned(); + let transport = transport.unwrap().to_owned(); + contents.push(Content { + creator: creator, + disposition: disposition.to_owned(), + name: name.to_owned(), + senders: senders, + description: description, + transport: transport, + security: security.to_owned(), + }); + } else if child.is("reason", JINGLE_NS) { + if reason_element.is_some() { + return Err(Error::ParseError("Jingle must not have more than one reason.")); + } + let mut reason = None; + let mut text = None; + for stuff in child.children() { + if stuff.ns() != Some(JINGLE_NS) { + return Err(Error::ParseError("Reason contains a foreign element.")); + } + let name = stuff.name(); + if name == "text" { + text = Some(stuff.text()); + } else { + reason = Some(name.parse()?); + } + } + if reason.is_none() { + return Err(Error::ParseError("Reason doesn’t contain a valid reason.")); + } + reason_element = Some(ReasonElement { + reason: reason.unwrap(), + text: text, + }); + } else { + return Err(Error::ParseError("Unknown element in jingle.")); + } + } + + return Ok(Jingle { + action: action, + initiator: initiator, + responder: responder, + sid: sid.to_owned(), + contents: contents, + reason: reason_element, + }); +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use jingle; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let jingle = jingle::parse_jingle(&elem).unwrap(); + assert_eq!(jingle.action, jingle::Action::SessionInitiate); + assert_eq!(jingle.sid, "coucou"); + } + + #[test] + fn test_invalid_jingle() { + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Jingle must have an 'action' attribute."); + + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Jingle must have a 'sid' attribute."); + + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown action."); + + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown element in jingle."); + } + + #[test] + fn test_content() { + let elem: Element = "".parse().unwrap(); + let jingle = jingle::parse_jingle(&elem).unwrap(); + assert_eq!(jingle.contents[0].creator, jingle::Creator::Initiator); + assert_eq!(jingle.contents[0].name, "coucou"); + assert_eq!(jingle.contents[0].senders, jingle::Senders::Both); + assert_eq!(jingle.contents[0].disposition, "session"); + println!("{:#?}", jingle); + + let elem: Element = "".parse().unwrap(); + let jingle = jingle::parse_jingle(&elem).unwrap(); + assert_eq!(jingle.contents[0].senders, jingle::Senders::Both); + + let elem: Element = "".parse().unwrap(); + let jingle = jingle::parse_jingle(&elem).unwrap(); + assert_eq!(jingle.contents[0].disposition, "early-session"); + } + + #[test] + fn test_invalid_content() { + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Content must have a 'creator' attribute."); + + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Content must have a 'name' attribute."); + + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown creator."); + + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown senders."); + + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown senders."); + + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Content must have one description."); + + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Content must have one transport."); + } + + #[test] + fn test_reason() { + let elem: Element = "".parse().unwrap(); + let jingle = jingle::parse_jingle(&elem).unwrap(); + let reason = jingle.reason.unwrap(); + assert_eq!(reason.reason, jingle::Reason::Success); + assert_eq!(reason.text, None); + + let elem: Element = "coucou".parse().unwrap(); + let jingle = jingle::parse_jingle(&elem).unwrap(); + let reason = jingle.reason.unwrap(); + assert_eq!(reason.reason, jingle::Reason::Success); + assert_eq!(reason.text, Some(String::from("coucou"))); + } + + #[test] + fn test_invalid_reason() { + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Reason doesn’t contain a valid reason."); + + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown reason."); + + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Reason contains a foreign element."); + + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Jingle must not have more than one reason."); + } +} diff --git a/src/lib.rs b/src/lib.rs index a6aa19012c75972a40a775a9399d47e266df8377..56d17f4726097d4084e40af7bce81213834deab3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,3 +7,4 @@ pub mod disco; pub mod data_forms; pub mod media_element; pub mod ecaps2; +pub mod jingle; diff --git a/src/ns.rs b/src/ns.rs index 36bd127c0837df33ee0a1c8067d2914893567a86..55da2eb8b6011c9dc06f1162585140bdd9b18405 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -1,3 +1,4 @@ pub const DISCO_INFO_NS: &'static str = "http://jabber.org/protocol/disco#info"; pub const DATA_FORMS_NS: &'static str = "jabber:x:data"; pub const MEDIA_ELEMENT_NS: &'static str = "urn:xmpp:media-element"; +pub const JINGLE_NS: &'static str = "urn:xmpp:jingle:1"; From 9d129701d09983898d059575312b8fc0b4e81a11 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 19 Apr 2017 02:38:10 +0100 Subject: [PATCH 004/698] jingle: Add an error when there is more than one in a . --- src/jingle.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/jingle.rs b/src/jingle.rs index e732c6b430b57a8331cae6c6863784da477f3ff6..37912a020c6a02edd9e54d14e85a5f4fdb722189 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -5,7 +5,7 @@ use std::str::FromStr; use minidom::Element; use error::Error; -use ns::{JINGLE_NS}; +use ns::JINGLE_NS; #[derive(Debug, PartialEq)] pub enum Action { @@ -285,6 +285,9 @@ pub fn parse_jingle(root: &Element) -> Result { } let name = stuff.name(); if name == "text" { + if text.is_some() { + return Err(Error::ParseError("Reason must not have more than one text.")); + } text = Some(stuff.text()); } else { reason = Some(name.parse()?); @@ -487,5 +490,13 @@ mod tests { _ => panic!(), }; assert_eq!(message, "Jingle must not have more than one reason."); + + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Reason must not have more than one text."); } } From 87cd047a46d61d4cb0bf633dda6d41888806aa96 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 19 Apr 2017 18:58:43 +0100 Subject: [PATCH 005/698] media_element: Import the namespace from ns. --- src/media_element.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/media_element.rs b/src/media_element.rs index 1fc84507deae634f804ae1c3880b743282a06159..86f286eebce54140104076fa18200e8e757521c4 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -2,7 +2,7 @@ use minidom::Element; use error::Error; -const MEDIA_ELEMENT_NS: &'static str = "urn:xmpp:media-element"; +use ns::MEDIA_ELEMENT_NS; #[derive(Debug)] pub struct URI { From fdc76eca3cc5ecc9079754dc933b3d15b293d9cc Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 19 Apr 2017 18:59:07 +0100 Subject: [PATCH 006/698] Add a ping parser. --- src/lib.rs | 1 + src/ns.rs | 1 + src/ping.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 src/ping.rs diff --git a/src/lib.rs b/src/lib.rs index 56d17f4726097d4084e40af7bce81213834deab3..d8a3f8cc56528b71a57ec9c7265a1eabf05b6b73 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,3 +8,4 @@ pub mod data_forms; pub mod media_element; pub mod ecaps2; pub mod jingle; +pub mod ping; diff --git a/src/ns.rs b/src/ns.rs index 55da2eb8b6011c9dc06f1162585140bdd9b18405..7c0903aa2b3bcc06ce7b5b1d07775b301530f3d2 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -2,3 +2,4 @@ pub const DISCO_INFO_NS: &'static str = "http://jabber.org/protocol/disco#info"; pub const DATA_FORMS_NS: &'static str = "jabber:x:data"; pub const MEDIA_ELEMENT_NS: &'static str = "urn:xmpp:media-element"; pub const JINGLE_NS: &'static str = "urn:xmpp:jingle:1"; +pub const PING_NS: &'static str = "urn:xmpp:ping"; diff --git a/src/ping.rs b/src/ping.rs new file mode 100644 index 0000000000000000000000000000000000000000..7176fa42baffa14fdad058c7c94312f2db7f189b --- /dev/null +++ b/src/ping.rs @@ -0,0 +1,53 @@ +use minidom::Element; + +use error::Error; + +use ns::PING_NS; + +#[derive(Debug)] +pub struct Ping { +} + +pub fn parse_ping(root: &Element) -> Result { + assert!(root.is("ping", PING_NS)); + for _ in root.children() { + return Err(Error::ParseError("Unknown child in ping element.")); + } + Ok(Ping { }) +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use ping; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + ping::parse_ping(&elem).unwrap(); + } + + #[test] + fn test_invalid() { + let elem: Element = "".parse().unwrap(); + let error = ping::parse_ping(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in ping element."); + } + + #[test] + #[ignore] + fn test_invalid_attribute() { + let elem: Element = "".parse().unwrap(); + let error = ping::parse_ping(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in ping element."); + } +} From 69f1405b9c6c396f1c136c41f091ac99d7cdb0ae Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 19 Apr 2017 19:15:57 +0100 Subject: [PATCH 007/698] Add a chatstate parser. --- src/chatstates.rs | 80 +++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/ns.rs | 1 + 3 files changed, 82 insertions(+) create mode 100644 src/chatstates.rs diff --git a/src/chatstates.rs b/src/chatstates.rs new file mode 100644 index 0000000000000000000000000000000000000000..6ec2e22df947ad3d050967c931f555a76c9c43bf --- /dev/null +++ b/src/chatstates.rs @@ -0,0 +1,80 @@ +use minidom::Element; + +use error::Error; + +use ns::CHATSTATES_NS; + +#[derive(Debug)] +pub enum ChatState { + Active, + Composing, + Gone, + Inactive, + Paused, +} + +pub fn parse_chatstate(root: &Element) -> Result { + for _ in root.children() { + return Err(Error::ParseError("Unknown child in chatstate element.")); + } + if root.is("active", CHATSTATES_NS) { + Ok(ChatState::Active) + } else if root.is("composing", CHATSTATES_NS) { + Ok(ChatState::Composing) + } else if root.is("gone", CHATSTATES_NS) { + Ok(ChatState::Gone) + } else if root.is("inactive", CHATSTATES_NS) { + Ok(ChatState::Inactive) + } else if root.is("paused", CHATSTATES_NS) { + Ok(ChatState::Paused) + } else { + Err(Error::ParseError("Unknown chatstate element.")) + } +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use chatstates; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + chatstates::parse_chatstate(&elem).unwrap(); + } + + #[test] + fn test_invalid() { + let elem: Element = "".parse().unwrap(); + let error = chatstates::parse_chatstate(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown chatstate element."); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = chatstates::parse_chatstate(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in chatstate element."); + } + + #[test] + #[ignore] + fn test_invalid_attribute() { + let elem: Element = "".parse().unwrap(); + let error = chatstates::parse_chatstate(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in chatstate element."); + } +} diff --git a/src/lib.rs b/src/lib.rs index d8a3f8cc56528b71a57ec9c7265a1eabf05b6b73..62996e9c0285fce8a2831c0c4cbab5bfbfd1aefb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,3 +9,4 @@ pub mod media_element; pub mod ecaps2; pub mod jingle; pub mod ping; +pub mod chatstates; diff --git a/src/ns.rs b/src/ns.rs index 7c0903aa2b3bcc06ce7b5b1d07775b301530f3d2..84ca86e90fcdc432a778f695042b3e12ef9dfb4e 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -3,3 +3,4 @@ pub const DATA_FORMS_NS: &'static str = "jabber:x:data"; pub const MEDIA_ELEMENT_NS: &'static str = "urn:xmpp:media-element"; pub const JINGLE_NS: &'static str = "urn:xmpp:jingle:1"; pub const PING_NS: &'static str = "urn:xmpp:ping"; +pub const CHATSTATES_NS: &'static str = "http://jabber.org/protocol/chatstates"; From 31a7d8be23741adeb916e1cf41d8112703167612 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 19 Apr 2017 21:50:14 +0100 Subject: [PATCH 008/698] disco: Drop some unneeded derived traits from Feature. --- src/disco.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/disco.rs b/src/disco.rs index bc65e3a1115945cbeb54bbf0b33aa63e89372239..e436b2620e899bada9cb0b50a9123ba3e7da858c 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -7,7 +7,7 @@ use ns::{DISCO_INFO_NS, DATA_FORMS_NS}; use data_forms::{DataForm, DataFormType, parse_data_form}; -#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] +#[derive(Debug, PartialEq)] pub struct Feature { pub var: String, } From 4f31727a1ad47bc461c009caf81b47c04d7b56c6 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 19 Apr 2017 21:52:14 +0100 Subject: [PATCH 009/698] Add an In-Band Bytestream parser. --- src/ibb.rs | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/ns.rs | 1 + 3 files changed, 124 insertions(+) create mode 100644 src/ibb.rs diff --git a/src/ibb.rs b/src/ibb.rs new file mode 100644 index 0000000000000000000000000000000000000000..1868772ee83e424b326780f5dd14fbb7dc44f9aa --- /dev/null +++ b/src/ibb.rs @@ -0,0 +1,122 @@ +use std::str::FromStr; + +use minidom::Element; + +use error::Error; + +use ns::IBB_NS; + +#[derive(Debug)] +pub enum Stanza { + Iq, + Message, +} + +impl Default for Stanza { + fn default() -> Stanza { + Stanza::Iq + } +} + +impl FromStr for Stanza { + type Err = Error; + + fn from_str(s: &str) -> Result { + if s == "iq" { + Ok(Stanza::Iq) + } else if s == "message" { + Ok(Stanza::Message) + } else { + Err(Error::ParseError("Unknown 'stanza' attribute.")) + } + } +} + +#[derive(Debug)] +pub enum IBB { + Open { block_size: u16, sid: String, stanza: Stanza }, + Data(u16, String, Vec), + Close(String), +} + +fn optional_attr(root: &Element, attr: &str) -> Option { + root.attr(attr) + .and_then(|value| value.parse().ok()) +} + +fn required_attr(root: &Element, attr: &str, err: Error) -> Result { + optional_attr(root, attr).ok_or(err) +} + +pub fn parse_ibb(root: &Element) -> Result { + if root.is("open", IBB_NS) { + let block_size = required_attr(root, "block-size", Error::ParseError("Required attribute 'block-size' missing in open element."))?; + let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in open element."))?; + let stanza = root.attr("stanza") + .and_then(|value| value.parse().ok()) + .unwrap_or(Default::default()); + for _ in root.children() { + return Err(Error::ParseError("Unknown child in open element.")); + } + Ok(IBB::Open { + block_size: block_size, + sid: sid, + stanza: stanza + }) + } else { + Err(Error::ParseError("Unknown ibb element.")) + } +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use ibb; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + ibb::parse_ibb(&elem).unwrap(); + } + + #[test] + fn test_invalid() { + let elem: Element = "".parse().unwrap(); + let error = ibb::parse_ibb(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'block-size' missing in open element."); + + // TODO: maybe make a better error message here. + let elem: Element = "".parse().unwrap(); + let error = ibb::parse_ibb(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'block-size' missing in open element."); + + let elem: Element = "".parse().unwrap(); + let error = ibb::parse_ibb(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'sid' missing in open element."); + } + + #[test] + #[ignore] + fn test_invalid_stanza() { + let elem: Element = "".parse().unwrap(); + let error = ibb::parse_ibb(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Wrong value for 'stanza' attribute in open."); + } +} diff --git a/src/lib.rs b/src/lib.rs index 62996e9c0285fce8a2831c0c4cbab5bfbfd1aefb..f7c964b95a803c16468cd88f87ae9c258a9945be 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,3 +10,4 @@ pub mod ecaps2; pub mod jingle; pub mod ping; pub mod chatstates; +pub mod ibb; diff --git a/src/ns.rs b/src/ns.rs index 84ca86e90fcdc432a778f695042b3e12ef9dfb4e..24b81133ab6b16346aad8104e74f0b920b2d06b8 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -4,3 +4,4 @@ pub const MEDIA_ELEMENT_NS: &'static str = "urn:xmpp:media-element"; pub const JINGLE_NS: &'static str = "urn:xmpp:jingle:1"; pub const PING_NS: &'static str = "urn:xmpp:ping"; pub const CHATSTATES_NS: &'static str = "http://jabber.org/protocol/chatstates"; +pub const IBB_NS: &'static str = "http://jabber.org/protocol/ibb"; From 948ecd7dd72147f1ae455a3d800cabc5cc0224dc Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 19 Apr 2017 23:21:23 +0100 Subject: [PATCH 010/698] Add a body plugin. --- src/body.rs | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/ns.rs | 1 + 3 files changed, 73 insertions(+) create mode 100644 src/body.rs diff --git a/src/body.rs b/src/body.rs new file mode 100644 index 0000000000000000000000000000000000000000..0bf2f498dcae3490d8bc1723198c97a64dbbe11e --- /dev/null +++ b/src/body.rs @@ -0,0 +1,71 @@ +use minidom::Element; + +use error::Error; +use super::MessagePayload; + +// TODO: also support components and servers. +use ns::JABBER_CLIENT_NS; + +#[derive(Debug)] +pub struct Body { + body: String, +} + +impl MessagePayload for Body {} + +pub fn parse_body(root: &Element) -> Result { + if !root.is("body", JABBER_CLIENT_NS) { + return Err(Error::ParseError("Not a body element.")); + } + for _ in root.children() { + return Err(Error::ParseError("Unknown child in body element.")); + } + Ok(Body { body: root.text() }) +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use chatstates; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + chatstates::parse_chatstate(&elem).unwrap(); + } + + #[test] + fn test_invalid() { + let elem: Element = "".parse().unwrap(); + let error = chatstates::parse_chatstate(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown chatstate element."); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = chatstates::parse_chatstate(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in chatstate element."); + } + + #[test] + #[ignore] + fn test_invalid_attribute() { + let elem: Element = "".parse().unwrap(); + let error = chatstates::parse_chatstate(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in chatstate element."); + } +} diff --git a/src/lib.rs b/src/lib.rs index f7c964b95a803c16468cd88f87ae9c258a9945be..e0798c12882cb74615f84ffa422a90356b6ba465 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ extern crate minidom; pub mod error; pub mod ns; +pub mod body; pub mod disco; pub mod data_forms; pub mod media_element; diff --git a/src/ns.rs b/src/ns.rs index 24b81133ab6b16346aad8104e74f0b920b2d06b8..4f18ccc84a4d58aa14701895458bde650374c774 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -1,3 +1,4 @@ +pub const JABBER_CLIENT_NS: &'static str = "jabber:client"; pub const DISCO_INFO_NS: &'static str = "http://jabber.org/protocol/disco#info"; pub const DATA_FORMS_NS: &'static str = "jabber:x:data"; pub const MEDIA_ELEMENT_NS: &'static str = "urn:xmpp:media-element"; From 6a0724d1335a3c80efd1f8f71a27cc908574d409 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 19 Apr 2017 23:21:53 +0100 Subject: [PATCH 011/698] Add a MessagePayload trait, and implement it for Body and ChatState. --- src/chatstates.rs | 3 +++ src/lib.rs | 15 +++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/chatstates.rs b/src/chatstates.rs index 6ec2e22df947ad3d050967c931f555a76c9c43bf..8941408dbdecdf19f46fea81172f660f5dfc4671 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -1,6 +1,7 @@ use minidom::Element; use error::Error; +use super::MessagePayload; use ns::CHATSTATES_NS; @@ -13,6 +14,8 @@ pub enum ChatState { Paused, } +impl MessagePayload for ChatState {} + pub fn parse_chatstate(root: &Element) -> Result { for _ in root.children() { return Err(Error::ParseError("Unknown child in chatstate element.")); diff --git a/src/lib.rs b/src/lib.rs index e0798c12882cb74615f84ffa422a90356b6ba465..8914a6c6eba0bded291f4200c76c06f3cd7c88bf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,3 +12,18 @@ pub mod jingle; pub mod ping; pub mod chatstates; pub mod ibb; + +use std::fmt::Debug; +use minidom::Element; + +pub trait MessagePayload: Debug {} + +pub fn parse_message_payload(elem: &Element) -> Option> { + if let Ok(body) = body::parse_body(elem) { + Some(Box::new(body)) + } else if let Ok(chatstate) = chatstates::parse_chatstate(elem) { + Some(Box::new(chatstate)) + } else { + None + } +} From fc7a0517d31f44302a772fd06e7fa00a2f9d52dc Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 19 Apr 2017 23:41:54 +0100 Subject: [PATCH 012/698] Replace assert!()s with proper errors in parsers. --- src/body.rs | 2 +- src/chatstates.rs | 2 +- src/data_forms.rs | 5 ++++- src/disco.rs | 5 ++++- src/ibb.rs | 2 +- src/jingle.rs | 5 ++++- src/media_element.rs | 5 ++++- src/ping.rs | 5 ++++- 8 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/body.rs b/src/body.rs index 0bf2f498dcae3490d8bc1723198c97a64dbbe11e..43bd6914852cfab737d2018e8215a49683158c1b 100644 --- a/src/body.rs +++ b/src/body.rs @@ -15,7 +15,7 @@ impl MessagePayload for Body {} pub fn parse_body(root: &Element) -> Result { if !root.is("body", JABBER_CLIENT_NS) { - return Err(Error::ParseError("Not a body element.")); + return Err(Error::ParseError("This is not a body element.")); } for _ in root.children() { return Err(Error::ParseError("Unknown child in body element.")); diff --git a/src/chatstates.rs b/src/chatstates.rs index 8941408dbdecdf19f46fea81172f660f5dfc4671..d84880c6591faddc50ff759ea634fccd4d730fd0 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -31,7 +31,7 @@ pub fn parse_chatstate(root: &Element) -> Result { } else if root.is("paused", CHATSTATES_NS) { Ok(ChatState::Paused) } else { - Err(Error::ParseError("Unknown chatstate element.")) + Err(Error::ParseError("This is not a chatstate element.")) } } diff --git a/src/data_forms.rs b/src/data_forms.rs index f4eb70c834487de4db9b088974e2b782db1d0aa5..68e0e00d43e9163bfb62394f7c67851e63ada81f 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -52,7 +52,10 @@ pub struct DataForm { } pub fn parse_data_form(root: &Element) -> Result { - assert!(root.is("x", DATA_FORMS_NS)); + if !root.is("x", DATA_FORMS_NS) { + return Err(Error::ParseError("This is not a data form element.")), + } + let type_: DataFormType = match root.attr("type") { Some(type_) => type_.parse()?, None => return Err(Error::ParseError("Type attribute on data form is mandatory.")), diff --git a/src/disco.rs b/src/disco.rs index e436b2620e899bada9cb0b50a9123ba3e7da858c..d51636e2c09be6c75117ef15ee1739154411701a 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -29,7 +29,10 @@ pub struct Disco { } pub fn parse_disco(root: &Element) -> Result { - assert!(root.is("query", DISCO_INFO_NS)); + if !root.is("query", DISCO_INFO_NS) { + return Err(Error::ParseError("This is not a disco#info element.")), + } + let mut identities: Vec = vec!(); let mut features: Vec = vec!(); let mut extensions: Vec = vec!(); diff --git a/src/ibb.rs b/src/ibb.rs index 1868772ee83e424b326780f5dd14fbb7dc44f9aa..64363c358164af5236816567d9b41d66c36fcde0 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -64,7 +64,7 @@ pub fn parse_ibb(root: &Element) -> Result { stanza: stanza }) } else { - Err(Error::ParseError("Unknown ibb element.")) + Err(Error::ParseError("This is not an ibb element.")) } } diff --git a/src/jingle.rs b/src/jingle.rs index 37912a020c6a02edd9e54d14e85a5f4fdb722189..00b390e4409cf2574bd86863fd990409bf59474f 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -209,7 +209,10 @@ pub struct Jingle { } pub fn parse_jingle(root: &Element) -> Result { - assert!(root.is("jingle", JINGLE_NS)); + if !root.is("jingle", JINGLE_NS) { + return Err(Error::ParseError("This is not a Jingle element.")), + } + let mut contents: Vec = vec!(); let action = root.attr("action") diff --git a/src/media_element.rs b/src/media_element.rs index 86f286eebce54140104076fa18200e8e757521c4..d912f126f80200445d6f42135052b2b5a8cfb702 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -18,7 +18,10 @@ pub struct MediaElement { } pub fn parse_media_element(root: &Element) -> Result { - assert!(root.is("media", MEDIA_ELEMENT_NS)); + if !root.is("media", MEDIA_ELEMENT_NS) { + return Err(Error::ParseError("This is not a media element.")), + } + let width = root.attr("width").and_then(|width| width.parse().ok()); let height = root.attr("height").and_then(|height| height.parse().ok()); let mut uris = vec!(); diff --git a/src/ping.rs b/src/ping.rs index 7176fa42baffa14fdad058c7c94312f2db7f189b..39145e2253ee0f9255e733e656041e34b8a77271 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -9,7 +9,10 @@ pub struct Ping { } pub fn parse_ping(root: &Element) -> Result { - assert!(root.is("ping", PING_NS)); + if !root.is("ping", PING_NS) { + return Err(Error::ParseError("This is not a ping element.")), + } + for _ in root.children() { return Err(Error::ParseError("Unknown child in ping element.")); } From b8b0494c191d10dc89fdee7eb0c33b4763d9e49e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Apr 2017 00:14:29 +0100 Subject: [PATCH 013/698] Fix a stupid copy/paste syntax error. --- src/data_forms.rs | 2 +- src/disco.rs | 2 +- src/jingle.rs | 2 +- src/media_element.rs | 2 +- src/ping.rs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index 68e0e00d43e9163bfb62394f7c67851e63ada81f..342c0c453d4b06e0fb2b899ede67731eef5139a5 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -53,7 +53,7 @@ pub struct DataForm { pub fn parse_data_form(root: &Element) -> Result { if !root.is("x", DATA_FORMS_NS) { - return Err(Error::ParseError("This is not a data form element.")), + return Err(Error::ParseError("This is not a data form element.")); } let type_: DataFormType = match root.attr("type") { diff --git a/src/disco.rs b/src/disco.rs index d51636e2c09be6c75117ef15ee1739154411701a..3100d4b5db27b45b02764b8c4d8f64fcfc564836 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -30,7 +30,7 @@ pub struct Disco { pub fn parse_disco(root: &Element) -> Result { if !root.is("query", DISCO_INFO_NS) { - return Err(Error::ParseError("This is not a disco#info element.")), + return Err(Error::ParseError("This is not a disco#info element.")); } let mut identities: Vec = vec!(); diff --git a/src/jingle.rs b/src/jingle.rs index 00b390e4409cf2574bd86863fd990409bf59474f..a2fc331d80f0fff86ff835d53f9e18fdf77849e6 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -210,7 +210,7 @@ pub struct Jingle { pub fn parse_jingle(root: &Element) -> Result { if !root.is("jingle", JINGLE_NS) { - return Err(Error::ParseError("This is not a Jingle element.")), + return Err(Error::ParseError("This is not a Jingle element.")); } let mut contents: Vec = vec!(); diff --git a/src/media_element.rs b/src/media_element.rs index d912f126f80200445d6f42135052b2b5a8cfb702..7d77400883b38866d911d99518309bee5ea3acb3 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -19,7 +19,7 @@ pub struct MediaElement { pub fn parse_media_element(root: &Element) -> Result { if !root.is("media", MEDIA_ELEMENT_NS) { - return Err(Error::ParseError("This is not a media element.")), + return Err(Error::ParseError("This is not a media element.")); } let width = root.attr("width").and_then(|width| width.parse().ok()); diff --git a/src/ping.rs b/src/ping.rs index 39145e2253ee0f9255e733e656041e34b8a77271..de7aeb7b5f96ce369f9a67bafbc64ae1e054bae2 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -10,7 +10,7 @@ pub struct Ping { pub fn parse_ping(root: &Element) -> Result { if !root.is("ping", PING_NS) { - return Err(Error::ParseError("This is not a ping element.")), + return Err(Error::ParseError("This is not a ping element.")); } for _ in root.children() { From a9993b128194ea16cc25ed88bbfb90c91f0fdbfb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Apr 2017 00:14:47 +0100 Subject: [PATCH 014/698] Remove the MessagePayload trait, it was a bad idea. --- src/body.rs | 3 --- src/chatstates.rs | 3 --- src/lib.rs | 13 ++++++++----- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/body.rs b/src/body.rs index 43bd6914852cfab737d2018e8215a49683158c1b..43c6c83387200a6ae5630bba6ccf9d954d051562 100644 --- a/src/body.rs +++ b/src/body.rs @@ -1,7 +1,6 @@ use minidom::Element; use error::Error; -use super::MessagePayload; // TODO: also support components and servers. use ns::JABBER_CLIENT_NS; @@ -11,8 +10,6 @@ pub struct Body { body: String, } -impl MessagePayload for Body {} - pub fn parse_body(root: &Element) -> Result { if !root.is("body", JABBER_CLIENT_NS) { return Err(Error::ParseError("This is not a body element.")); diff --git a/src/chatstates.rs b/src/chatstates.rs index d84880c6591faddc50ff759ea634fccd4d730fd0..77db1413459f600b36f9384402f1f60877affbdb 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -1,7 +1,6 @@ use minidom::Element; use error::Error; -use super::MessagePayload; use ns::CHATSTATES_NS; @@ -14,8 +13,6 @@ pub enum ChatState { Paused, } -impl MessagePayload for ChatState {} - pub fn parse_chatstate(root: &Element) -> Result { for _ in root.children() { return Err(Error::ParseError("Unknown child in chatstate element.")); diff --git a/src/lib.rs b/src/lib.rs index 8914a6c6eba0bded291f4200c76c06f3cd7c88bf..c6f93d93665ce445ceb32fdbfc48881d47e476e2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,16 +13,19 @@ pub mod ping; pub mod chatstates; pub mod ibb; -use std::fmt::Debug; use minidom::Element; -pub trait MessagePayload: Debug {} +#[derive(Debug)] +pub enum MessagePayload { + Body(body::Body), + ChatState(chatstates::ChatState), +} -pub fn parse_message_payload(elem: &Element) -> Option> { +pub fn parse_message_payload(elem: &Element) -> Option { if let Ok(body) = body::parse_body(elem) { - Some(Box::new(body)) + Some(MessagePayload::Body(body)) } else if let Ok(chatstate) = chatstates::parse_chatstate(elem) { - Some(Box::new(chatstate)) + Some(MessagePayload::ChatState(chatstate)) } else { None } From 861c933c568f42f02cb6b0a0cb24f50d4d35aaeb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Apr 2017 00:43:17 +0100 Subject: [PATCH 015/698] body, chatstates: Fix tests. --- src/body.rs | 24 ++++++++++++------------ src/chatstates.rs | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/body.rs b/src/body.rs index 43c6c83387200a6ae5630bba6ccf9d954d051562..d02e508c858e120af4adc6c3275e449829c0f03d 100644 --- a/src/body.rs +++ b/src/body.rs @@ -24,45 +24,45 @@ pub fn parse_body(root: &Element) -> Result { mod tests { use minidom::Element; use error::Error; - use chatstates; + use body; #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); - chatstates::parse_chatstate(&elem).unwrap(); + let elem: Element = "".parse().unwrap(); + body::parse_body(&elem).unwrap(); } #[test] fn test_invalid() { - let elem: Element = "".parse().unwrap(); - let error = chatstates::parse_chatstate(&elem).unwrap_err(); + let elem: Element = "".parse().unwrap(); + let error = body::parse_body(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown chatstate element."); + assert_eq!(message, "This is not a body element."); } #[test] fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); - let error = chatstates::parse_chatstate(&elem).unwrap_err(); + let elem: Element = "".parse().unwrap(); + let error = body::parse_body(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown child in chatstate element."); + assert_eq!(message, "Unknown child in body element."); } #[test] #[ignore] fn test_invalid_attribute() { - let elem: Element = "".parse().unwrap(); - let error = chatstates::parse_chatstate(&elem).unwrap_err(); + let elem: Element = "".parse().unwrap(); + let error = body::parse_body(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown attribute in chatstate element."); + assert_eq!(message, "Unknown attribute in body element."); } } diff --git a/src/chatstates.rs b/src/chatstates.rs index 77db1413459f600b36f9384402f1f60877affbdb..1651e3a5bcfcee290a226e9b28ec197a6f22f392 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -52,7 +52,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown chatstate element."); + assert_eq!(message, "This is not a chatstate element."); } #[test] From f3c9a58862942fafdfdda3facd3753739173e048 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Apr 2017 00:43:33 +0100 Subject: [PATCH 016/698] Add a receipts parser. --- src/lib.rs | 4 ++++ src/ns.rs | 1 + src/receipts.rs | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 src/receipts.rs diff --git a/src/lib.rs b/src/lib.rs index c6f93d93665ce445ceb32fdbfc48881d47e476e2..f7608cd746e45ad3280fd4a16a476e7a7ad0e70f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ pub mod jingle; pub mod ping; pub mod chatstates; pub mod ibb; +pub mod receipts; use minidom::Element; @@ -19,6 +20,7 @@ use minidom::Element; pub enum MessagePayload { Body(body::Body), ChatState(chatstates::ChatState), + Receipt(receipts::Receipt), } pub fn parse_message_payload(elem: &Element) -> Option { @@ -26,6 +28,8 @@ pub fn parse_message_payload(elem: &Element) -> Option { Some(MessagePayload::Body(body)) } else if let Ok(chatstate) = chatstates::parse_chatstate(elem) { Some(MessagePayload::ChatState(chatstate)) + } else if let Ok(receipt) = receipts::parse_receipt(elem) { + Some(MessagePayload::Receipt(receipt)) } else { None } diff --git a/src/ns.rs b/src/ns.rs index 4f18ccc84a4d58aa14701895458bde650374c774..49293f4f1dbb0ebcece1d3868f3657fb881b53fd 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -6,3 +6,4 @@ pub const JINGLE_NS: &'static str = "urn:xmpp:jingle:1"; pub const PING_NS: &'static str = "urn:xmpp:ping"; pub const CHATSTATES_NS: &'static str = "http://jabber.org/protocol/chatstates"; pub const IBB_NS: &'static str = "http://jabber.org/protocol/ibb"; +pub const RECEIPTS_NS: &'static str = "urn:xmpp:receipts"; diff --git a/src/receipts.rs b/src/receipts.rs new file mode 100644 index 0000000000000000000000000000000000000000..a19dff1c982155638d6dc9c92596256d3f60158d --- /dev/null +++ b/src/receipts.rs @@ -0,0 +1,44 @@ +use minidom::Element; + +use error::Error; + +use ns::RECEIPTS_NS; + +#[derive(Debug)] +pub enum Receipt { + Request, + Received(String), +} + +pub fn parse_receipt(root: &Element) -> Result { + for _ in root.children() { + return Err(Error::ParseError("Unknown child in receipt element.")); + } + if root.is("request", RECEIPTS_NS) { + Ok(Receipt::Request) + } else if root.is("received", RECEIPTS_NS) { + let id = root.attr("id").unwrap_or("").to_owned(); + Ok(Receipt::Received(id)) + } else { + Err(Error::ParseError("This is not a receipt element.")) + } +} + +#[cfg(test)] +mod tests { + use minidom::Element; + //use error::Error; + use receipts; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + receipts::parse_receipt(&elem).unwrap(); + + let elem: Element = "".parse().unwrap(); + receipts::parse_receipt(&elem).unwrap(); + + let elem: Element = "".parse().unwrap(); + receipts::parse_receipt(&elem).unwrap(); + } +} From 55f5435f6e1dfe1e106ed38c591bbd8f7e616de0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Apr 2017 01:31:03 +0100 Subject: [PATCH 017/698] body: Make the string public. --- src/body.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/body.rs b/src/body.rs index d02e508c858e120af4adc6c3275e449829c0f03d..7c5fcae4aa14f3e7d927e804f629f7c32787c485 100644 --- a/src/body.rs +++ b/src/body.rs @@ -7,7 +7,7 @@ use ns::JABBER_CLIENT_NS; #[derive(Debug)] pub struct Body { - body: String, + pub body: String, } pub fn parse_body(root: &Element) -> Result { From e0e6119e55bb3db0da1c94457696824e252d245b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Apr 2017 21:02:51 +0100 Subject: [PATCH 018/698] disco: Relax the parsing rules for get disco#info. --- src/disco.rs | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index 3100d4b5db27b45b02764b8c4d8f64fcfc564836..afb1fa7f6125654573937a33ac99d2ec0b0c99f8 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -85,6 +85,8 @@ pub fn parse_disco(root: &Element) -> Result { } } + /* + // TODO: encode these restrictions only for result disco#info, not get ones. if identities.is_empty() { return Err(Error::ParseError("There must be at least one identity in disco#info.")); } @@ -94,6 +96,7 @@ pub fn parse_disco(root: &Element) -> Result { if !features.contains(&Feature { var: DISCO_INFO_NS.to_owned() }) { return Err(Error::ParseError("disco#info feature not present in disco#info.")); } + */ return Ok(Disco { node: node, @@ -128,15 +131,10 @@ mod tests { _ => panic!(), }; assert_eq!(message, "Unknown element in disco#info."); + } - let elem: Element = "".parse().unwrap(); - let error = disco::parse_disco(&elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "There must be at least one identity in disco#info."); - + #[test] + fn test_invalid_identity() { let elem: Element = "".parse().unwrap(); let error = disco::parse_disco(&elem).unwrap_err(); let message = match error { @@ -168,7 +166,10 @@ mod tests { _ => panic!(), }; assert_eq!(message, "Identity must have a non-empty 'type' attribute."); + } + #[test] + fn test_invalid_feature() { let elem: Element = "".parse().unwrap(); let error = disco::parse_disco(&elem).unwrap_err(); let message = match error { @@ -176,6 +177,18 @@ mod tests { _ => panic!(), }; assert_eq!(message, "Feature must have a 'var' attribute."); + } + + #[test] + #[ignore] + fn test_invalid_result() { + let elem: Element = "".parse().unwrap(); + let error = disco::parse_disco(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "There must be at least one identity in disco#info."); let elem: Element = "".parse().unwrap(); let error = disco::parse_disco(&elem).unwrap_err(); From 1190dd9001a5c945f576064281b2cdcc6f82f5a1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Apr 2017 21:03:02 +0100 Subject: [PATCH 019/698] disco: Add a serializer. --- src/disco.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/disco.rs b/src/disco.rs index afb1fa7f6125654573937a33ac99d2ec0b0c99f8..2a4ff64a6f8beb18f7bae6fa7cc72e83c3543a42 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -106,6 +106,34 @@ pub fn parse_disco(root: &Element) -> Result { }); } +pub fn serialise_disco(disco: Disco) -> Element { + let mut root = Element::builder("query") + .ns(DISCO_INFO_NS) + .attr("node", disco.node) + .build(); + for identity in disco.identities { + let identity_element = Element::builder("identity") + .ns(DISCO_INFO_NS) + .attr("category", identity.category) + .attr("type", identity.type_) + .attr("xml:lang", identity.xml_lang) + .attr("name", identity.name) + .build(); + root.append_child(identity_element); + } + for feature in disco.features { + let feature_element = Element::builder("feature") + .ns(DISCO_INFO_NS) + .attr("var", feature.var) + .build(); + root.append_child(feature_element); + } + for _ in disco.extensions { + panic!("Not yet implemented!"); + } + root +} + #[cfg(test)] mod tests { use minidom::Element; From 83cf57abd05da4bf4b3ea9f2edb0453ba91087c7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Apr 2017 21:53:46 +0100 Subject: [PATCH 020/698] disco: Use a reference everywhere, instead of taking ownership. --- src/disco.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index 2a4ff64a6f8beb18f7bae6fa7cc72e83c3543a42..4b2d0334e48350dd8f1f454490176b53581ae9b9 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -106,29 +106,29 @@ pub fn parse_disco(root: &Element) -> Result { }); } -pub fn serialise_disco(disco: Disco) -> Element { +pub fn serialise_disco(disco: &Disco) -> Element { let mut root = Element::builder("query") .ns(DISCO_INFO_NS) - .attr("node", disco.node) + .attr("node", disco.node.clone()) .build(); - for identity in disco.identities { + for identity in &disco.identities { let identity_element = Element::builder("identity") .ns(DISCO_INFO_NS) - .attr("category", identity.category) - .attr("type", identity.type_) - .attr("xml:lang", identity.xml_lang) - .attr("name", identity.name) + .attr("category", identity.category.clone()) + .attr("type", identity.type_.clone()) + .attr("xml:lang", identity.xml_lang.clone()) + .attr("name", identity.name.clone()) .build(); root.append_child(identity_element); } - for feature in disco.features { + for feature in &disco.features { let feature_element = Element::builder("feature") .ns(DISCO_INFO_NS) - .attr("var", feature.var) + .attr("var", feature.var.clone()) .build(); root.append_child(feature_element); } - for _ in disco.extensions { + for _ in &disco.extensions { panic!("Not yet implemented!"); } root From 411e421732a18a5fb950f0a0b09c2c74ffa29ff8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Apr 2017 21:53:46 +0100 Subject: [PATCH 021/698] ecaps2: Use a reference everywhere, instead of taking ownership. --- src/ecaps2.rs | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 50b94128ee78fed1849251d620e41b9c1b9f47b4..fddc712b336e9ccf98cc317f9a05549cda8bdee5 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -7,13 +7,13 @@ use error::Error; use disco::{Feature, Identity, Disco, parse_disco}; use data_forms::DataForm; -fn compute_item(field: String) -> Vec { +fn compute_item(field: &String) -> Vec { let mut bytes = field.as_bytes().to_vec(); bytes.push(0x1f); bytes } -fn compute_items Vec>(things: Vec, separator: u8, encode: F) -> Vec { +fn compute_items Vec>(things: &Vec, separator: u8, encode: F) -> Vec { let mut string: Vec = vec!(); let mut accumulator: Vec> = vec!(); for thing in things { @@ -29,36 +29,36 @@ fn compute_items Vec>(things: Vec, separator: u8, encode: string } -fn compute_features(features: Vec) -> Vec { - compute_items(features, 0x1c, |feature| compute_item(feature.var)) +fn compute_features(features: &Vec) -> Vec { + compute_items(features, 0x1c, |feature| compute_item(&feature.var)) } -fn compute_identities(identities: Vec) -> Vec { +fn compute_identities(identities: &Vec) -> Vec { compute_items(identities, 0x1c, |identity| { - let mut bytes = compute_item(identity.category); - bytes.append(&mut compute_item(identity.type_)); - bytes.append(&mut compute_item(identity.xml_lang)); - bytes.append(&mut compute_item(identity.name.unwrap_or(String::new()))); + let mut bytes = compute_item(&identity.category); + bytes.append(&mut compute_item(&identity.type_)); + bytes.append(&mut compute_item(&identity.xml_lang)); + bytes.append(&mut compute_item(&identity.name.clone().unwrap_or(String::new()))); bytes.push(0x1e); bytes }) } -fn compute_extensions(extensions: Vec) -> Vec { +fn compute_extensions(extensions: &Vec) -> Vec { compute_items(extensions, 0x1c, |extension| { - compute_items(extension.fields, 0x1d, |field| { - let mut bytes = compute_item(field.var); - bytes.append(&mut compute_items(field.values, 0x1e, + compute_items(&extension.fields, 0x1d, |field| { + let mut bytes = compute_item(&field.var); + bytes.append(&mut compute_items(&field.values, 0x1e, |value| compute_item(value))); bytes }) }) } -fn compute_disco(disco: Disco) -> Vec { - let features_string = compute_features(disco.features); - let identities_string = compute_identities(disco.identities); - let extensions_string = compute_extensions(disco.extensions); +pub fn compute_disco(disco: &Disco) -> Vec { + let features_string = compute_features(&disco.features); + let identities_string = compute_identities(&disco.identities); + let extensions_string = compute_extensions(&disco.extensions); let mut final_string = vec!(); final_string.extend(features_string); @@ -69,7 +69,7 @@ fn compute_disco(disco: Disco) -> Vec { pub fn convert_element(root: &Element) -> Result, Error> { let disco = parse_disco(root)?; - let final_string = compute_disco(disco); + let final_string = compute_disco(&disco); Ok(final_string) } From 7288c2c74fe77abd5d3e9dd751d5b25ee2c29323 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Apr 2017 23:16:12 +0100 Subject: [PATCH 022/698] Import ns itself, and remove the _NS suffix on all namespaces. --- src/body.rs | 6 +++--- src/chatstates.rs | 12 ++++++------ src/data_forms.rs | 10 +++++----- src/disco.rs | 18 +++++++++--------- src/ibb.rs | 4 ++-- src/jingle.rs | 10 +++++----- src/media_element.rs | 6 +++--- src/ns.rs | 18 +++++++++--------- src/ping.rs | 4 ++-- src/receipts.rs | 6 +++--- 10 files changed, 47 insertions(+), 47 deletions(-) diff --git a/src/body.rs b/src/body.rs index 7c5fcae4aa14f3e7d927e804f629f7c32787c485..64624695e2d079ff1124a730256d47fb99972f63 100644 --- a/src/body.rs +++ b/src/body.rs @@ -2,8 +2,7 @@ use minidom::Element; use error::Error; -// TODO: also support components and servers. -use ns::JABBER_CLIENT_NS; +use ns; #[derive(Debug)] pub struct Body { @@ -11,7 +10,8 @@ pub struct Body { } pub fn parse_body(root: &Element) -> Result { - if !root.is("body", JABBER_CLIENT_NS) { + // TODO: also support components and servers. + if !root.is("body", ns::JABBER_CLIENT) { return Err(Error::ParseError("This is not a body element.")); } for _ in root.children() { diff --git a/src/chatstates.rs b/src/chatstates.rs index 1651e3a5bcfcee290a226e9b28ec197a6f22f392..5a9a4dc4d5fe8ecd68d031a81a9f45441c58d555 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -2,7 +2,7 @@ use minidom::Element; use error::Error; -use ns::CHATSTATES_NS; +use ns; #[derive(Debug)] pub enum ChatState { @@ -17,15 +17,15 @@ pub fn parse_chatstate(root: &Element) -> Result { for _ in root.children() { return Err(Error::ParseError("Unknown child in chatstate element.")); } - if root.is("active", CHATSTATES_NS) { + if root.is("active", ns::CHATSTATES) { Ok(ChatState::Active) - } else if root.is("composing", CHATSTATES_NS) { + } else if root.is("composing", ns::CHATSTATES) { Ok(ChatState::Composing) - } else if root.is("gone", CHATSTATES_NS) { + } else if root.is("gone", ns::CHATSTATES) { Ok(ChatState::Gone) - } else if root.is("inactive", CHATSTATES_NS) { + } else if root.is("inactive", ns::CHATSTATES) { Ok(ChatState::Inactive) - } else if root.is("paused", CHATSTATES_NS) { + } else if root.is("paused", ns::CHATSTATES) { Ok(ChatState::Paused) } else { Err(Error::ParseError("This is not a chatstate element.")) diff --git a/src/data_forms.rs b/src/data_forms.rs index 342c0c453d4b06e0fb2b899ede67731eef5139a5..4ea50c965458e9d3aa433cf7e2415893bb71dd34 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -5,7 +5,7 @@ use std::str::FromStr; use minidom::Element; use error::Error; -use ns::{DATA_FORMS_NS, MEDIA_ELEMENT_NS}; +use ns; use media_element::{MediaElement, parse_media_element}; @@ -52,7 +52,7 @@ pub struct DataForm { } pub fn parse_data_form(root: &Element) -> Result { - if !root.is("x", DATA_FORMS_NS) { + if !root.is("x", ns::DATA_FORMS) { return Err(Error::ParseError("This is not a data form element.")); } @@ -63,16 +63,16 @@ pub fn parse_data_form(root: &Element) -> Result { let mut fields = vec!(); let mut form_type = None; for field in root.children() { - if field.is("field", DATA_FORMS_NS) { + if field.is("field", ns::DATA_FORMS) { let var = field.attr("var").ok_or(Error::ParseError("Field must have a 'var' attribute."))?; let field_type = field.attr("type").unwrap_or("text-single"); let label = field.attr("label").and_then(|label| label.parse().ok()); let mut values = vec!(); let mut media = vec!(); for element in field.children() { - if element.is("value", DATA_FORMS_NS) { + if element.is("value", ns::DATA_FORMS) { values.push(element.text()); - } else if element.is("media", MEDIA_ELEMENT_NS) { + } else if element.is("media", ns::MEDIA_ELEMENT) { match parse_media_element(element) { Ok(media_element) => media.push(media_element), Err(_) => (), // TODO: is it really nice to swallow this error? diff --git a/src/disco.rs b/src/disco.rs index 4b2d0334e48350dd8f1f454490176b53581ae9b9..39befce5fde89bc3c9dcb48faf388d8777aca565 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -3,7 +3,7 @@ extern crate minidom; use minidom::Element; use error::Error; -use ns::{DISCO_INFO_NS, DATA_FORMS_NS}; +use ns; use data_forms::{DataForm, DataFormType, parse_data_form}; @@ -29,7 +29,7 @@ pub struct Disco { } pub fn parse_disco(root: &Element) -> Result { - if !root.is("query", DISCO_INFO_NS) { + if !root.is("query", ns::DISCO_INFO) { return Err(Error::ParseError("This is not a disco#info element.")); } @@ -41,13 +41,13 @@ pub fn parse_disco(root: &Element) -> Result { .and_then(|node| node.parse().ok()); for child in root.children() { - if child.is("feature", DISCO_INFO_NS) { + if child.is("feature", ns::DISCO_INFO) { let feature = child.attr("var") .ok_or(Error::ParseError("Feature must have a 'var' attribute."))?; features.push(Feature { var: feature.to_owned(), }); - } else if child.is("identity", DISCO_INFO_NS) { + } else if child.is("identity", ns::DISCO_INFO) { let category = child.attr("category") .ok_or(Error::ParseError("Identity must have a 'category' attribute."))?; if category == "" { @@ -70,7 +70,7 @@ pub fn parse_disco(root: &Element) -> Result { xml_lang: xml_lang.to_owned(), name: name, }); - } else if child.is("x", DATA_FORMS_NS) { + } else if child.is("x", ns::DATA_FORMS) { let data_form = parse_data_form(child)?; match data_form.type_ { DataFormType::Result_ => (), @@ -93,7 +93,7 @@ pub fn parse_disco(root: &Element) -> Result { if features.is_empty() { return Err(Error::ParseError("There must be at least one feature in disco#info.")); } - if !features.contains(&Feature { var: DISCO_INFO_NS.to_owned() }) { + if !features.contains(&Feature { var: ns::DISCO_INFO.to_owned() }) { return Err(Error::ParseError("disco#info feature not present in disco#info.")); } */ @@ -108,12 +108,12 @@ pub fn parse_disco(root: &Element) -> Result { pub fn serialise_disco(disco: &Disco) -> Element { let mut root = Element::builder("query") - .ns(DISCO_INFO_NS) + .ns(ns::DISCO_INFO) .attr("node", disco.node.clone()) .build(); for identity in &disco.identities { let identity_element = Element::builder("identity") - .ns(DISCO_INFO_NS) + .ns(ns::DISCO_INFO) .attr("category", identity.category.clone()) .attr("type", identity.type_.clone()) .attr("xml:lang", identity.xml_lang.clone()) @@ -123,7 +123,7 @@ pub fn serialise_disco(disco: &Disco) -> Element { } for feature in &disco.features { let feature_element = Element::builder("feature") - .ns(DISCO_INFO_NS) + .ns(ns::DISCO_INFO) .attr("var", feature.var.clone()) .build(); root.append_child(feature_element); diff --git a/src/ibb.rs b/src/ibb.rs index 64363c358164af5236816567d9b41d66c36fcde0..fb368a566cbb75e3990bf92d9c9fa51cd42eccac 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -4,7 +4,7 @@ use minidom::Element; use error::Error; -use ns::IBB_NS; +use ns; #[derive(Debug)] pub enum Stanza { @@ -49,7 +49,7 @@ fn required_attr(root: &Element, attr: &str, err: Error) -> Result Result { - if root.is("open", IBB_NS) { + if root.is("open", ns::IBB) { let block_size = required_attr(root, "block-size", Error::ParseError("Required attribute 'block-size' missing in open element."))?; let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in open element."))?; let stanza = root.attr("stanza") diff --git a/src/jingle.rs b/src/jingle.rs index a2fc331d80f0fff86ff835d53f9e18fdf77849e6..40ba69da3a965e79c92fd6b1d38068ed11288b8c 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -5,7 +5,7 @@ use std::str::FromStr; use minidom::Element; use error::Error; -use ns::JINGLE_NS; +use ns; #[derive(Debug, PartialEq)] pub enum Action { @@ -209,7 +209,7 @@ pub struct Jingle { } pub fn parse_jingle(root: &Element) -> Result { - if !root.is("jingle", JINGLE_NS) { + if !root.is("jingle", ns::JINGLE) { return Err(Error::ParseError("This is not a Jingle element.")); } @@ -227,7 +227,7 @@ pub fn parse_jingle(root: &Element) -> Result { let mut reason_element = None; for child in root.children() { - if child.is("content", JINGLE_NS) { + if child.is("content", ns::JINGLE) { let creator = child.attr("creator") .ok_or(Error::ParseError("Content must have a 'creator' attribute."))? .parse()?; @@ -276,14 +276,14 @@ pub fn parse_jingle(root: &Element) -> Result { transport: transport, security: security.to_owned(), }); - } else if child.is("reason", JINGLE_NS) { + } else if child.is("reason", ns::JINGLE) { if reason_element.is_some() { return Err(Error::ParseError("Jingle must not have more than one reason.")); } let mut reason = None; let mut text = None; for stuff in child.children() { - if stuff.ns() != Some(JINGLE_NS) { + if stuff.ns() != Some(ns::JINGLE) { return Err(Error::ParseError("Reason contains a foreign element.")); } let name = stuff.name(); diff --git a/src/media_element.rs b/src/media_element.rs index 7d77400883b38866d911d99518309bee5ea3acb3..db40dfc6042d809128259cea73d86bdea25bfb23 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -2,7 +2,7 @@ use minidom::Element; use error::Error; -use ns::MEDIA_ELEMENT_NS; +use ns; #[derive(Debug)] pub struct URI { @@ -18,7 +18,7 @@ pub struct MediaElement { } pub fn parse_media_element(root: &Element) -> Result { - if !root.is("media", MEDIA_ELEMENT_NS) { + if !root.is("media", ns::MEDIA_ELEMENT) { return Err(Error::ParseError("This is not a media element.")); } @@ -26,7 +26,7 @@ pub fn parse_media_element(root: &Element) -> Result { let height = root.attr("height").and_then(|height| height.parse().ok()); let mut uris = vec!(); for uri in root.children() { - if uri.is("uri", MEDIA_ELEMENT_NS) { + if uri.is("uri", ns::MEDIA_ELEMENT) { let type_ = uri.attr("type").ok_or(Error::ParseError("Attribute type on uri is mandatory."))?; let text = uri.text().trim().to_owned(); if text == "" { diff --git a/src/ns.rs b/src/ns.rs index 49293f4f1dbb0ebcece1d3868f3657fb881b53fd..a1848e52d870b287db41528d696fc098bad66066 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -1,9 +1,9 @@ -pub const JABBER_CLIENT_NS: &'static str = "jabber:client"; -pub const DISCO_INFO_NS: &'static str = "http://jabber.org/protocol/disco#info"; -pub const DATA_FORMS_NS: &'static str = "jabber:x:data"; -pub const MEDIA_ELEMENT_NS: &'static str = "urn:xmpp:media-element"; -pub const JINGLE_NS: &'static str = "urn:xmpp:jingle:1"; -pub const PING_NS: &'static str = "urn:xmpp:ping"; -pub const CHATSTATES_NS: &'static str = "http://jabber.org/protocol/chatstates"; -pub const IBB_NS: &'static str = "http://jabber.org/protocol/ibb"; -pub const RECEIPTS_NS: &'static str = "urn:xmpp:receipts"; +pub const JABBER_CLIENT: &'static str = "jabber:client"; +pub const DISCO_INFO: &'static str = "http://jabber.org/protocol/disco#info"; +pub const DATA_FORMS: &'static str = "jabber:x:data"; +pub const MEDIA_ELEMENT: &'static str = "urn:xmpp:media-element"; +pub const JINGLE: &'static str = "urn:xmpp:jingle:1"; +pub const PING: &'static str = "urn:xmpp:ping"; +pub const CHATSTATES: &'static str = "http://jabber.org/protocol/chatstates"; +pub const IBB: &'static str = "http://jabber.org/protocol/ibb"; +pub const RECEIPTS: &'static str = "urn:xmpp:receipts"; diff --git a/src/ping.rs b/src/ping.rs index de7aeb7b5f96ce369f9a67bafbc64ae1e054bae2..adb0f45c27011832de057c9632ffe3e08af470da 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -2,14 +2,14 @@ use minidom::Element; use error::Error; -use ns::PING_NS; +use ns; #[derive(Debug)] pub struct Ping { } pub fn parse_ping(root: &Element) -> Result { - if !root.is("ping", PING_NS) { + if !root.is("ping", ns::PING) { return Err(Error::ParseError("This is not a ping element.")); } diff --git a/src/receipts.rs b/src/receipts.rs index a19dff1c982155638d6dc9c92596256d3f60158d..a45a54c106ec554e016792d09a36eb6f10c8883c 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -2,7 +2,7 @@ use minidom::Element; use error::Error; -use ns::RECEIPTS_NS; +use ns; #[derive(Debug)] pub enum Receipt { @@ -14,9 +14,9 @@ pub fn parse_receipt(root: &Element) -> Result { for _ in root.children() { return Err(Error::ParseError("Unknown child in receipt element.")); } - if root.is("request", RECEIPTS_NS) { + if root.is("request", ns::RECEIPTS) { Ok(Receipt::Request) - } else if root.is("received", RECEIPTS_NS) { + } else if root.is("received", ns::RECEIPTS) { let id = root.attr("id").unwrap_or("").to_owned(); Ok(Receipt::Received(id)) } else { From efedb215c4933cef80114c9e0f774ea7a43fec1b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 00:01:57 +0100 Subject: [PATCH 023/698] ecaps2: Add Rust-Crypto and base64 dependencies, and implement hashing. --- Cargo.toml | 4 ++++ src/ecaps2.rs | 66 ++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 10c55dd53824d3fe4df43a74c6b6b8975951ee11..7102bc64eb6d1972ee60a46a6e4b0947ac81ae56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,3 +5,7 @@ authors = ["Emmanuel Gil Peyrot "] [dependencies] minidom = "0.1.1" +base64 = "0.4.1" +sha2 = "0.5.0" +sha3 = "0.5.0" +blake2 = "0.5.0" diff --git a/src/ecaps2.rs b/src/ecaps2.rs index fddc712b336e9ccf98cc317f9a05549cda8bdee5..21efc9c372a58add7baae397dfad113119283ce9 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -1,4 +1,8 @@ extern crate minidom; +extern crate sha2; +extern crate sha3; +extern crate blake2; +extern crate base64; use minidom::Element; @@ -7,6 +11,10 @@ use error::Error; use disco::{Feature, Identity, Disco, parse_disco}; use data_forms::DataForm; +use self::sha2::{Sha256, Sha512, Digest}; +use self::sha3::{Sha3_256, Sha3_512}; +use self::blake2::{Blake2b}; + fn compute_item(field: &String) -> Vec { let mut bytes = field.as_bytes().to_vec(); bytes.push(0x1f); @@ -73,6 +81,52 @@ pub fn convert_element(root: &Element) -> Result, Error> { Ok(final_string) } +pub fn hash_ecaps2(data: &Vec, algo: String) -> String { + match algo.as_ref() { + "sha-256" => { + let mut hasher = Sha256::default(); + hasher.input(data); + let hash = hasher.result(); + base64::encode(&hash) + }, + "sha-512" => { + let mut hasher = Sha512::default(); + hasher.input(data); + let hash = hasher.result(); + base64::encode(&hash) + }, + "sha3-256" => { + let mut hasher = Sha3_256::default(); + hasher.input(data); + let hash = hasher.result(); + base64::encode(&hash) + }, + "sha3-512" => { + let mut hasher = Sha3_512::default(); + hasher.input(data); + let hash = hasher.result(); + base64::encode(&hash) + }, + /* + "blake2b-256" => { + // TODO: bit length is most likely wrong here! + let mut hasher = Blake2b::default(); + hasher.input(data); + let hash = hasher.result(); + base64::encode(&hash) + }, + "blake2b-512" => { + // TODO: bit length is most likely wrong here! + let mut hasher = Blake2b::default(); + hasher.input(data); + let hash = hasher.result(); + base64::encode(&hash) + }, + */ + _ => panic!(), + } +} + #[cfg(test)] mod tests { use minidom::Element; @@ -147,12 +201,10 @@ mod tests { assert_eq!(ecaps2.len(), 0x1d9); assert_eq!(ecaps2, expected); - /* - let sha_256 = hash(ecaps2, "sha-256"); + let sha_256 = ecaps2::hash_ecaps2(&ecaps2, String::from("sha-256")); assert_eq!(sha_256, "kzBZbkqJ3ADrj7v08reD1qcWUwNGHaidNUgD7nHpiw8="); - let sha3_256 = hash(ecaps2, "sha3-256"); + let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, String::from("sha3-256")); assert_eq!(sha3_256, "79mdYAfU9rEdTOcWDO7UEAt6E56SUzk/g6TnqUeuD9Q="); - */ } #[test] @@ -320,11 +372,9 @@ mod tests { assert_eq!(ecaps2.len(), 0x543); assert_eq!(ecaps2, expected); - /* - let sha_256 = hash(ecaps2, "sha-256"); + let sha_256 = ecaps2::hash_ecaps2(&ecaps2, String::from("sha-256")); assert_eq!(sha_256, "u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY="); - let sha3_256 = hash(ecaps2, "sha3-256"); + let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, String::from("sha3-256")); assert_eq!(sha3_256, "XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg="); - */ } } From 88ae9aca3f1115a0297c1ef7ed25ea7810d157ea Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 00:24:20 +0100 Subject: [PATCH 024/698] ecaps2: Remove unused brackets around Blake2b. --- src/ecaps2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 21efc9c372a58add7baae397dfad113119283ce9..da36f9e19e6b8e8fcfcd16aedd5e969c0b98d049 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -13,7 +13,7 @@ use data_forms::DataForm; use self::sha2::{Sha256, Sha512, Digest}; use self::sha3::{Sha3_256, Sha3_512}; -use self::blake2::{Blake2b}; +use self::blake2::Blake2b; fn compute_item(field: &String) -> Vec { let mut bytes = field.as_bytes().to_vec(); From 62d9385728b689f7399bfd35f1fcb4ecb4ffaa35 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 00:41:15 +0100 Subject: [PATCH 025/698] Make all parsed types implement Clone. --- src/body.rs | 2 +- src/chatstates.rs | 2 +- src/data_forms.rs | 6 +++--- src/disco.rs | 6 +++--- src/ibb.rs | 4 ++-- src/jingle.rs | 14 +++++++------- src/media_element.rs | 4 ++-- src/ping.rs | 2 +- src/receipts.rs | 2 +- 9 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/body.rs b/src/body.rs index 64624695e2d079ff1124a730256d47fb99972f63..45406e6c7f76b851270293420c93f8a951be578f 100644 --- a/src/body.rs +++ b/src/body.rs @@ -4,7 +4,7 @@ use error::Error; use ns; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Body { pub body: String, } diff --git a/src/chatstates.rs b/src/chatstates.rs index 5a9a4dc4d5fe8ecd68d031a81a9f45441c58d555..8732d5653dc1aa5648bdb44663f52511cb58ac9c 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -4,7 +4,7 @@ use error::Error; use ns; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum ChatState { Active, Composing, diff --git a/src/data_forms.rs b/src/data_forms.rs index 4ea50c965458e9d3aa433cf7e2415893bb71dd34..dd9b3e11be87072e6e8049a83c283e614f1e94de 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -9,7 +9,7 @@ use ns; use media_element::{MediaElement, parse_media_element}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Field { pub var: String, pub type_: String, // TODO: use an enum here. @@ -18,7 +18,7 @@ pub struct Field { pub media: Vec, } -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum DataFormType { Cancel, Form, @@ -44,7 +44,7 @@ impl FromStr for DataFormType { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct DataForm { pub type_: DataFormType, pub form_type: Option, diff --git a/src/disco.rs b/src/disco.rs index 39befce5fde89bc3c9dcb48faf388d8777aca565..a9d8b37082923346a966679fa791518a9c20f03d 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -7,12 +7,12 @@ use ns; use data_forms::{DataForm, DataFormType, parse_data_form}; -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub struct Feature { pub var: String, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Identity { pub category: String, // TODO: use an enum here. pub type_: String, // TODO: use an enum here. @@ -20,7 +20,7 @@ pub struct Identity { pub name: Option, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Disco { pub node: Option, pub identities: Vec, diff --git a/src/ibb.rs b/src/ibb.rs index fb368a566cbb75e3990bf92d9c9fa51cd42eccac..00d30f3f0a313ea7d1342fce32f3d94c14ed71a2 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -6,7 +6,7 @@ use error::Error; use ns; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Stanza { Iq, Message, @@ -32,7 +32,7 @@ impl FromStr for Stanza { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum IBB { Open { block_size: u16, sid: String, stanza: Stanza }, Data(u16, String, Vec), diff --git a/src/jingle.rs b/src/jingle.rs index 40ba69da3a965e79c92fd6b1d38068ed11288b8c..2b7f253f49ed02f726b219b2cae4877dacac6a94 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -7,7 +7,7 @@ use minidom::Element; use error::Error; use ns; -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum Action { ContentAccept, ContentAdd, @@ -69,7 +69,7 @@ impl FromStr for Action { // TODO: use a real JID type. type Jid = String; -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum Creator { Initiator, Responder, @@ -89,7 +89,7 @@ impl FromStr for Creator { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum Senders { Both, Initiator, @@ -115,7 +115,7 @@ impl FromStr for Senders { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Content { pub creator: Creator, pub disposition: String, @@ -126,7 +126,7 @@ pub struct Content { pub security: Option, } -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum Reason { AlternativeSession, //(String), Busy, @@ -191,13 +191,13 @@ impl FromStr for Reason { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ReasonElement { pub reason: Reason, pub text: Option, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Jingle { pub action: Action, pub initiator: Option, diff --git a/src/media_element.rs b/src/media_element.rs index db40dfc6042d809128259cea73d86bdea25bfb23..b48636a507ce0a80ac7e3ec252690b8eb60161a1 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -4,13 +4,13 @@ use error::Error; use ns; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct URI { pub type_: String, pub uri: String, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct MediaElement { pub width: Option, pub height: Option, diff --git a/src/ping.rs b/src/ping.rs index adb0f45c27011832de057c9632ffe3e08af470da..2fb3e5cc693bbac4a03855cebb3e4d45f91b5401 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -4,7 +4,7 @@ use error::Error; use ns; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Ping { } diff --git a/src/receipts.rs b/src/receipts.rs index a45a54c106ec554e016792d09a36eb6f10c8883c..052982ca8ecf8e4b5deaa1f238cb7242e078c6bd 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -4,7 +4,7 @@ use error::Error; use ns; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Receipt { Request, Received(String), From 0da3d55e40f783c54e65052a783e49b02555d69d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 01:06:30 +0100 Subject: [PATCH 026/698] First attempt at a documentation. --- src/lib.rs | 45 ++++++++++++++++++++++++++++++++++++++------- src/ns.rs | 32 +++++++++++++++++++++++++++----- 2 files changed, 65 insertions(+), 12 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f7608cd746e45ad3280fd4a16a476e7a7ad0e70f..dc8fcdb1a277e7283975f36c667ebdfa2ce0164d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,21 +1,51 @@ +//! A crate parsing common XMPP elements into Rust structures. +//! +//! The main entrypoint is `parse_message_payload`, it takes a minidom +//! `Element` reference and optionally returns `Some(MessagePayload)` if the +//! parsing succeeded. +//! +//! Parsed structs can then be manipulated internally, and serialised back +//! before being sent over the wire. + extern crate minidom; +use minidom::Element; +/// Error type returned by every parser on failure. pub mod error; +/// XML namespace definitions used through XMPP. pub mod ns; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod body; -pub mod disco; + +/// XEP-0004: Data Forms pub mod data_forms; -pub mod media_element; -pub mod ecaps2; -pub mod jingle; -pub mod ping; -pub mod chatstates; + +/// XEP-0030: Service Discovery +pub mod disco; + +/// XEP-0047: In-Band Bytestreams pub mod ibb; + +/// XEP-0085: Chat State Notifications +pub mod chatstates; + +/// XEP-0166: Jingle +pub mod jingle; + +/// XEP-0184: Message Delivery Receipts pub mod receipts; -use minidom::Element; +/// XEP-0199: XMPP Ping +pub mod ping; + +/// XEP-0221: Data Forms Media Element +pub mod media_element; + +/// XEP-0390: Entity Capabilities 2.0 +pub mod ecaps2; +/// Lists every known payload of a ``. #[derive(Debug)] pub enum MessagePayload { Body(body::Body), @@ -23,6 +53,7 @@ pub enum MessagePayload { Receipt(receipts::Receipt), } +/// Parse one of the payloads of a `` element, and return `Some` of a `MessagePayload` if parsing it succeeded, `None` otherwise. pub fn parse_message_payload(elem: &Element) -> Option { if let Ok(body) = body::parse_body(elem) { Some(MessagePayload::Body(body)) diff --git a/src/ns.rs b/src/ns.rs index a1848e52d870b287db41528d696fc098bad66066..5bcf6d1e8eab104fd7f6837755ab6e1a4d1fe67e 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -1,9 +1,31 @@ +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub const JABBER_CLIENT: &'static str = "jabber:client"; -pub const DISCO_INFO: &'static str = "http://jabber.org/protocol/disco#info"; + +/// XEP-0004: Data Forms pub const DATA_FORMS: &'static str = "jabber:x:data"; -pub const MEDIA_ELEMENT: &'static str = "urn:xmpp:media-element"; -pub const JINGLE: &'static str = "urn:xmpp:jingle:1"; -pub const PING: &'static str = "urn:xmpp:ping"; -pub const CHATSTATES: &'static str = "http://jabber.org/protocol/chatstates"; + +/// XEP-0030: Service Discovery +pub const DISCO_INFO: &'static str = "http://jabber.org/protocol/disco#info"; + +/// XEP-0047: In-Band Bytestreams pub const IBB: &'static str = "http://jabber.org/protocol/ibb"; + +/// XEP-0085: Chat State Notifications +pub const CHATSTATES: &'static str = "http://jabber.org/protocol/chatstates"; + +/// XEP-0166: Jingle +pub const JINGLE: &'static str = "urn:xmpp:jingle:1"; + +/// XEP-0184: Message Delivery Receipts pub const RECEIPTS: &'static str = "urn:xmpp:receipts"; + +/// XEP-0199: XMPP Ping +pub const PING: &'static str = "urn:xmpp:ping"; + +/// XEP-0221: Data Forms Media Element +pub const MEDIA_ELEMENT: &'static str = "urn:xmpp:media-element"; + +/// XEP-0390: Entity Capabilities 2.0 +pub const ECAPS2: &'static str = "urn:xmpp:caps"; +/// XEP-0390: Entity Capabilities 2.0 +pub const ECAPS2_OPTIMIZE: &'static str = "urn:xmpp:caps:optimize"; From 3209b04a50947c23520f59730f4873e935dc0747 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 01:28:58 +0100 Subject: [PATCH 027/698] Fix all warnings issued by `cargo clippy`. --- src/disco.rs | 4 ++-- src/ecaps2.rs | 22 +++++++++++----------- src/ibb.rs | 2 +- src/jingle.rs | 4 ++-- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index a9d8b37082923346a966679fa791518a9c20f03d..4ac7240e163f6f901a7b6b995ac27542f7681a50 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -98,12 +98,12 @@ pub fn parse_disco(root: &Element) -> Result { } */ - return Ok(Disco { + Ok(Disco { node: node, identities: identities, features: features, extensions: extensions - }); + }) } pub fn serialise_disco(disco: &Disco) -> Element { diff --git a/src/ecaps2.rs b/src/ecaps2.rs index da36f9e19e6b8e8fcfcd16aedd5e969c0b98d049..e2e2de186127dee2f1dd4713fdd30e79adbfc4d7 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -15,13 +15,13 @@ use self::sha2::{Sha256, Sha512, Digest}; use self::sha3::{Sha3_256, Sha3_512}; use self::blake2::Blake2b; -fn compute_item(field: &String) -> Vec { +fn compute_item(field: &str) -> Vec { let mut bytes = field.as_bytes().to_vec(); bytes.push(0x1f); bytes } -fn compute_items Vec>(things: &Vec, separator: u8, encode: F) -> Vec { +fn compute_items Vec>(things: &[T], separator: u8, encode: F) -> Vec { let mut string: Vec = vec!(); let mut accumulator: Vec> = vec!(); for thing in things { @@ -37,22 +37,22 @@ fn compute_items Vec>(things: &Vec, separator: u8, encode string } -fn compute_features(features: &Vec) -> Vec { +fn compute_features(features: &[Feature]) -> Vec { compute_items(features, 0x1c, |feature| compute_item(&feature.var)) } -fn compute_identities(identities: &Vec) -> Vec { +fn compute_identities(identities: &[Identity]) -> Vec { compute_items(identities, 0x1c, |identity| { let mut bytes = compute_item(&identity.category); bytes.append(&mut compute_item(&identity.type_)); bytes.append(&mut compute_item(&identity.xml_lang)); - bytes.append(&mut compute_item(&identity.name.clone().unwrap_or(String::new()))); + bytes.append(&mut compute_item(&identity.name.clone().unwrap_or_default())); bytes.push(0x1e); bytes }) } -fn compute_extensions(extensions: &Vec) -> Vec { +fn compute_extensions(extensions: &[DataForm]) -> Vec { compute_items(extensions, 0x1c, |extension| { compute_items(&extension.fields, 0x1d, |field| { let mut bytes = compute_item(&field.var); @@ -81,7 +81,7 @@ pub fn convert_element(root: &Element) -> Result, Error> { Ok(final_string) } -pub fn hash_ecaps2(data: &Vec, algo: String) -> String { +pub fn hash_ecaps2(data: &[u8], algo: &str) -> String { match algo.as_ref() { "sha-256" => { let mut hasher = Sha256::default(); @@ -201,9 +201,9 @@ mod tests { assert_eq!(ecaps2.len(), 0x1d9); assert_eq!(ecaps2, expected); - let sha_256 = ecaps2::hash_ecaps2(&ecaps2, String::from("sha-256")); + let sha_256 = ecaps2::hash_ecaps2(&ecaps2, "sha-256"); assert_eq!(sha_256, "kzBZbkqJ3ADrj7v08reD1qcWUwNGHaidNUgD7nHpiw8="); - let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, String::from("sha3-256")); + let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, "sha3-256"); assert_eq!(sha3_256, "79mdYAfU9rEdTOcWDO7UEAt6E56SUzk/g6TnqUeuD9Q="); } @@ -372,9 +372,9 @@ mod tests { assert_eq!(ecaps2.len(), 0x543); assert_eq!(ecaps2, expected); - let sha_256 = ecaps2::hash_ecaps2(&ecaps2, String::from("sha-256")); + let sha_256 = ecaps2::hash_ecaps2(&ecaps2, "sha-256"); assert_eq!(sha_256, "u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY="); - let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, String::from("sha3-256")); + let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, "sha3-256"); assert_eq!(sha3_256, "XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg="); } } diff --git a/src/ibb.rs b/src/ibb.rs index 00d30f3f0a313ea7d1342fce32f3d94c14ed71a2..2e99506babd348553b5a77aa9611b851498abe3d 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -54,7 +54,7 @@ pub fn parse_ibb(root: &Element) -> Result { let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in open element."))?; let stanza = root.attr("stanza") .and_then(|value| value.parse().ok()) - .unwrap_or(Default::default()); + .unwrap_or_default(); for _ in root.children() { return Err(Error::ParseError("Unknown child in open element.")); } diff --git a/src/jingle.rs b/src/jingle.rs index 2b7f253f49ed02f726b219b2cae4877dacac6a94..0caba248fa4ef614a8b9c2e261c5f3e9bbff7790 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -308,14 +308,14 @@ pub fn parse_jingle(root: &Element) -> Result { } } - return Ok(Jingle { + Ok(Jingle { action: action, initiator: initiator, responder: responder, sid: sid.to_owned(), contents: contents, reason: reason_element, - }); + }) } #[cfg(test)] From 79c691e1c0b69905074efc0a018240ae49c5b635 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 01:30:29 +0100 Subject: [PATCH 028/698] ecaps2: Add a TODO about changing algo into an enum. --- src/ecaps2.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index e2e2de186127dee2f1dd4713fdd30e79adbfc4d7..5b74beaa4695219c2c5b126e95d73b61916228fa 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -81,8 +81,9 @@ pub fn convert_element(root: &Element) -> Result, Error> { Ok(final_string) } +// TODO: make algo into an enum. pub fn hash_ecaps2(data: &[u8], algo: &str) -> String { - match algo.as_ref() { + match algo { "sha-256" => { let mut hasher = Sha256::default(); hasher.input(data); From 66004180702eeca00b4c68db6ca1f025025718a4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 01:39:32 +0100 Subject: [PATCH 029/698] ecaps2: Remove function convert_element, used only in the tests. --- src/ecaps2.rs | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 5b74beaa4695219c2c5b126e95d73b61916228fa..68012d960f117c88effb9d998476e9e6d6b45d81 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -1,14 +1,9 @@ -extern crate minidom; extern crate sha2; extern crate sha3; extern crate blake2; extern crate base64; -use minidom::Element; - -use error::Error; - -use disco::{Feature, Identity, Disco, parse_disco}; +use disco::{Feature, Identity, Disco}; use data_forms::DataForm; use self::sha2::{Sha256, Sha512, Digest}; @@ -75,12 +70,6 @@ pub fn compute_disco(disco: &Disco) -> Vec { final_string } -pub fn convert_element(root: &Element) -> Result, Error> { - let disco = parse_disco(root)?; - let final_string = compute_disco(&disco); - Ok(final_string) -} - // TODO: make algo into an enum. pub fn hash_ecaps2(data: &[u8], algo: &str) -> String { match algo { @@ -131,12 +120,14 @@ pub fn hash_ecaps2(data: &[u8], algo: &str) -> String { #[cfg(test)] mod tests { use minidom::Element; + use disco; use ecaps2; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let ecaps2 = ecaps2::convert_element(&elem).unwrap(); + let disco = disco::parse_disco(&elem).unwrap(); + let ecaps2 = ecaps2::compute_disco(&disco); assert_eq!(ecaps2.len(), 54); } @@ -198,7 +189,8 @@ mod tests { 105, 109, 101, 31, 28, 99, 108, 105, 101, 110, 116, 31, 109, 111, 98, 105, 108, 101, 31, 31, 66, 111, 109, 98, 117, 115, 77, 111, 100, 31, 30, 28, 28]; - let ecaps2 = ecaps2::convert_element(&elem).unwrap(); + let disco = disco::parse_disco(&elem).unwrap(); + let ecaps2 = ecaps2::compute_disco(&disco); assert_eq!(ecaps2.len(), 0x1d9); assert_eq!(ecaps2, expected); @@ -369,7 +361,8 @@ mod tests { 111, 110, 31, 48, 46, 49, 49, 46, 49, 45, 115, 118, 110, 45, 50, 48, 49, 49, 49, 50, 49, 54, 45, 109, 111, 100, 32, 40, 84, 99, 108, 47, 84, 107, 32, 56, 46,54, 98, 50, 41, 31, 30, 29, 28]; - let ecaps2 = ecaps2::convert_element(&elem).unwrap(); + let disco = disco::parse_disco(&elem).unwrap(); + let ecaps2 = ecaps2::compute_disco(&disco); assert_eq!(ecaps2.len(), 0x543); assert_eq!(ecaps2, expected); From 458099cef07c4c32918ed69f9f15806cec5d1749 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 01:53:47 +0100 Subject: [PATCH 030/698] Add an attention parser. --- src/attention.rs | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 +++ src/ns.rs | 3 +++ 3 files changed, 50 insertions(+) create mode 100644 src/attention.rs diff --git a/src/attention.rs b/src/attention.rs new file mode 100644 index 0000000000000000000000000000000000000000..2438dc6bce92c33cf271a53bf964ed13b1973180 --- /dev/null +++ b/src/attention.rs @@ -0,0 +1,44 @@ +use minidom::Element; + +use error::Error; + +use ns; + +#[derive(Debug, Clone)] +pub enum Attention { + Attention, +} + +pub fn parse_attention(root: &Element) -> Result { + if !root.is("attention", ns::ATTENTION) { + return Err(Error::ParseError("This is not an attention element.")); + } + for _ in root.children() { + return Err(Error::ParseError("Unknown child in attention element.")); + } + Ok(Attention::Attention) +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use attention; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + attention::parse_attention(&elem).unwrap(); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = attention::parse_attention(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in attention element."); + } +} diff --git a/src/lib.rs b/src/lib.rs index dc8fcdb1a277e7283975f36c667ebdfa2ce0164d..2ba49d65fede95ed24ad8b819ae267d98f8f5a70 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,6 +42,9 @@ pub mod ping; /// XEP-0221: Data Forms Media Element pub mod media_element; +/// XEP-0224: Attention +pub mod attention; + /// XEP-0390: Entity Capabilities 2.0 pub mod ecaps2; diff --git a/src/ns.rs b/src/ns.rs index 5bcf6d1e8eab104fd7f6837755ab6e1a4d1fe67e..2b5ba3f1e12b39313450d62715aa15e0fdb1daf0 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -25,6 +25,9 @@ pub const PING: &'static str = "urn:xmpp:ping"; /// XEP-0221: Data Forms Media Element pub const MEDIA_ELEMENT: &'static str = "urn:xmpp:media-element"; +/// XEP-0224: Attention +pub const ATTENTION: &'static str = "urn:xmpp:attention:0"; + /// XEP-0390: Entity Capabilities 2.0 pub const ECAPS2: &'static str = "urn:xmpp:caps"; /// XEP-0390: Entity Capabilities 2.0 From 9410b9682a74ff33330214a3e16e1f468441c37d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 01:56:18 +0100 Subject: [PATCH 031/698] lib: Export attention as a MessagePayload. --- src/lib.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 2ba49d65fede95ed24ad8b819ae267d98f8f5a70..1e4b975f61ec3d72b7cf68804fa001c1b230b289 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,9 +54,11 @@ pub enum MessagePayload { Body(body::Body), ChatState(chatstates::ChatState), Receipt(receipts::Receipt), + Attention(attention::Attention), } -/// Parse one of the payloads of a `` element, and return `Some` of a `MessagePayload` if parsing it succeeded, `None` otherwise. +/// Parse one of the payloads of a `` element, and return `Some` of a +/// `MessagePayload` if parsing it succeeded, `None` otherwise. pub fn parse_message_payload(elem: &Element) -> Option { if let Ok(body) = body::parse_body(elem) { Some(MessagePayload::Body(body)) @@ -64,6 +66,8 @@ pub fn parse_message_payload(elem: &Element) -> Option { Some(MessagePayload::ChatState(chatstate)) } else if let Ok(receipt) = receipts::parse_receipt(elem) { Some(MessagePayload::Receipt(receipt)) + } else if let Ok(attention) = attention::parse_attention(elem) { + Some(MessagePayload::Attention(attention)) } else { None } From 8fba5cf9669443bde351244c64f9dc6581bc035d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 02:07:44 +0100 Subject: [PATCH 032/698] Add a Last Message Correction parser. --- src/lib.rs | 6 ++++++ src/message_correct.rs | 45 ++++++++++++++++++++++++++++++++++++++++++ src/ns.rs | 3 +++ 3 files changed, 54 insertions(+) create mode 100644 src/message_correct.rs diff --git a/src/lib.rs b/src/lib.rs index 1e4b975f61ec3d72b7cf68804fa001c1b230b289..c9315f6e5092d62d02de55f631cfde6796b43757 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,6 +45,9 @@ pub mod media_element; /// XEP-0224: Attention pub mod attention; +/// XEP-0308: Last Message Correction +pub mod message_correct; + /// XEP-0390: Entity Capabilities 2.0 pub mod ecaps2; @@ -55,6 +58,7 @@ pub enum MessagePayload { ChatState(chatstates::ChatState), Receipt(receipts::Receipt), Attention(attention::Attention), + MessageCorrect(message_correct::MessageCorrect), } /// Parse one of the payloads of a `` element, and return `Some` of a @@ -68,6 +72,8 @@ pub fn parse_message_payload(elem: &Element) -> Option { Some(MessagePayload::Receipt(receipt)) } else if let Ok(attention) = attention::parse_attention(elem) { Some(MessagePayload::Attention(attention)) + } else if let Ok(replace) = message_correct::parse_message_correct(elem) { + Some(MessagePayload::MessageCorrect(replace)) } else { None } diff --git a/src/message_correct.rs b/src/message_correct.rs new file mode 100644 index 0000000000000000000000000000000000000000..789febb96d3ae634834e0804c4201358753ea2f7 --- /dev/null +++ b/src/message_correct.rs @@ -0,0 +1,45 @@ +use minidom::Element; + +use error::Error; + +use ns; + +#[derive(Debug, Clone)] +pub enum MessageCorrect { + Replace(String), +} + +pub fn parse_message_correct(root: &Element) -> Result { + if !root.is("replace", ns::MESSAGE_CORRECT) { + return Err(Error::ParseError("This is not a replace element.")); + } + for _ in root.children() { + return Err(Error::ParseError("Unknown child in replace element.")); + } + let id = root.attr("id").unwrap_or("").to_owned(); + Ok(MessageCorrect::Replace(id)) +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use message_correct; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + message_correct::parse_message_correct(&elem).unwrap(); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = message_correct::parse_message_correct(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in replace element."); + } +} diff --git a/src/ns.rs b/src/ns.rs index 2b5ba3f1e12b39313450d62715aa15e0fdb1daf0..df708fbea2f0805876464ea8197e104ed5dad978 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -28,6 +28,9 @@ pub const MEDIA_ELEMENT: &'static str = "urn:xmpp:media-element"; /// XEP-0224: Attention pub const ATTENTION: &'static str = "urn:xmpp:attention:0"; +/// XEP-0308: Last Message Correction +pub const MESSAGE_CORRECT: &'static str = "urn:xmpp:message-correct:0"; + /// XEP-0390: Entity Capabilities 2.0 pub const ECAPS2: &'static str = "urn:xmpp:caps"; /// XEP-0390: Entity Capabilities 2.0 From 6ba22a43e5e11f4372569a4456c1f17bd418ebac Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 02:46:41 +0100 Subject: [PATCH 033/698] lib: Import base64 at the top-level, and not in a module. --- src/ecaps2.rs | 2 +- src/error.rs | 10 +++++++++- src/lib.rs | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 68012d960f117c88effb9d998476e9e6d6b45d81..fce5e6ea8ccabfc869e497d69f26bb9e13a74a48 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -1,7 +1,6 @@ extern crate sha2; extern crate sha3; extern crate blake2; -extern crate base64; use disco::{Feature, Identity, Disco}; use data_forms::DataForm; @@ -9,6 +8,7 @@ use data_forms::DataForm; use self::sha2::{Sha256, Sha512, Digest}; use self::sha3::{Sha3_256, Sha3_512}; use self::blake2::Blake2b; +use base64; fn compute_item(field: &str) -> Vec { let mut bytes = field.as_bytes().to_vec(); diff --git a/src/error.rs b/src/error.rs index bd7d2d9cb78de8139f056e49809df24309f4e63c..c55a52a1d153b045c0d7c31e92ff1ad72a29a462 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,13 +1,15 @@ use std::convert::From; use std::io; +use base64; use minidom; #[derive(Debug)] pub enum Error { + ParseError(&'static str), IoError(io::Error), XMLError(minidom::Error), - ParseError(&'static str), + Base64Error(base64::Base64Error), } impl From for Error { @@ -21,3 +23,9 @@ impl From for Error { Error::XMLError(err) } } + +impl From for Error { + fn from(err: base64::Base64Error) -> Error { + Error::Base64Error(err) + } +} diff --git a/src/lib.rs b/src/lib.rs index c9315f6e5092d62d02de55f631cfde6796b43757..50679604da619fc2f1a8a3c72aa58711e5f270da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ //! before being sent over the wire. extern crate minidom; +extern crate base64; use minidom::Element; /// Error type returned by every parser on failure. From fb373c2b662ea76d3bb4d080748b45e5746a936a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 03:07:21 +0100 Subject: [PATCH 034/698] ibb: Add a parser for the element. --- src/ibb.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/ibb.rs b/src/ibb.rs index 2e99506babd348553b5a77aa9611b851498abe3d..6811e68c9418da58d487faba9d221f25a033723d 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -1,6 +1,7 @@ use std::str::FromStr; use minidom::Element; +use base64; use error::Error; @@ -35,7 +36,7 @@ impl FromStr for Stanza { #[derive(Debug, Clone)] pub enum IBB { Open { block_size: u16, sid: String, stanza: Stanza }, - Data(u16, String, Vec), + Data { seq: u16, sid: String, data: Vec }, Close(String), } @@ -63,6 +64,18 @@ pub fn parse_ibb(root: &Element) -> Result { sid: sid, stanza: stanza }) + } else if root.is("data", ns::IBB) { + let seq = required_attr(root, "seq", Error::ParseError("Required attribute 'seq' missing in data element."))?; + let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in data element."))?; + let data = base64::decode(&root.text())?; + for _ in root.children() { + return Err(Error::ParseError("Unknown child in data element.")); + } + Ok(IBB::Data { + seq: seq, + sid: sid, + data: data + }) } else { Err(Error::ParseError("This is not an ibb element.")) } @@ -76,7 +89,10 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); + ibb::parse_ibb(&elem).unwrap(); + + let elem: Element = "AAAA".parse().unwrap(); ibb::parse_ibb(&elem).unwrap(); } From 189f17c5696cf26707efedaff86701fc3c3d3161 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 03:09:10 +0100 Subject: [PATCH 035/698] ibb: Add a parser for the element. --- src/ibb.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/ibb.rs b/src/ibb.rs index 6811e68c9418da58d487faba9d221f25a033723d..f77541c8f98ee97775bf340f4af08e5273d045e6 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -37,7 +37,7 @@ impl FromStr for Stanza { pub enum IBB { Open { block_size: u16, sid: String, stanza: Stanza }, Data { seq: u16, sid: String, data: Vec }, - Close(String), + Close { sid: String }, } fn optional_attr(root: &Element, attr: &str) -> Option { @@ -76,6 +76,14 @@ pub fn parse_ibb(root: &Element) -> Result { sid: sid, data: data }) + } else if root.is("close", ns::IBB) { + let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in data element."))?; + for _ in root.children() { + return Err(Error::ParseError("Unknown child in close element.")); + } + Ok(IBB::Close { + sid: sid, + }) } else { Err(Error::ParseError("This is not an ibb element.")) } @@ -94,6 +102,9 @@ mod tests { let elem: Element = "AAAA".parse().unwrap(); ibb::parse_ibb(&elem).unwrap(); + + let elem: Element = "".parse().unwrap(); + ibb::parse_ibb(&elem).unwrap(); } #[test] From 94380fdbd5a0a78c4b1abb8f43f18ebaf09c8b10 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 03:21:06 +0100 Subject: [PATCH 036/698] ibb: Add some more tests. --- src/ibb.rs | 46 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/src/ibb.rs b/src/ibb.rs index f77541c8f98ee97775bf340f4af08e5273d045e6..89aad820d808bf04861cafa85c2276094ac072e1 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -7,7 +7,7 @@ use error::Error; use ns; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum Stanza { Iq, Message, @@ -35,9 +35,19 @@ impl FromStr for Stanza { #[derive(Debug, Clone)] pub enum IBB { - Open { block_size: u16, sid: String, stanza: Stanza }, - Data { seq: u16, sid: String, data: Vec }, - Close { sid: String }, + Open { + block_size: u16, + sid: String, + stanza: Stanza, + }, + Data { + seq: u16, + sid: String, + data: Vec, + }, + Close { + sid: String, + }, } fn optional_attr(root: &Element, attr: &str) -> Option { @@ -98,13 +108,35 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - ibb::parse_ibb(&elem).unwrap(); + let open = ibb::parse_ibb(&elem).unwrap(); + match open { + ibb::IBB::Open { block_size, sid, stanza } => { + assert_eq!(block_size, 3); + assert_eq!(sid, "coucou"); + assert_eq!(stanza, ibb::Stanza::Iq); + }, + _ => panic!(), + } let elem: Element = "AAAA".parse().unwrap(); - ibb::parse_ibb(&elem).unwrap(); + let data = ibb::parse_ibb(&elem).unwrap(); + match data { + ibb::IBB::Data { seq, sid, data } => { + assert_eq!(seq, 0); + assert_eq!(sid, "coucou"); + assert_eq!(data, vec!(0, 0, 0)); + }, + _ => panic!(), + } let elem: Element = "".parse().unwrap(); - ibb::parse_ibb(&elem).unwrap(); + let close = ibb::parse_ibb(&elem).unwrap(); + match close { + ibb::IBB::Close { sid } => { + assert_eq!(sid, "coucou"); + }, + _ => panic!(), + } } #[test] From a680ab194ced4f7fd044ed45f42062581e4f26ca Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 03:45:05 +0100 Subject: [PATCH 037/698] Add an Explicit Message Encryption parser. --- src/eme.rs | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 6 +++++ src/ns.rs | 3 +++ 3 files changed, 77 insertions(+) create mode 100644 src/eme.rs diff --git a/src/eme.rs b/src/eme.rs new file mode 100644 index 0000000000000000000000000000000000000000..cf33de7fbbee83b7be32680b1f573494e5b1f8a8 --- /dev/null +++ b/src/eme.rs @@ -0,0 +1,68 @@ +use minidom::Element; + +use error::Error; + +use ns; + +#[derive(Debug, Clone)] +pub struct ExplicitMessageEncryption { + pub namespace: String, + pub name: Option, +} + +pub fn parse_explicit_message_encryption(root: &Element) -> Result { + if !root.is("encryption", ns::EME) { + return Err(Error::ParseError("This is not an encryption element.")); + } + for _ in root.children() { + return Err(Error::ParseError("Unknown child in encryption element.")); + } + let namespace = root.attr("namespace").ok_or(Error::ParseError("Mandatory argument 'namespace' not present in encryption element."))?.to_owned(); + let name = root.attr("name").and_then(|value| value.parse().ok()); + Ok(ExplicitMessageEncryption { + namespace: namespace, + name: name, + }) +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use eme; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let encryption = eme::parse_explicit_message_encryption(&elem).unwrap(); + assert_eq!(encryption.namespace, "urn:xmpp:otr:0"); + assert_eq!(encryption.name, None); + + let elem: Element = "".parse().unwrap(); + let encryption = eme::parse_explicit_message_encryption(&elem).unwrap(); + assert_eq!(encryption.namespace, "some.unknown.mechanism"); + assert_eq!(encryption.name, Some(String::from("SuperMechanism"))); + } + + #[test] + fn test_unknown() { + let elem: Element = "".parse().unwrap(); + let error = eme::parse_explicit_message_encryption(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "This is not an encryption element."); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = eme::parse_explicit_message_encryption(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in encryption element."); + } +} diff --git a/src/lib.rs b/src/lib.rs index 50679604da619fc2f1a8a3c72aa58711e5f270da..167f58ec3d55be4b74b85f54af49e219ecd54f71 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,6 +49,9 @@ pub mod attention; /// XEP-0308: Last Message Correction pub mod message_correct; +/// XEP-0380: Explicit Message Encryption +pub mod eme; + /// XEP-0390: Entity Capabilities 2.0 pub mod ecaps2; @@ -60,6 +63,7 @@ pub enum MessagePayload { Receipt(receipts::Receipt), Attention(attention::Attention), MessageCorrect(message_correct::MessageCorrect), + ExplicitMessageEncryption(eme::ExplicitMessageEncryption), } /// Parse one of the payloads of a `` element, and return `Some` of a @@ -75,6 +79,8 @@ pub fn parse_message_payload(elem: &Element) -> Option { Some(MessagePayload::Attention(attention)) } else if let Ok(replace) = message_correct::parse_message_correct(elem) { Some(MessagePayload::MessageCorrect(replace)) + } else if let Ok(eme) = eme::parse_explicit_message_encryption(elem) { + Some(MessagePayload::ExplicitMessageEncryption(eme)) } else { None } diff --git a/src/ns.rs b/src/ns.rs index df708fbea2f0805876464ea8197e104ed5dad978..6aa1d3f7d682f832faf7a177aa7c186f4723ba52 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -31,6 +31,9 @@ pub const ATTENTION: &'static str = "urn:xmpp:attention:0"; /// XEP-0308: Last Message Correction pub const MESSAGE_CORRECT: &'static str = "urn:xmpp:message-correct:0"; +/// XEP-0380: Explicit Message Encryption +pub const EME: &'static str = "urn:xmpp:eme:0"; + /// XEP-0390: Entity Capabilities 2.0 pub const ECAPS2: &'static str = "urn:xmpp:caps"; /// XEP-0390: Entity Capabilities 2.0 From cfadff3bc9f13c9e007a35088f70ff3cdf08f5b0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 03:57:34 +0100 Subject: [PATCH 038/698] Add a Delayed Delivery parser. --- src/delay.rs | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 6 +++++ src/ns.rs | 3 +++ 3 files changed, 75 insertions(+) create mode 100644 src/delay.rs diff --git a/src/delay.rs b/src/delay.rs new file mode 100644 index 0000000000000000000000000000000000000000..f4f59356786f320128d24c996bf8dab3d0385646 --- /dev/null +++ b/src/delay.rs @@ -0,0 +1,66 @@ +use minidom::Element; + +use error::Error; + +use ns; + +#[derive(Debug, Clone)] +pub struct Delay { + pub from: Option, + pub stamp: String, + pub data: Option, +} + +pub fn parse_delay(root: &Element) -> Result { + if !root.is("delay", ns::DELAY) { + return Err(Error::ParseError("This is not a delay element.")); + } + for _ in root.children() { + return Err(Error::ParseError("Unknown child in delay element.")); + } + let from = root.attr("from").and_then(|value| value.parse().ok()); + let stamp = root.attr("stamp").ok_or(Error::ParseError("Mandatory argument 'stamp' not present in delay element."))?.to_owned(); + Ok(Delay { + from: from, + stamp: stamp, + data: None, + }) +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use delay; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let delay = delay::parse_delay(&elem).unwrap(); + assert_eq!(delay.from, Some(String::from("capulet.com"))); + assert_eq!(delay.stamp, "2002-09-10T23:08:25Z"); + assert_eq!(delay.data, None); + } + + #[test] + fn test_unknown() { + let elem: Element = "".parse().unwrap(); + let error = delay::parse_delay(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "This is not a delay element."); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = delay::parse_delay(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in delay element."); + } +} diff --git a/src/lib.rs b/src/lib.rs index 167f58ec3d55be4b74b85f54af49e219ecd54f71..e00ccb525ecb571b31ee408047dcab12a66d91ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,6 +40,9 @@ pub mod receipts; /// XEP-0199: XMPP Ping pub mod ping; +/// XEP-0203: Delayed Delivery +pub mod delay; + /// XEP-0221: Data Forms Media Element pub mod media_element; @@ -61,6 +64,7 @@ pub enum MessagePayload { Body(body::Body), ChatState(chatstates::ChatState), Receipt(receipts::Receipt), + Delay(delay::Delay), Attention(attention::Attention), MessageCorrect(message_correct::MessageCorrect), ExplicitMessageEncryption(eme::ExplicitMessageEncryption), @@ -75,6 +79,8 @@ pub fn parse_message_payload(elem: &Element) -> Option { Some(MessagePayload::ChatState(chatstate)) } else if let Ok(receipt) = receipts::parse_receipt(elem) { Some(MessagePayload::Receipt(receipt)) + } else if let Ok(delay) = delay::parse_delay(elem) { + Some(MessagePayload::Delay(delay)) } else if let Ok(attention) = attention::parse_attention(elem) { Some(MessagePayload::Attention(attention)) } else if let Ok(replace) = message_correct::parse_message_correct(elem) { diff --git a/src/ns.rs b/src/ns.rs index 6aa1d3f7d682f832faf7a177aa7c186f4723ba52..8aefa2eeea0cfa6d95a2c1df5adb5953173d4b18 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -22,6 +22,9 @@ pub const RECEIPTS: &'static str = "urn:xmpp:receipts"; /// XEP-0199: XMPP Ping pub const PING: &'static str = "urn:xmpp:ping"; +/// XEP-0203: Delayed Delivery +pub const DELAY: &'static str = "urn:xmpp:delay"; + /// XEP-0221: Data Forms Media Element pub const MEDIA_ELEMENT: &'static str = "urn:xmpp:media-element"; From d39d13b7cb936ec82170e52ba48cb88b7138fb43 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 04:01:14 +0100 Subject: [PATCH 039/698] delay: Correctly parse the content data. --- src/delay.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/delay.rs b/src/delay.rs index f4f59356786f320128d24c996bf8dab3d0385646..b54dd04796399dd0ed208dbdbb906e702d5942d5 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -20,10 +20,14 @@ pub fn parse_delay(root: &Element) -> Result { } let from = root.attr("from").and_then(|value| value.parse().ok()); let stamp = root.attr("stamp").ok_or(Error::ParseError("Mandatory argument 'stamp' not present in delay element."))?.to_owned(); + let data = match root.text().as_ref() { + "" => None, + text => Some(text.to_owned()), + }; Ok(Delay { from: from, stamp: stamp, - data: None, + data: data, }) } From ea2ff8a35db030f57c332be44065cfbffcae1adb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 04:21:16 +0100 Subject: [PATCH 040/698] Add a hash parser. --- src/hashes.rs | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 +++ src/ns.rs | 3 +++ 3 files changed, 72 insertions(+) create mode 100644 src/hashes.rs diff --git a/src/hashes.rs b/src/hashes.rs new file mode 100644 index 0000000000000000000000000000000000000000..21301633ce89458edb97635fcc1e017c522b9f66 --- /dev/null +++ b/src/hashes.rs @@ -0,0 +1,66 @@ +use minidom::Element; + +use error::Error; + +use ns; + +#[derive(Debug, Clone)] +pub struct Hash { + pub algo: String, + pub hash: String, +} + +pub fn parse_hash(root: &Element) -> Result { + if !root.is("hash", ns::HASHES) { + return Err(Error::ParseError("This is not a hash element.")); + } + for _ in root.children() { + return Err(Error::ParseError("Unknown child in hash element.")); + } + let algo = root.attr("algo").ok_or(Error::ParseError("Mandatory argument 'algo' not present in hash element."))?.to_owned(); + let hash = match root.text().as_ref() { + "" => return Err(Error::ParseError("Hash element shouldn’t be empty.")), + text => text.to_owned(), + }; + Ok(Hash { + algo: algo, + hash: hash, + }) +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use hashes; + + #[test] + fn test_simple() { + let elem: Element = "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=".parse().unwrap(); + let hash = hashes::parse_hash(&elem).unwrap(); + assert_eq!(hash.algo, "sha-256"); + assert_eq!(hash.hash, "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU="); + } + + #[test] + fn test_unknown() { + let elem: Element = "".parse().unwrap(); + let error = hashes::parse_hash(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "This is not a hash element."); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = hashes::parse_hash(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in hash element."); + } +} diff --git a/src/lib.rs b/src/lib.rs index e00ccb525ecb571b31ee408047dcab12a66d91ea..d26a5c9a6052c1061493dd3c91d2d1ccaaff8289 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,6 +49,9 @@ pub mod media_element; /// XEP-0224: Attention pub mod attention; +/// XEP-0300: Use of Cryptographic Hash Functions in XMPP +pub mod hashes; + /// XEP-0308: Last Message Correction pub mod message_correct; diff --git a/src/ns.rs b/src/ns.rs index 8aefa2eeea0cfa6d95a2c1df5adb5953173d4b18..d3d21068d638fec5c0cc480eb9efb1c8402fe4c0 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -31,6 +31,9 @@ pub const MEDIA_ELEMENT: &'static str = "urn:xmpp:media-element"; /// XEP-0224: Attention pub const ATTENTION: &'static str = "urn:xmpp:attention:0"; +/// XEP-0300: Use of Cryptographic Hash Functions in XMPP +pub const HASHES: &'static str = "urn:xmpp:hashes:2"; + /// XEP-0308: Last Message Correction pub const MESSAGE_CORRECT: &'static str = "urn:xmpp:message-correct:0"; From e1070a8b98b31c57e72ea6748d644f030c7aadef Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 22 Apr 2017 16:51:10 +0100 Subject: [PATCH 041/698] ecaps2: Add a parser too. --- src/ecaps2.rs | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index fce5e6ea8ccabfc869e497d69f26bb9e13a74a48..fabd2e51f6878e50bd10f9891b82750ce9a9d23b 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -4,12 +4,40 @@ extern crate blake2; use disco::{Feature, Identity, Disco}; use data_forms::DataForm; +use hashes::{Hash, parse_hash}; + +use minidom::Element; +use error::Error; +use ns; use self::sha2::{Sha256, Sha512, Digest}; use self::sha3::{Sha3_256, Sha3_512}; use self::blake2::Blake2b; use base64; +#[derive(Debug, Clone)] +pub struct ECaps2 { + hashes: Vec, +} + +pub fn parse_ecaps2(root: &Element) -> Result { + if !root.is("c", ns::ECAPS2) { + return Err(Error::ParseError("This is not an ecaps2 element.")); + } + let mut hashes = vec!(); + for child in root.children() { + if child.is("hash", ns::HASHES) { + let hash = parse_hash(child)?; + hashes.push(hash); + } else { + return Err(Error::ParseError("Unknown child in ecaps2 element.")); + } + } + Ok(ECaps2 { + hashes: hashes, + }) +} + fn compute_item(field: &str) -> Vec { let mut bytes = field.as_bytes().to_vec(); bytes.push(0x1f); @@ -120,9 +148,32 @@ pub fn hash_ecaps2(data: &[u8], algo: &str) -> String { #[cfg(test)] mod tests { use minidom::Element; + use error::Error; use disco; use ecaps2; + #[test] + fn test_parse() { + let elem: Element = "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=".parse().unwrap(); + let ecaps2 = ecaps2::parse_ecaps2(&elem).unwrap(); + assert_eq!(ecaps2.hashes.len(), 2); + assert_eq!(ecaps2.hashes[0].algo, "sha-256"); + assert_eq!(ecaps2.hashes[0].hash, "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4="); + assert_eq!(ecaps2.hashes[1].algo, "sha3-256"); + assert_eq!(ecaps2.hashes[1].hash, "+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8="); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=".parse().unwrap(); + let error = ecaps2::parse_ecaps2(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in ecaps2 element."); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); From 0778ceea7d42dd85e569c0863d3a729b93a9be6c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 22 Apr 2017 17:38:19 +0100 Subject: [PATCH 042/698] jingle: Remove extraneous println!(). --- src/jingle.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/jingle.rs b/src/jingle.rs index 0caba248fa4ef614a8b9c2e261c5f3e9bbff7790..80ad7b0617bcd4eca807d6c9e4ed10f1046ff993 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -375,7 +375,6 @@ mod tests { assert_eq!(jingle.contents[0].name, "coucou"); assert_eq!(jingle.contents[0].senders, jingle::Senders::Both); assert_eq!(jingle.contents[0].disposition, "session"); - println!("{:#?}", jingle); let elem: Element = "".parse().unwrap(); let jingle = jingle::parse_jingle(&elem).unwrap(); From edc5961a7969b1510998608f1d927a3c7f425c94 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 22 Apr 2017 17:38:36 +0100 Subject: [PATCH 043/698] hashes: Implement PartialEq on Hash. --- src/hashes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hashes.rs b/src/hashes.rs index 21301633ce89458edb97635fcc1e017c522b9f66..f9e1e846c1a4c4f8b1cc2f5edf8efdba427e38a1 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -4,7 +4,7 @@ use error::Error; use ns; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Hash { pub algo: String, pub hash: String, From 000e5a50c29d365d1defa5fc765fd1f97007a88a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 22 Apr 2017 17:39:00 +0100 Subject: [PATCH 044/698] error: Add ParseIntError to the possible errors. --- src/error.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/error.rs b/src/error.rs index c55a52a1d153b045c0d7c31e92ff1ad72a29a462..e9506bae476d052f24a9c2e4a6b34e2a60b91cbf 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,5 +1,6 @@ use std::convert::From; use std::io; +use std::num; use base64; use minidom; @@ -10,6 +11,7 @@ pub enum Error { IoError(io::Error), XMLError(minidom::Error), Base64Error(base64::Base64Error), + ParseIntError(num::ParseIntError), } impl From for Error { @@ -29,3 +31,9 @@ impl From for Error { Error::Base64Error(err) } } + +impl From for Error { + fn from(err: num::ParseIntError) -> Error { + Error::ParseIntError(err) + } +} From fbb609485b04ddc9e9738c0142a06c9ec190ff09 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 22 Apr 2017 17:39:21 +0100 Subject: [PATCH 045/698] Add a JingleFT parser. --- src/jingle_ft.rs | 175 +++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 + src/ns.rs | 5 ++ 3 files changed, 183 insertions(+) create mode 100644 src/jingle_ft.rs diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs new file mode 100644 index 0000000000000000000000000000000000000000..6e3e2803ba27ba6bb345adda6124b7757d1df27f --- /dev/null +++ b/src/jingle_ft.rs @@ -0,0 +1,175 @@ +extern crate minidom; + +use hashes::{Hash, parse_hash}; + +use minidom::Element; + +use error::Error; +use ns; + +#[derive(Debug, Clone, PartialEq)] +pub struct Range { + pub offset: u64, + pub length: Option, + pub hashes: Vec, +} + +#[derive(Debug, Clone)] +pub struct File { + pub date: Option, + pub media_type: Option, + pub name: Option, + pub size: Option, + pub range: Option, + pub hashes: Vec, +} + +#[derive(Debug, Clone)] +pub struct Description { + pub file: File, +} + +#[derive(Debug, Clone)] +pub struct Creator { + pub creator: String, +} + +#[derive(Debug, Clone)] +pub struct Checksum { + pub name: String, + pub creator: Creator, + pub file: File, +} + +pub fn parse_jingle_ft(root: &Element) -> Result { + if !root.is("description", ns::JINGLE_FT) { + return Err(Error::ParseError("This is not a JingleFT description element.")); + } + if root.children().collect::>().len() != 1 { + return Err(Error::ParseError("JingleFT description element must have exactly one child.")); + } + + let mut date = None; + let mut media_type = None; + let mut name = None; + let mut size = None; + let mut range = None; + let mut hashes = vec!(); + for description_payload in root.children() { + if !description_payload.is("file", ns::JINGLE_FT) { + return Err(Error::ParseError("Unknown element in JingleFT description.")); + } + for file_payload in description_payload.children() { + if file_payload.is("date", ns::JINGLE_FT) { + if date.is_some() { + return Err(Error::ParseError("File must not have more than one date.")); + } + date = Some(file_payload.text()); + } else if file_payload.is("media-type", ns::JINGLE_FT) { + if media_type.is_some() { + return Err(Error::ParseError("File must not have more than one media-type.")); + } + media_type = Some(file_payload.text()); + } else if file_payload.is("name", ns::JINGLE_FT) { + if name.is_some() { + return Err(Error::ParseError("File must not have more than one name.")); + } + name = Some(file_payload.text()); + } else if file_payload.is("size", ns::JINGLE_FT) { + if size.is_some() { + return Err(Error::ParseError("File must not have more than one size.")); + } + size = Some(file_payload.text()); + } else if file_payload.is("range", ns::JINGLE_FT) { + if range.is_some() { + return Err(Error::ParseError("File must not have more than one range.")); + } + let offset = file_payload.attr("offset").unwrap_or("0").parse()?; + let length = match file_payload.attr("length") { + Some(length) => Some(length.parse()?), + None => None, + }; + let mut range_hashes = vec!(); + for hash_element in file_payload.children() { + if !hash_element.is("hash", ns::HASHES) { + return Err(Error::ParseError("Unknown element in JingleFT range.")); + } + range_hashes.push(parse_hash(hash_element)?); + } + range = Some(Range { + offset: offset, + length: length, + hashes: range_hashes, + }); + } else if file_payload.is("hash", ns::HASHES) { + hashes.push(parse_hash(file_payload)?); + } else { + return Err(Error::ParseError("Unknown element in JingleFT file.")); + } + } + } + + Ok(Description { + file: File { + date: date, + media_type: media_type, + name: name, + size: size, + range: range, + hashes: hashes, + }, + }) +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use jingle_ft; + + #[test] + fn test_description() { + let elem: Element = r#" + + + text/plain + test.txt + 2015-07-26T21:46:00 + 6144 + w0mcJylzCn+AfvuGdqkty2+KP48= + + +"#.parse().unwrap(); + + let desc = jingle_ft::parse_jingle_ft(&elem).unwrap(); + assert_eq!(desc.file.media_type, Some(String::from("text/plain"))); + assert_eq!(desc.file.name, Some(String::from("test.txt"))); + assert_eq!(desc.file.date, Some(String::from("2015-07-26T21:46:00"))); + assert_eq!(desc.file.size, Some(String::from("6144"))); + assert_eq!(desc.file.range, None); + assert_eq!(desc.file.hashes[0].algo, "sha-1"); + assert_eq!(desc.file.hashes[0].hash, "w0mcJylzCn+AfvuGdqkty2+KP48="); + } + + #[test] + fn test_request() { + let elem: Element = r#" + + + w0mcJylzCn+AfvuGdqkty2+KP48= + + +"#.parse().unwrap(); + + let desc = jingle_ft::parse_jingle_ft(&elem).unwrap(); + assert_eq!(desc.file.media_type, None); + assert_eq!(desc.file.name, None); + assert_eq!(desc.file.date, None); + assert_eq!(desc.file.size, None); + assert_eq!(desc.file.range, None); + assert_eq!(desc.file.hashes[0].algo, "sha-1"); + assert_eq!(desc.file.hashes[0].hash, "w0mcJylzCn+AfvuGdqkty2+KP48="); + } +} diff --git a/src/lib.rs b/src/lib.rs index d26a5c9a6052c1061493dd3c91d2d1ccaaff8289..614737dd937c195c8f03ff9b3aedb40a384402b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,6 +49,9 @@ pub mod media_element; /// XEP-0224: Attention pub mod attention; +/// XEP-0234: Jingle File Transfer +pub mod jingle_ft; + /// XEP-0300: Use of Cryptographic Hash Functions in XMPP pub mod hashes; diff --git a/src/ns.rs b/src/ns.rs index d3d21068d638fec5c0cc480eb9efb1c8402fe4c0..5dfb3f5d18e77eddbf9dc99e824aedd35d6b1ff5 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -31,6 +31,11 @@ pub const MEDIA_ELEMENT: &'static str = "urn:xmpp:media-element"; /// XEP-0224: Attention pub const ATTENTION: &'static str = "urn:xmpp:attention:0"; +/// XEP-0234: Jingle File Transfer +pub const JINGLE_FT: &'static str = "urn:xmpp:jingle:apps:file-transfer:5"; +/// XEP-0234: Jingle File Transfer +pub const JINGLE_FT_ERROR: &'static str = "urn:xmpp:jingle:apps:file-transfer:errors:0"; + /// XEP-0300: Use of Cryptographic Hash Functions in XMPP pub const HASHES: &'static str = "urn:xmpp:hashes:2"; From 22ec4b19137233cc48be376b9b6800a30d1dcd9f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 22 Apr 2017 19:15:29 +0100 Subject: [PATCH 046/698] ibb: Improve handling of optional stanza attribute. --- src/ibb.rs | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/ibb.rs b/src/ibb.rs index 89aad820d808bf04861cafa85c2276094ac072e1..a8705cd11d9046ce07474bc31a2b929e8d760a02 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -28,7 +28,7 @@ impl FromStr for Stanza { } else if s == "message" { Ok(Stanza::Message) } else { - Err(Error::ParseError("Unknown 'stanza' attribute.")) + Err(Error::ParseError("Invalid 'stanza' attribute.")) } } } @@ -50,37 +50,35 @@ pub enum IBB { }, } -fn optional_attr(root: &Element, attr: &str) -> Option { +fn required_attr(root: &Element, attr: &str, err: Error) -> Result { root.attr(attr) .and_then(|value| value.parse().ok()) -} - -fn required_attr(root: &Element, attr: &str, err: Error) -> Result { - optional_attr(root, attr).ok_or(err) + .ok_or(err) } pub fn parse_ibb(root: &Element) -> Result { if root.is("open", ns::IBB) { - let block_size = required_attr(root, "block-size", Error::ParseError("Required attribute 'block-size' missing in open element."))?; - let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in open element."))?; - let stanza = root.attr("stanza") - .and_then(|value| value.parse().ok()) - .unwrap_or_default(); for _ in root.children() { return Err(Error::ParseError("Unknown child in open element.")); } + let block_size = required_attr(root, "block-size", Error::ParseError("Required attribute 'block-size' missing in open element."))?; + let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in open element."))?; + let stanza = match root.attr("stanza") { + Some(stanza) => stanza.parse()?, + None => Default::default(), + }; Ok(IBB::Open { block_size: block_size, sid: sid, stanza: stanza }) } else if root.is("data", ns::IBB) { - let seq = required_attr(root, "seq", Error::ParseError("Required attribute 'seq' missing in data element."))?; - let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in data element."))?; - let data = base64::decode(&root.text())?; for _ in root.children() { return Err(Error::ParseError("Unknown child in data element.")); } + let seq = required_attr(root, "seq", Error::ParseError("Required attribute 'seq' missing in data element."))?; + let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in data element."))?; + let data = base64::decode(&root.text())?; Ok(IBB::Data { seq: seq, sid: sid, @@ -168,7 +166,6 @@ mod tests { } #[test] - #[ignore] fn test_invalid_stanza() { let elem: Element = "".parse().unwrap(); let error = ibb::parse_ibb(&elem).unwrap_err(); @@ -176,6 +173,6 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Wrong value for 'stanza' attribute in open."); + assert_eq!(message, "Invalid 'stanza' attribute."); } } From ff5be32a0e5f6675e8abdbf481d140f7c59cf929 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 22 Apr 2017 19:15:48 +0100 Subject: [PATCH 047/698] Add a JingleIBB parser. --- src/jingle_ibb.rs | 101 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 ++ src/ns.rs | 3 ++ 3 files changed, 107 insertions(+) create mode 100644 src/jingle_ibb.rs diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs new file mode 100644 index 0000000000000000000000000000000000000000..4ab73e684bdeee9243e501f697e3fbd4b7d90348 --- /dev/null +++ b/src/jingle_ibb.rs @@ -0,0 +1,101 @@ +use std::str::FromStr; + +use minidom::Element; + +use error::Error; + +use ns; + +use ibb::Stanza; + +#[derive(Debug, Clone)] +pub struct Transport { + block_size: u16, + sid: String, + stanza: Stanza, +} + +fn optional_attr(root: &Element, attr: &str) -> Option { + root.attr(attr) + .and_then(|value| value.parse().ok()) +} + +fn required_attr(root: &Element, attr: &str, err: Error) -> Result { + optional_attr(root, attr).ok_or(err) +} + +pub fn parse_jingle_ibb(root: &Element) -> Result { + if root.is("transport", ns::JINGLE_IBB) { + for _ in root.children() { + return Err(Error::ParseError("Unknown child in JingleIBB element.")); + } + let block_size = required_attr(root, "block-size", Error::ParseError("Required attribute 'block-size' missing in JingleIBB element."))?; + let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in JingleIBB element."))?; + let stanza = root.attr("stanza") + .unwrap_or("iq") + .parse()?; + Ok(Transport { + block_size: block_size, + sid: sid, + stanza: stanza + }) + } else { + Err(Error::ParseError("This is not an JingleIBB element.")) + } +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use ibb; + use jingle_ibb; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let transport = jingle_ibb::parse_jingle_ibb(&elem).unwrap(); + assert_eq!(transport.block_size, 3); + assert_eq!(transport.sid, "coucou"); + assert_eq!(transport.stanza, ibb::Stanza::Iq); + } + + #[test] + fn test_invalid() { + let elem: Element = "".parse().unwrap(); + let error = jingle_ibb::parse_jingle_ibb(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'block-size' missing in JingleIBB element."); + + // TODO: maybe make a better error message here. + let elem: Element = "".parse().unwrap(); + let error = jingle_ibb::parse_jingle_ibb(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'block-size' missing in JingleIBB element."); + + let elem: Element = "".parse().unwrap(); + let error = jingle_ibb::parse_jingle_ibb(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'sid' missing in JingleIBB element."); + } + + #[test] + fn test_invalid_stanza() { + let elem: Element = "".parse().unwrap(); + let error = jingle_ibb::parse_jingle_ibb(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Invalid 'stanza' attribute."); + } +} diff --git a/src/lib.rs b/src/lib.rs index 614737dd937c195c8f03ff9b3aedb40a384402b0..b311682b8c77afba18c7f4602ce1e7ecc30b3aa2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,6 +52,9 @@ pub mod attention; /// XEP-0234: Jingle File Transfer pub mod jingle_ft; +/// XEP-0261: Jingle In-Band Bytestreams Transport Method +pub mod jingle_ibb; + /// XEP-0300: Use of Cryptographic Hash Functions in XMPP pub mod hashes; diff --git a/src/ns.rs b/src/ns.rs index 5dfb3f5d18e77eddbf9dc99e824aedd35d6b1ff5..de18053a4a57268a8e81693b6a534ecc68c53204 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -36,6 +36,9 @@ pub const JINGLE_FT: &'static str = "urn:xmpp:jingle:apps:file-transfer:5"; /// XEP-0234: Jingle File Transfer pub const JINGLE_FT_ERROR: &'static str = "urn:xmpp:jingle:apps:file-transfer:errors:0"; +/// XEP-0261: Jingle In-Band Bytestreams Transport Method +pub const JINGLE_IBB: &'static str = "urn:xmpp:jingle:transports:ibb:1"; + /// XEP-0300: Use of Cryptographic Hash Functions in XMPP pub const HASHES: &'static str = "urn:xmpp:hashes:2"; From 24d563ff18a7be28ba3cecdd317dfba9c6fa9d7f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 22 Apr 2017 19:30:17 +0100 Subject: [PATCH 048/698] jingle: Carry the minidom Element for description, transport and security. --- src/jingle.rs | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index 80ad7b0617bcd4eca807d6c9e4ed10f1046ff993..fbda090cead4a18c6eea13a0c7439dd9c8c0ef4f 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -121,9 +121,9 @@ pub struct Content { pub disposition: String, pub name: String, pub senders: Senders, - pub description: String, - pub transport: String, - pub security: Option, + pub description: (String, Element), + pub transport: (String, Element), + pub security: Option<(String, Element)>, } #[derive(Debug, Clone, PartialEq)] @@ -246,17 +246,38 @@ pub fn parse_jingle(root: &Element) -> Result { if description.is_some() { return Err(Error::ParseError("Content must not have more than one description.")); } - description = Some(stuff.ns().ok_or(Error::ParseError("Description without a namespace."))?); + let namespace = stuff.ns() + .and_then(|ns| ns.parse().ok()) + // TODO: is this even reachable? + .ok_or(Error::ParseError("Invalid namespace on description element."))?; + description = Some(( + namespace, + stuff.clone(), + )); } else if stuff.name() == "transport" { if transport.is_some() { return Err(Error::ParseError("Content must not have more than one transport.")); } - transport = Some(stuff.ns().ok_or(Error::ParseError("Transport without a namespace."))?); + let namespace = stuff.ns() + .and_then(|ns| ns.parse().ok()) + // TODO: is this even reachable? + .ok_or(Error::ParseError("Invalid namespace on transport element."))?; + transport = Some(( + namespace, + stuff.clone(), + )); } else if stuff.name() == "security" { if security.is_some() { return Err(Error::ParseError("Content must not have more than one security.")); } - security = stuff.ns().and_then(|ns| ns.parse().ok()); + let namespace = stuff.ns() + .and_then(|ns| ns.parse().ok()) + // TODO: is this even reachable? + .ok_or(Error::ParseError("Invalid namespace on security element."))?; + security = Some(( + namespace, + stuff.clone(), + )); } } if description.is_none() { @@ -274,7 +295,7 @@ pub fn parse_jingle(root: &Element) -> Result { senders: senders, description: description, transport: transport, - security: security.to_owned(), + security: security, }); } else if child.is("reason", ns::JINGLE) { if reason_element.is_some() { From 6d6ac8a380043f3361fc2879da7eba6b813d2c16 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 22 Apr 2017 20:49:17 +0100 Subject: [PATCH 049/698] body: Simplify the type of Body to an alias of String. --- src/body.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/body.rs b/src/body.rs index 45406e6c7f76b851270293420c93f8a951be578f..dcc4389d748cfd7db182fac77ea8ab2ac03b8b0c 100644 --- a/src/body.rs +++ b/src/body.rs @@ -4,10 +4,7 @@ use error::Error; use ns; -#[derive(Debug, Clone)] -pub struct Body { - pub body: String, -} +pub type Body = String; pub fn parse_body(root: &Element) -> Result { // TODO: also support components and servers. @@ -17,7 +14,7 @@ pub fn parse_body(root: &Element) -> Result { for _ in root.children() { return Err(Error::ParseError("Unknown child in body element.")); } - Ok(Body { body: root.text() }) + Ok(root.text()) } #[cfg(test)] From ca6e65ad761746813b3087c7b7d8613a9c1d94ea Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 02:24:13 +0100 Subject: [PATCH 050/698] body: Add a serialise function. --- src/body.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/body.rs b/src/body.rs index dcc4389d748cfd7db182fac77ea8ab2ac03b8b0c..f170cd389b3d8717a3f2d0e01713af284d9e7927 100644 --- a/src/body.rs +++ b/src/body.rs @@ -17,6 +17,13 @@ pub fn parse_body(root: &Element) -> Result { Ok(root.text()) } +pub fn serialise(body: &Body) -> Element { + Element::builder("body") + .ns(ns::JABBER_CLIENT) + .append(body.to_owned()) + .build() +} + #[cfg(test)] mod tests { use minidom::Element; From 54f404025125e18e8c008feae0a8648d108d6c23 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 03:20:52 +0100 Subject: [PATCH 051/698] attention: Simplify the payload type, and add a serialise function. --- src/attention.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/attention.rs b/src/attention.rs index 2438dc6bce92c33cf271a53bf964ed13b1973180..f64dbf5bddcda2f09edb94747509103a0e6778f0 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -5,9 +5,7 @@ use error::Error; use ns; #[derive(Debug, Clone)] -pub enum Attention { - Attention, -} +pub struct Attention; pub fn parse_attention(root: &Element) -> Result { if !root.is("attention", ns::ATTENTION) { @@ -16,7 +14,13 @@ pub fn parse_attention(root: &Element) -> Result { for _ in root.children() { return Err(Error::ParseError("Unknown child in attention element.")); } - Ok(Attention::Attention) + Ok(Attention) +} + +pub fn serialise(_: &Attention) -> Element { + Element::builder("attention") + .ns(ns::ATTENTION) + .build() } #[cfg(test)] @@ -41,4 +45,12 @@ mod tests { }; assert_eq!(message, "Unknown child in attention element."); } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let attention = attention::Attention; + let elem2 = attention::serialise(&attention); + assert_eq!(elem, elem2); + } } From ab841dc37563ec4ec7f386dc26dd074feb03984f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 03:21:21 +0100 Subject: [PATCH 052/698] receipts: Add a serialise function. --- src/receipts.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/receipts.rs b/src/receipts.rs index 052982ca8ecf8e4b5deaa1f238cb7242e078c6bd..8eb07ff991a5d6f1fefa1c08e5d3ca38fb77691e 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -24,11 +24,24 @@ pub fn parse_receipt(root: &Element) -> Result { } } +pub fn serialise(receipt: &Receipt) -> Element { + match *receipt { + Receipt::Request => Element::builder("request") + .ns(ns::RECEIPTS) + .build(), + Receipt::Received(ref id) => Element::builder("received") + .ns(ns::RECEIPTS) + .attr("id", id.clone()) + .build(), + } +} + #[cfg(test)] mod tests { use minidom::Element; //use error::Error; use receipts; + use ns; #[test] fn test_simple() { @@ -41,4 +54,16 @@ mod tests { let elem: Element = "".parse().unwrap(); receipts::parse_receipt(&elem).unwrap(); } + + #[test] + fn test_serialise() { + let receipt = receipts::Receipt::Request; + let elem = receipts::serialise(&receipt); + assert!(elem.is("request", ns::RECEIPTS)); + + let receipt = receipts::Receipt::Received("coucou".to_owned()); + let elem = receipts::serialise(&receipt); + assert!(elem.is("received", ns::RECEIPTS)); + assert_eq!(elem.attr("id"), Some("coucou")); + } } From c6036194b1f2fbcc24bc7212533d18adb0b064ab Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 03:21:53 +0100 Subject: [PATCH 053/698] delay: Add a serialise function. --- src/delay.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/delay.rs b/src/delay.rs index b54dd04796399dd0ed208dbdbb906e702d5942d5..54fa7e87be4229ab8e7a6bc079de387d9618028f 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -31,6 +31,15 @@ pub fn parse_delay(root: &Element) -> Result { }) } +pub fn serialise(delay: &Delay) -> Element { + Element::builder("delay") + .ns(ns::DELAY) + .attr("from", delay.from.clone()) + .attr("stamp", delay.stamp.clone()) + .append(delay.data.clone()) + .build() +} + #[cfg(test)] mod tests { use minidom::Element; @@ -67,4 +76,28 @@ mod tests { }; assert_eq!(message, "Unknown child in delay element."); } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let delay = delay::Delay { + from: None, + stamp: "2002-09-10T23:08:25Z".to_owned(), + data: None, + }; + let elem2 = delay::serialise(&delay); + assert_eq!(elem, elem2); + } + + #[test] + fn test_serialise_data() { + let elem: Element = "Reason".parse().unwrap(); + let delay = delay::Delay { + from: Some(String::from("juliet@example.org")), + stamp: "2002-09-10T23:08:25Z".to_owned(), + data: Some(String::from("Reason")), + }; + let elem2 = delay::serialise(&delay); + assert_eq!(elem, elem2); + } } From cebccb363fad035204e82a2b5e9b7f08c1fe1bda Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 03:22:02 +0100 Subject: [PATCH 054/698] eme: Add a serialise function. --- src/eme.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/eme.rs b/src/eme.rs index cf33de7fbbee83b7be32680b1f573494e5b1f8a8..79d8c89b58c60b90f198053ced988e9ea3aaaf24 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -25,6 +25,14 @@ pub fn parse_explicit_message_encryption(root: &Element) -> Result Element { + Element::builder("encryption") + .ns(ns::EME) + .attr("namespace", eme.namespace.clone()) + .attr("name", eme.name.clone()) + .build() +} + #[cfg(test)] mod tests { use minidom::Element; @@ -65,4 +73,12 @@ mod tests { }; assert_eq!(message, "Unknown child in encryption element."); } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let eme = eme::ExplicitMessageEncryption { namespace: String::from("coucou"), name: None }; + let elem2 = eme::serialise(&eme); + assert_eq!(elem, elem2); + } } From 7002578bc0a651e29eea5bd07a6472e8507f0149 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 03:22:25 +0100 Subject: [PATCH 055/698] body: Test the serialise function. --- src/body.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/body.rs b/src/body.rs index f170cd389b3d8717a3f2d0e01713af284d9e7927..7703d655c48a444a753f254c6447b083911128a4 100644 --- a/src/body.rs +++ b/src/body.rs @@ -29,6 +29,7 @@ mod tests { use minidom::Element; use error::Error; use body; + use ns; #[test] fn test_simple() { @@ -69,4 +70,11 @@ mod tests { }; assert_eq!(message, "Unknown attribute in body element."); } + + #[test] + fn test_serialise() { + let body = body::Body::from("Hello world!"); + let elem = body::serialise(&body); + assert!(elem.is("body", ns::JABBER_CLIENT)); + } } From 4128c4b9edec6483189dd8d73729b4bb9a39f6f9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 03:22:42 +0100 Subject: [PATCH 056/698] chatstates: Add a serialise function. --- src/chatstates.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/chatstates.rs b/src/chatstates.rs index 8732d5653dc1aa5648bdb44663f52511cb58ac9c..7632086e40f2d052af84ef2159a73de1485b1eb0 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -32,11 +32,23 @@ pub fn parse_chatstate(root: &Element) -> Result { } } +pub fn serialise(chatstate: &ChatState) -> Element { + Element::builder(match *chatstate { + ChatState::Active => "active", + ChatState::Composing => "composing", + ChatState::Gone => "gone", + ChatState::Inactive => "inactive", + ChatState::Paused => "paused", + }).ns(ns::CHATSTATES) + .build() +} + #[cfg(test)] mod tests { use minidom::Element; use error::Error; use chatstates; + use ns; #[test] fn test_simple() { @@ -77,4 +89,11 @@ mod tests { }; assert_eq!(message, "Unknown attribute in chatstate element."); } + + #[test] + fn test_serialise() { + let chatstate = chatstates::ChatState::Active; + let elem = chatstates::serialise(&chatstate); + assert!(elem.is("active", ns::CHATSTATES)); + } } From 7e964b167a5af6e496de927aceba0aacccc04cfc Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 03:23:13 +0100 Subject: [PATCH 057/698] message_correct: Add a serialise function, and simplify the representation. --- src/lib.rs | 4 ++-- src/message_correct.rs | 45 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b311682b8c77afba18c7f4602ce1e7ecc30b3aa2..8efa52a30d0b50b21d5c7f08b10763a0a2c224e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,7 +75,7 @@ pub enum MessagePayload { Receipt(receipts::Receipt), Delay(delay::Delay), Attention(attention::Attention), - MessageCorrect(message_correct::MessageCorrect), + MessageCorrect(message_correct::Replace), ExplicitMessageEncryption(eme::ExplicitMessageEncryption), } @@ -92,7 +92,7 @@ pub fn parse_message_payload(elem: &Element) -> Option { Some(MessagePayload::Delay(delay)) } else if let Ok(attention) = attention::parse_attention(elem) { Some(MessagePayload::Attention(attention)) - } else if let Ok(replace) = message_correct::parse_message_correct(elem) { + } else if let Ok(replace) = message_correct::parse_replace(elem) { Some(MessagePayload::MessageCorrect(replace)) } else if let Ok(eme) = eme::parse_explicit_message_encryption(elem) { Some(MessagePayload::ExplicitMessageEncryption(eme)) diff --git a/src/message_correct.rs b/src/message_correct.rs index 789febb96d3ae634834e0804c4201358753ea2f7..09596ec078b7b0d4f7b4542cd4192056ca8a71f3 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -5,19 +5,29 @@ use error::Error; use ns; #[derive(Debug, Clone)] -pub enum MessageCorrect { - Replace(String), +pub struct Replace { + pub id: String, } -pub fn parse_message_correct(root: &Element) -> Result { +pub fn parse_replace(root: &Element) -> Result { if !root.is("replace", ns::MESSAGE_CORRECT) { return Err(Error::ParseError("This is not a replace element.")); } for _ in root.children() { return Err(Error::ParseError("Unknown child in replace element.")); } - let id = root.attr("id").unwrap_or("").to_owned(); - Ok(MessageCorrect::Replace(id)) + let id = match root.attr("id") { + Some(id) => id.to_owned(), + None => return Err(Error::ParseError("No 'id' attribute present in replace.")), + }; + Ok(Replace { id: id }) +} + +pub fn serialise(replace: &Replace) -> Element { + Element::builder("replace") + .ns(ns::MESSAGE_CORRECT) + .attr("id", replace.id.clone()) + .build() } #[cfg(test)] @@ -28,18 +38,37 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); - message_correct::parse_message_correct(&elem).unwrap(); + let elem: Element = "".parse().unwrap(); + message_correct::parse_replace(&elem).unwrap(); } #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = message_correct::parse_message_correct(&elem).unwrap_err(); + let error = message_correct::parse_replace(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in replace element."); } + + #[test] + fn test_invalid_id() { + let elem: Element = "".parse().unwrap(); + let error = message_correct::parse_replace(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "No 'id' attribute present in replace."); + } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let replace = message_correct::Replace { id: String::from("coucou") }; + let elem2 = message_correct::serialise(&replace); + assert_eq!(elem, elem2); + } } From 90db24eed88342f87b66e3918de291c0858ac422 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 03:41:26 +0100 Subject: [PATCH 058/698] jingle: Simplify the FromStr using match. --- src/jingle.rs | 142 +++++++++++++++++++------------------------------- 1 file changed, 54 insertions(+), 88 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index fbda090cead4a18c6eea13a0c7439dd9c8c0ef4f..9cea6cf821d07072fca39916f5fcb95bf0941ba3 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -30,39 +30,25 @@ impl FromStr for Action { type Err = Error; fn from_str(s: &str) -> Result { - if s == "content-accept" { - Ok(Action::ContentAccept) - } else if s == "content-add" { - Ok(Action::ContentAdd) - } else if s == "content-modify" { - Ok(Action::ContentModify) - } else if s == "content-reject" { - Ok(Action::ContentReject) - } else if s == "content-remove" { - Ok(Action::ContentRemove) - } else if s == "description-info" { - Ok(Action::DescriptionInfo) - } else if s == "security-info" { - Ok(Action::SecurityInfo) - } else if s == "session-accept" { - Ok(Action::SessionAccept) - } else if s == "session-info" { - Ok(Action::SessionInfo) - } else if s == "session-initiate" { - Ok(Action::SessionInitiate) - } else if s == "session-terminate" { - Ok(Action::SessionTerminate) - } else if s == "transport-accept" { - Ok(Action::TransportAccept) - } else if s == "transport-info" { - Ok(Action::TransportInfo) - } else if s == "transport-reject" { - Ok(Action::TransportReject) - } else if s == "transport-replace" { - Ok(Action::TransportReplace) - } else { - Err(Error::ParseError("Unknown action.")) - } + Ok(match s { + "content-accept" => Action::ContentAccept, + "content-add" => Action::ContentAdd, + "content-modify" => Action::ContentModify, + "content-reject" => Action::ContentReject, + "content-remove" => Action::ContentRemove, + "description-info" => Action::DescriptionInfo, + "security-info" => Action::SecurityInfo, + "session-accept" => Action::SessionAccept, + "session-info" => Action::SessionInfo, + "session-initiate" => Action::SessionInitiate, + "session-terminate" => Action::SessionTerminate, + "transport-accept" => Action::TransportAccept, + "transport-info" => Action::TransportInfo, + "transport-reject" => Action::TransportReject, + "transport-replace" => Action::TransportReplace, + + _ => return Err(Error::ParseError("Unknown action.")), + }) } } @@ -79,13 +65,12 @@ impl FromStr for Creator { type Err = Error; fn from_str(s: &str) -> Result { - if s == "initiator" { - Ok(Creator::Initiator) - } else if s == "responder" { - Ok(Creator::Responder) - } else { - Err(Error::ParseError("Unknown creator.")) - } + Ok(match s { + "initiator" => Creator::Initiator, + "responder" => Creator::Responder, + + _ => return Err(Error::ParseError("Unknown creator.")), + }) } } @@ -101,17 +86,14 @@ impl FromStr for Senders { type Err = Error; fn from_str(s: &str) -> Result { - if s == "both" { - Ok(Senders::Both) - } else if s == "initiator" { - Ok(Senders::Initiator) - } else if s == "none" { - Ok(Senders::None_) - } else if s == "responder" { - Ok(Senders::Responder) - } else { - Err(Error::ParseError("Unknown senders.")) - } + Ok(match s { + "both" => Senders::Both, + "initiator" => Senders::Initiator, + "none" => Senders::None_, + "responder" => Senders::Responder, + + _ => return Err(Error::ParseError("Unknown senders.")), + }) } } @@ -151,43 +133,27 @@ impl FromStr for Reason { type Err = Error; fn from_str(s: &str) -> Result { - if s == "alternative-session" { - Ok(Reason::AlternativeSession) - } else if s == "busy" { - Ok(Reason::Busy) - } else if s == "cancel" { - Ok(Reason::Cancel) - } else if s == "connectivity-error" { - Ok(Reason::ConnectivityError) - } else if s == "decline" { - Ok(Reason::Decline) - } else if s == "expired" { - Ok(Reason::Expired) - } else if s == "failed-application" { - Ok(Reason::FailedApplication) - } else if s == "failed-transport" { - Ok(Reason::FailedTransport) - } else if s == "general-error" { - Ok(Reason::GeneralError) - } else if s == "gone" { - Ok(Reason::Gone) - } else if s == "incompatible-parameters" { - Ok(Reason::IncompatibleParameters) - } else if s == "media-error" { - Ok(Reason::MediaError) - } else if s == "security-error" { - Ok(Reason::SecurityError) - } else if s == "success" { - Ok(Reason::Success) - } else if s == "timeout" { - Ok(Reason::Timeout) - } else if s == "unsupported-applications" { - Ok(Reason::UnsupportedApplications) - } else if s == "unsupported-transports" { - Ok(Reason::UnsupportedTransports) - } else { - Err(Error::ParseError("Unknown reason.")) - } + Ok(match s { + "alternative-session" => Reason::AlternativeSession, + "busy" => Reason::Busy, + "cancel" => Reason::Cancel, + "connectivity-error" => Reason::ConnectivityError, + "decline" => Reason::Decline, + "expired" => Reason::Expired, + "failed-application" => Reason::FailedApplication, + "failed-transport" => Reason::FailedTransport, + "general-error" => Reason::GeneralError, + "gone" => Reason::Gone, + "incompatible-parameters" => Reason::IncompatibleParameters, + "media-error" => Reason::MediaError, + "security-error" => Reason::SecurityError, + "success" => Reason::Success, + "timeout" => Reason::Timeout, + "unsupported-applications" => Reason::UnsupportedApplications, + "unsupported-transports" => Reason::UnsupportedTransports, + + _ => return Err(Error::ParseError("Unknown reason.")), + }) } } From 659eaee14ecf688814cf8bff4294dc28bb85ac2a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 03:42:50 +0100 Subject: [PATCH 059/698] data_forms: Simplify the FromStr using match. --- src/data_forms.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index dd9b3e11be87072e6e8049a83c283e614f1e94de..f25c88188d1ab3d46456172a53301ada6f02712d 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -30,17 +30,14 @@ impl FromStr for DataFormType { type Err = Error; fn from_str(s: &str) -> Result { - if s == "cancel" { - Ok(DataFormType::Cancel) - } else if s == "form" { - Ok(DataFormType::Form) - } else if s == "result" { - Ok(DataFormType::Result_) - } else if s == "submit" { - Ok(DataFormType::Submit) - } else { - Err(Error::ParseError("Unknown data form type.")) - } + Ok(match s { + "cancel" => DataFormType::Cancel, + "form" => DataFormType::Form, + "result" => DataFormType::Result_, + "submit" => DataFormType::Submit, + + _ => return Err(Error::ParseError("Unknown data form type.")), + }) } } From 16a6ebd75148310121e54434453e48ab9c463cdc Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 03:44:58 +0100 Subject: [PATCH 060/698] ibb: Simplify the FromStr using match. --- src/ibb.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/ibb.rs b/src/ibb.rs index a8705cd11d9046ce07474bc31a2b929e8d760a02..507e3aa4392eecce48043473b7b20d1bd97c73e9 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -23,13 +23,12 @@ impl FromStr for Stanza { type Err = Error; fn from_str(s: &str) -> Result { - if s == "iq" { - Ok(Stanza::Iq) - } else if s == "message" { - Ok(Stanza::Message) - } else { - Err(Error::ParseError("Invalid 'stanza' attribute.")) - } + Ok(match s { + "iq" => Stanza::Iq, + "message" => Stanza::Message, + + _ => return Err(Error::ParseError("Invalid 'stanza' attribute.")), + }) } } From 15634b6500f33e47eddfe46a09f9c3537f4cc4b0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 03:45:14 +0100 Subject: [PATCH 061/698] ping: Remove extraneous brackets. --- src/ping.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ping.rs b/src/ping.rs index 2fb3e5cc693bbac4a03855cebb3e4d45f91b5401..a8c94c500f5fe5526c1dddde55fe3111e44a7af0 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -5,8 +5,7 @@ use error::Error; use ns; #[derive(Debug, Clone)] -pub struct Ping { -} +pub struct Ping; pub fn parse_ping(root: &Element) -> Result { if !root.is("ping", ns::PING) { From 5e7ad720c32630c2b42dbf8a9afaf31e8a35fe83 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 15:13:03 +0100 Subject: [PATCH 062/698] Add a message parser, along with a dependency on jid. --- Cargo.toml | 1 + src/lib.rs | 43 ++------- src/message.rs | 234 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 241 insertions(+), 37 deletions(-) create mode 100644 src/message.rs diff --git a/Cargo.toml b/Cargo.toml index 7102bc64eb6d1972ee60a46a6e4b0947ac81ae56..f513c3b4da9e4b15d7430d0fe46040505bfd7b2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ authors = ["Emmanuel Gil Peyrot "] [dependencies] minidom = "0.1.1" +jid = "0.1.0" base64 = "0.4.1" sha2 = "0.5.0" sha3 = "0.5.0" diff --git a/src/lib.rs b/src/lib.rs index 8efa52a30d0b50b21d5c7f08b10763a0a2c224e8..bb9dae7ad0a53b277148a6780ec8e75360f72b96 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,13 +1,14 @@ //! A crate parsing common XMPP elements into Rust structures. //! -//! The main entrypoint is `parse_message_payload`, it takes a minidom -//! `Element` reference and optionally returns `Some(MessagePayload)` if the -//! parsing succeeded. +//! Each module implements a `parse` function, which takes a minidom +//! `Element` reference and returns `Some(MessagePayload)` if the parsing +//! succeeded, None otherwise. //! //! Parsed structs can then be manipulated internally, and serialised back //! before being sent over the wire. extern crate minidom; +extern crate jid; extern crate base64; use minidom::Element; @@ -16,6 +17,8 @@ pub mod error; /// XML namespace definitions used through XMPP. pub mod ns; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core +pub mod message; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod body; @@ -66,37 +69,3 @@ pub mod eme; /// XEP-0390: Entity Capabilities 2.0 pub mod ecaps2; - -/// Lists every known payload of a ``. -#[derive(Debug)] -pub enum MessagePayload { - Body(body::Body), - ChatState(chatstates::ChatState), - Receipt(receipts::Receipt), - Delay(delay::Delay), - Attention(attention::Attention), - MessageCorrect(message_correct::Replace), - ExplicitMessageEncryption(eme::ExplicitMessageEncryption), -} - -/// Parse one of the payloads of a `` element, and return `Some` of a -/// `MessagePayload` if parsing it succeeded, `None` otherwise. -pub fn parse_message_payload(elem: &Element) -> Option { - if let Ok(body) = body::parse_body(elem) { - Some(MessagePayload::Body(body)) - } else if let Ok(chatstate) = chatstates::parse_chatstate(elem) { - Some(MessagePayload::ChatState(chatstate)) - } else if let Ok(receipt) = receipts::parse_receipt(elem) { - Some(MessagePayload::Receipt(receipt)) - } else if let Ok(delay) = delay::parse_delay(elem) { - Some(MessagePayload::Delay(delay)) - } else if let Ok(attention) = attention::parse_attention(elem) { - Some(MessagePayload::Attention(attention)) - } else if let Ok(replace) = message_correct::parse_replace(elem) { - Some(MessagePayload::MessageCorrect(replace)) - } else if let Ok(eme) = eme::parse_explicit_message_encryption(elem) { - Some(MessagePayload::ExplicitMessageEncryption(eme)) - } else { - None - } -} diff --git a/src/message.rs b/src/message.rs new file mode 100644 index 0000000000000000000000000000000000000000..985b3eb601ed784bab6b1a293635f357dca3735a --- /dev/null +++ b/src/message.rs @@ -0,0 +1,234 @@ +use std::str::FromStr; + +use minidom::Element; +use minidom::IntoAttributeValue; + +use jid::Jid; + +use error::Error; + +use ns; + +use body; +use chatstates; +use receipts; +use delay; +use attention; +use message_correct; +use eme; + +/// Lists every known payload of a ``. +#[derive(Debug, Clone)] +pub enum MessagePayload { + Body(body::Body), + ChatState(chatstates::ChatState), + Receipt(receipts::Receipt), + Delay(delay::Delay), + Attention(attention::Attention), + MessageCorrect(message_correct::Replace), + ExplicitMessageEncryption(eme::ExplicitMessageEncryption), +} + +#[derive(Debug, Clone, PartialEq)] +pub enum MessageType { + Chat, + Error, + Groupchat, + Headline, + Normal, +} + +impl Default for MessageType { + fn default() -> MessageType { + MessageType::Normal + } +} + +impl FromStr for MessageType { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "chat" => MessageType::Chat, + "error" => MessageType::Error, + "groupchat" => MessageType::Groupchat, + "headline" => MessageType::Headline, + "normal" => MessageType::Normal, + + _ => return Err(Error::ParseError("Invalid 'type' attribute on message element.")), + }) + } +} + +impl IntoAttributeValue for MessageType { + fn into_attribute_value(self) -> Option { + Some(match self { + MessageType::Chat => "chat", + MessageType::Error => "error", + MessageType::Groupchat => "groupchat", + MessageType::Headline => "headline", + MessageType::Normal => "normal", + }.to_owned()) + } +} + +#[derive(Debug, Clone)] +pub enum MessagePayloadType { + XML(Element), + Parsed(MessagePayload), +} + +#[derive(Debug, Clone)] +pub struct Message { + pub from: Option, + pub to: Option, + pub id: Option, + pub type_: MessageType, + pub payloads: Vec, +} + +pub fn parse_message(root: &Element) -> Result { + if !root.is("message", ns::JABBER_CLIENT) { + return Err(Error::ParseError("This is not a message element.")); + } + let from = root.attr("from") + .and_then(|value| value.parse().ok()); + let to = root.attr("to") + .and_then(|value| value.parse().ok()); + let id = root.attr("id") + .and_then(|value| value.parse().ok()); + let type_ = match root.attr("type") { + Some(type_) => type_.parse()?, + None => Default::default(), + }; + let mut payloads = vec!(); + for elem in root.children() { + let payload = if let Ok(body) = body::parse_body(elem) { + Some(MessagePayload::Body(body)) + } else if let Ok(chatstate) = chatstates::parse_chatstate(elem) { + Some(MessagePayload::ChatState(chatstate)) + } else if let Ok(receipt) = receipts::parse_receipt(elem) { + Some(MessagePayload::Receipt(receipt)) + } else if let Ok(delay) = delay::parse_delay(elem) { + Some(MessagePayload::Delay(delay)) + } else if let Ok(attention) = attention::parse_attention(elem) { + Some(MessagePayload::Attention(attention)) + } else if let Ok(replace) = message_correct::parse_replace(elem) { + Some(MessagePayload::MessageCorrect(replace)) + } else if let Ok(eme) = eme::parse_explicit_message_encryption(elem) { + Some(MessagePayload::ExplicitMessageEncryption(eme)) + } else { + None + }; + payloads.push(match payload { + Some(payload) => MessagePayloadType::Parsed(payload), + None => MessagePayloadType::XML(elem.clone()), + }); + } + Ok(Message { + from: from, + to: to, + id: id, + type_: type_, + payloads: payloads, + }) +} + +pub fn serialise_payload(payload: &MessagePayload) -> Element { + match *payload { + MessagePayload::Body(ref body) => body::serialise(body), + MessagePayload::Attention(ref attention) => attention::serialise(attention), + MessagePayload::ChatState(ref chatstate) => chatstates::serialise(chatstate), + MessagePayload::Receipt(ref receipt) => receipts::serialise(receipt), + MessagePayload::Delay(ref delay) => delay::serialise(delay), + MessagePayload::MessageCorrect(ref replace) => message_correct::serialise(replace), + MessagePayload::ExplicitMessageEncryption(ref eme) => eme::serialise(eme), + } +} + +pub fn serialise(message: &Message) -> Element { + let mut stanza = Element::builder("message") + .ns(ns::JABBER_CLIENT) + .attr("from", message.from.clone().and_then(|value| Some(String::from(value)))) + .attr("to", message.to.clone().and_then(|value| Some(String::from(value)))) + .attr("id", message.id.clone()) + .attr("type", message.type_.clone()) + .build(); + for child in message.payloads.clone() { + let elem = match child { + MessagePayloadType::XML(elem) => elem, + MessagePayloadType::Parsed(payload) => serialise_payload(&payload), + }; + stanza.append_child(elem); + } + stanza +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + use minidom::Element; + use error::Error; + use jid::Jid; + use message; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let message = message::parse_message(&elem).unwrap(); + assert_eq!(message.from, None); + assert_eq!(message.to, None); + assert_eq!(message.id, None); + assert_eq!(message.type_, message::MessageType::Normal); + assert!(message.payloads.is_empty()); + } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let message = message::parse_message(&elem).unwrap(); + let message2 = message::Message { + from: None, + to: None, + id: None, + type_: message::MessageType::Normal, + payloads: vec!(), + }; + let elem2 = message::serialise(&message2); + assert_eq!(elem, elem2); + println!("{:#?}", message); + } + + #[test] + fn test_body() { + let elem: Element = "Hello world!".parse().unwrap(); + let message = message::parse_message(&elem).unwrap(); + println!("{:#?}", message); + } + + #[test] + fn test_serialise_body() { + let elem: Element = "Hello world!".parse().unwrap(); + let message = message::parse_message(&elem).unwrap(); + let message2 = message::Message { + from: None, + to: Some(Jid::from_str("coucou@example.org").unwrap()), + id: None, + type_: message::MessageType::Chat, + payloads: vec!( + message::MessagePayloadType::Parsed(message::MessagePayload::Body("Hello world!".to_owned())), + ), + }; + let elem2 = message::serialise(&message2); + assert_eq!(elem, elem2); + println!("{:#?}", message); + } + + #[test] + fn test_attention() { + let elem: Element = "".parse().unwrap(); + let message = message::parse_message(&elem).unwrap(); + let elem2 = message::serialise(&message); + assert_eq!(elem, elem2); + } +} From c81509744f0606b1bfb667ae4a0a22ee3ab11d22 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 15:32:38 +0100 Subject: [PATCH 063/698] lib, message, jingle_ft: Remove unused imports. --- src/jingle_ft.rs | 1 - src/lib.rs | 1 - src/message.rs | 1 - 3 files changed, 3 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 6e3e2803ba27ba6bb345adda6124b7757d1df27f..82941b3606eedddb5971e07b221711c971be200a 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -124,7 +124,6 @@ pub fn parse_jingle_ft(root: &Element) -> Result { #[cfg(test)] mod tests { use minidom::Element; - use error::Error; use jingle_ft; #[test] diff --git a/src/lib.rs b/src/lib.rs index bb9dae7ad0a53b277148a6780ec8e75360f72b96..7be94346a209b544ab8ea9aee069958f13fd98bf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,7 +10,6 @@ extern crate minidom; extern crate jid; extern crate base64; -use minidom::Element; /// Error type returned by every parser on failure. pub mod error; diff --git a/src/message.rs b/src/message.rs index 985b3eb601ed784bab6b1a293635f357dca3735a..609aa77afe591f9020d91016ba05eff39bfa0d75 100644 --- a/src/message.rs +++ b/src/message.rs @@ -168,7 +168,6 @@ pub fn serialise(message: &Message) -> Element { mod tests { use std::str::FromStr; use minidom::Element; - use error::Error; use jid::Jid; use message; From 5e802b457efb52a78bef37ff0bbf5fbd0db2007c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 16:30:41 +0100 Subject: [PATCH 064/698] Cargo.toml: Update the jid crate to 0.2.0, which implements From on String. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f513c3b4da9e4b15d7430d0fe46040505bfd7b2c..262fe7e765ff686688645e69a9f0daf1b9945a29 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Emmanuel Gil Peyrot "] [dependencies] minidom = "0.1.1" -jid = "0.1.0" +jid = "0.2.0" base64 = "0.4.1" sha2 = "0.5.0" sha3 = "0.5.0" From 6a569184842e337ef0425bd1ec341c2855ace56f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 17:30:07 +0100 Subject: [PATCH 065/698] ns: Add the namespaces of hashes functions we implement. --- src/ns.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/ns.rs b/src/ns.rs index de18053a4a57268a8e81693b6a534ecc68c53204..130f1a19abf229b4c2265101c16a8719537183ca 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -41,6 +41,18 @@ pub const JINGLE_IBB: &'static str = "urn:xmpp:jingle:transports:ibb:1"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP pub const HASHES: &'static str = "urn:xmpp:hashes:2"; +/// XEP-0300: Use of Cryptographic Hash Functions in XMPP +pub const HASH_ALGO_SHA_256: &'static str = "urn:xmpp:hash-function-text-name:sha-256"; +/// XEP-0300: Use of Cryptographic Hash Functions in XMPP +pub const HASH_ALGO_SHA_512: &'static str = "urn:xmpp:hash-function-text-name:sha-512"; +/// XEP-0300: Use of Cryptographic Hash Functions in XMPP +pub const HASH_ALGO_SHA3_256: &'static str = "urn:xmpp:hash-function-text-name:sha3-256"; +/// XEP-0300: Use of Cryptographic Hash Functions in XMPP +pub const HASH_ALGO_SHA3_512: &'static str = "urn:xmpp:hash-function-text-name:sha3-512"; +/// XEP-0300: Use of Cryptographic Hash Functions in XMPP +pub const HASH_ALGO_BLAKE2B_256: &'static str = "urn:xmpp:hash-function-text-name:id-blake2b256"; +/// XEP-0300: Use of Cryptographic Hash Functions in XMPP +pub const HASH_ALGO_BLAKE2B_512: &'static str = "urn:xmpp:hash-function-text-name:id-blake2b512"; /// XEP-0308: Last Message Correction pub const MESSAGE_CORRECT: &'static str = "urn:xmpp:message-correct:0"; From 78e8a06ec2a75159774535d182a90ad7b25b3889 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 17:30:23 +0100 Subject: [PATCH 066/698] Add a presence parser. --- src/lib.rs | 2 + src/presence.rs | 179 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 src/presence.rs diff --git a/src/lib.rs b/src/lib.rs index 7be94346a209b544ab8ea9aee069958f13fd98bf..79b7d1daeb6e603bb7b01e8c17061e78b3465980 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,6 +19,8 @@ pub mod ns; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod message; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core +pub mod presence; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod body; /// XEP-0004: Data Forms diff --git a/src/presence.rs b/src/presence.rs new file mode 100644 index 0000000000000000000000000000000000000000..51e5854ea40959e5426877483037893aab64f317 --- /dev/null +++ b/src/presence.rs @@ -0,0 +1,179 @@ +use std::str::FromStr; + +use minidom::Element; +use minidom::IntoAttributeValue; + +use jid::Jid; + +use error::Error; + +use ns; + +use delay; + +/// Lists every known payload of a ``. +#[derive(Debug, Clone)] +pub enum PresencePayload { + Delay(delay::Delay), +} + +#[derive(Debug, Clone, PartialEq)] +pub enum PresenceType { + /// This value is not an acceptable 'type' attribute, it is only used + /// internally to signal the absence of 'type'. + Available, + Error, + Probe, + Subscribe, + Subscribed, + Unavailable, + Unsubscribe, + Unsubscribed, +} + +impl Default for PresenceType { + fn default() -> PresenceType { + PresenceType::Available + } +} + +impl FromStr for PresenceType { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "error" => PresenceType::Error, + "probe" => PresenceType::Probe, + "subscribe" => PresenceType::Subscribe, + "subscribed" => PresenceType::Subscribed, + "unavailable" => PresenceType::Unavailable, + "unsubscribe" => PresenceType::Unsubscribe, + "unsubscribed" => PresenceType::Unsubscribed, + + _ => return Err(Error::ParseError("Invalid 'type' attribute on presence element.")), + }) + } +} + +impl IntoAttributeValue for PresenceType { + fn into_attribute_value(self) -> Option { + Some(match self { + PresenceType::Available => return None, + + PresenceType::Error => "error", + PresenceType::Probe => "probe", + PresenceType::Subscribe => "subscribe", + PresenceType::Subscribed => "subscribed", + PresenceType::Unavailable => "unavailable", + PresenceType::Unsubscribe => "unsubscribe", + PresenceType::Unsubscribed => "unsubscribed", + }.to_owned()) + } +} + +#[derive(Debug, Clone)] +pub enum PresencePayloadType { + XML(Element), + Parsed(PresencePayload), +} + +#[derive(Debug, Clone)] +pub struct Presence { + pub from: Option, + pub to: Option, + pub id: Option, + pub type_: PresenceType, + pub payloads: Vec, +} + +pub fn parse_presence(root: &Element) -> Result { + if !root.is("presence", ns::JABBER_CLIENT) { + return Err(Error::ParseError("This is not a presence element.")); + } + let from = root.attr("from") + .and_then(|value| value.parse().ok()); + let to = root.attr("to") + .and_then(|value| value.parse().ok()); + let id = root.attr("id") + .and_then(|value| value.parse().ok()); + let type_ = match root.attr("type") { + Some(type_) => type_.parse()?, + None => Default::default(), + }; + let mut payloads = vec!(); + for elem in root.children() { + let payload = if let Ok(delay) = delay::parse_delay(elem) { + Some(PresencePayload::Delay(delay)) + } else { + None + }; + payloads.push(match payload { + Some(payload) => PresencePayloadType::Parsed(payload), + None => PresencePayloadType::XML(elem.clone()), + }); + } + Ok(Presence { + from: from, + to: to, + id: id, + type_: type_, + payloads: payloads, + }) +} + +pub fn serialise_payload(payload: &PresencePayload) -> Element { + match *payload { + PresencePayload::Delay(ref delay) => delay::serialise(delay), + } +} + +pub fn serialise(presence: &Presence) -> Element { + let mut stanza = Element::builder("presence") + .ns(ns::JABBER_CLIENT) + .attr("from", presence.from.clone().and_then(|value| Some(String::from(value)))) + .attr("to", presence.to.clone().and_then(|value| Some(String::from(value)))) + .attr("id", presence.id.clone()) + .attr("type", presence.type_.clone()) + .build(); + for child in presence.payloads.clone() { + let elem = match child { + PresencePayloadType::XML(elem) => elem, + PresencePayloadType::Parsed(payload) => serialise_payload(&payload), + }; + stanza.append_child(elem); + } + stanza +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use presence; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let presence = presence::parse_presence(&elem).unwrap(); + assert_eq!(presence.from, None); + assert_eq!(presence.to, None); + assert_eq!(presence.id, None); + assert_eq!(presence.type_, presence::PresenceType::Available); + assert!(presence.payloads.is_empty()); + } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let presence = presence::parse_presence(&elem).unwrap(); + let presence2 = presence::Presence { + from: None, + to: None, + id: None, + type_: presence::PresenceType::Unavailable, + payloads: vec!(), + }; + let elem2 = presence::serialise(&presence2); + assert_eq!(elem, elem2); + println!("{:#?}", presence); + } +} From 20949c283290d8ce2ed98982a7b418761f07408d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 18:36:12 +0100 Subject: [PATCH 067/698] hashes: Implement serialise. --- src/hashes.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/hashes.rs b/src/hashes.rs index f9e1e846c1a4c4f8b1cc2f5edf8efdba427e38a1..febf8b3f7ec65ecf1c08750f9b704c50cfe17bd9 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -28,6 +28,14 @@ pub fn parse_hash(root: &Element) -> Result { }) } +pub fn serialise(hash: &Hash) -> Element { + Element::builder("hash") + .ns(ns::HASHES) + .attr("algo", hash.algo.clone()) + .append(hash.hash.clone()) + .build() +} + #[cfg(test)] mod tests { use minidom::Element; From 429abb996d84ac3a1c1d6e1bd2a02a7b24bc8fef Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 18:36:37 +0100 Subject: [PATCH 068/698] ecaps2: Implement serialise. --- src/ecaps2.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index fabd2e51f6878e50bd10f9891b82750ce9a9d23b..780c62e1e62fdc4128a0d470bcc85836e0f387fe 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -4,6 +4,7 @@ extern crate blake2; use disco::{Feature, Identity, Disco}; use data_forms::DataForm; +use hashes; use hashes::{Hash, parse_hash}; use minidom::Element; @@ -38,6 +39,17 @@ pub fn parse_ecaps2(root: &Element) -> Result { }) } +pub fn serialise(ecaps2: &ECaps2) -> Element { + let mut c = Element::builder("c") + .ns(ns::ECAPS2) + .build(); + for hash in ecaps2.hashes.clone() { + let hash_elem = hashes::serialise(&hash); + c.append_child(hash_elem); + } + c +} + fn compute_item(field: &str) -> Vec { let mut bytes = field.as_bytes().to_vec(); bytes.push(0x1f); From 2c95fd678643d5d7bac0d1237de636c5b159a867 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 18:38:53 +0100 Subject: [PATCH 069/698] presence: Add ecaps2 as a possible payload. --- src/presence.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/presence.rs b/src/presence.rs index 51e5854ea40959e5426877483037893aab64f317..bb3334ef0dd08197f8b488806d1f2965ab526e0a 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -10,11 +10,13 @@ use error::Error; use ns; use delay; +use ecaps2; /// Lists every known payload of a ``. #[derive(Debug, Clone)] pub enum PresencePayload { Delay(delay::Delay), + ECaps2(ecaps2::ECaps2), } #[derive(Debug, Clone, PartialEq)] @@ -104,6 +106,8 @@ pub fn parse_presence(root: &Element) -> Result { for elem in root.children() { let payload = if let Ok(delay) = delay::parse_delay(elem) { Some(PresencePayload::Delay(delay)) + } else if let Ok(ecaps2) = ecaps2::parse_ecaps2(elem) { + Some(PresencePayload::ECaps2(ecaps2)) } else { None }; @@ -124,6 +128,7 @@ pub fn parse_presence(root: &Element) -> Result { pub fn serialise_payload(payload: &PresencePayload) -> Element { match *payload { PresencePayload::Delay(ref delay) => delay::serialise(delay), + PresencePayload::ECaps2(ref ecaps2) => ecaps2::serialise(ecaps2), } } From 948b54d07363cf35b8a62519f4b8c8a1d81d2aa3 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 19:28:03 +0100 Subject: [PATCH 070/698] ecaps2: Implement blake2b. --- Cargo.toml | 1 + src/ecaps2.rs | 25 +++++++++++++++++++------ src/lib.rs | 1 + 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 262fe7e765ff686688645e69a9f0daf1b9945a29..b46e00d702daffb61db3b4dcf9e906de10f17316 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ authors = ["Emmanuel Gil Peyrot "] minidom = "0.1.1" jid = "0.2.0" base64 = "0.4.1" +digest = "0.5.0" sha2 = "0.5.0" sha3 = "0.5.0" blake2 = "0.5.0" diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 780c62e1e62fdc4128a0d470bcc85836e0f387fe..d9e9a7dedd23d84052dd621ee5119a65ae3ecfd9 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -14,6 +14,7 @@ use ns; use self::sha2::{Sha256, Sha512, Digest}; use self::sha3::{Sha3_256, Sha3_512}; use self::blake2::Blake2b; +use digest::VariableOutput; use base64; #[derive(Debug, Clone)] @@ -137,22 +138,20 @@ pub fn hash_ecaps2(data: &[u8], algo: &str) -> String { let hash = hasher.result(); base64::encode(&hash) }, - /* "blake2b-256" => { - // TODO: bit length is most likely wrong here! let mut hasher = Blake2b::default(); hasher.input(data); - let hash = hasher.result(); + let mut buf: [u8; 32] = [0; 32]; + let hash = hasher.variable_result(&mut buf).unwrap(); base64::encode(&hash) }, "blake2b-512" => { - // TODO: bit length is most likely wrong here! let mut hasher = Blake2b::default(); hasher.input(data); - let hash = hasher.result(); + let mut buf: [u8; 64] = [0; 64]; + let hash = hasher.variable_result(&mut buf).unwrap(); base64::encode(&hash) }, - */ _ => panic!(), } } @@ -163,6 +162,7 @@ mod tests { use error::Error; use disco; use ecaps2; + use base64; #[test] fn test_parse() { @@ -434,4 +434,17 @@ mod tests { let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, "sha3-256"); assert_eq!(sha3_256, "XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg="); } + + #[test] + fn test_blake2b_512() { + let hash = ecaps2::hash_ecaps2("abc".as_bytes(), "blake2b-512"); + let known_hash: [u8; 64] = [ + 0xBA, 0x80, 0xA5, 0x3F, 0x98, 0x1C, 0x4D, 0x0D, 0x6A, 0x27, 0x97, 0xB6, 0x9F, 0x12, 0xF6, 0xE9, + 0x4C, 0x21, 0x2F, 0x14, 0x68, 0x5A, 0xC4, 0xB7, 0x4B, 0x12, 0xBB, 0x6F, 0xDB, 0xFF, 0xA2, 0xD1, + 0x7D, 0x87, 0xC5, 0x39, 0x2A, 0xAB, 0x79, 0x2D, 0xC2, 0x52, 0xD5, 0xDE, 0x45, 0x33, 0xCC, 0x95, + 0x18, 0xD3, 0x8A, 0xA8, 0xDB, 0xF1, 0x92, 0x5A, 0xB9, 0x23, 0x86, 0xED, 0xD4, 0x00, 0x99, 0x23, + ]; + let known_hash = base64::encode(&known_hash); + assert_eq!(hash, known_hash); + } } diff --git a/src/lib.rs b/src/lib.rs index 79b7d1daeb6e603bb7b01e8c17061e78b3465980..ba1f6501ba9304442b96c5cb0d95e714ad2d75b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,7 @@ extern crate minidom; extern crate jid; extern crate base64; +extern crate digest; /// Error type returned by every parser on failure. pub mod error; From 93343f3a000ef30b0ac568146f04c906499967ed Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 19:28:25 +0100 Subject: [PATCH 071/698] Add a status parser. --- src/lib.rs | 5 +++- src/status.rs | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 src/status.rs diff --git a/src/lib.rs b/src/lib.rs index ba1f6501ba9304442b96c5cb0d95e714ad2d75b7..2536aaa4b7eade372db77ed8ba5b048c253376f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,8 +21,11 @@ pub mod ns; pub mod message; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod presence; -/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core + +/// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence pub mod body; +/// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence +pub mod status; /// XEP-0004: Data Forms pub mod data_forms; diff --git a/src/status.rs b/src/status.rs new file mode 100644 index 0000000000000000000000000000000000000000..6412ffc05a558ce53782866f6345c7a8a1e7afd8 --- /dev/null +++ b/src/status.rs @@ -0,0 +1,80 @@ +use minidom::Element; + +use error::Error; + +use ns; + +pub type Status = String; + +pub fn parse_status(root: &Element) -> Result { + // TODO: also support components and servers. + if !root.is("status", ns::JABBER_CLIENT) { + return Err(Error::ParseError("This is not a status element.")); + } + for _ in root.children() { + return Err(Error::ParseError("Unknown child in status element.")); + } + Ok(root.text()) +} + +pub fn serialise(status: &Status) -> Element { + Element::builder("status") + .ns(ns::JABBER_CLIENT) + .append(status.to_owned()) + .build() +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use status; + use ns; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + status::parse_status(&elem).unwrap(); + } + + #[test] + fn test_invalid() { + let elem: Element = "".parse().unwrap(); + let error = status::parse_status(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "This is not a status element."); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = status::parse_status(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in status element."); + } + + #[test] + #[ignore] + fn test_invalid_attribute() { + let elem: Element = "".parse().unwrap(); + let error = status::parse_status(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in status element."); + } + + #[test] + fn test_serialise() { + let status = status::Status::from("Hello world!"); + let elem = status::serialise(&status); + assert!(elem.is("status", ns::JABBER_CLIENT)); + } +} From 944cb3964e88d07a140f65f360635ec39adb6494 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 19:32:48 +0100 Subject: [PATCH 072/698] presence: Add status as a possible payload. --- src/presence.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/presence.rs b/src/presence.rs index bb3334ef0dd08197f8b488806d1f2965ab526e0a..ea2da4a3fcdb1c0932febdc5b2ebafa628001ae2 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -9,12 +9,14 @@ use error::Error; use ns; +use status; use delay; use ecaps2; /// Lists every known payload of a ``. #[derive(Debug, Clone)] pub enum PresencePayload { + Status(status::Status), Delay(delay::Delay), ECaps2(ecaps2::ECaps2), } @@ -104,7 +106,9 @@ pub fn parse_presence(root: &Element) -> Result { }; let mut payloads = vec!(); for elem in root.children() { - let payload = if let Ok(delay) = delay::parse_delay(elem) { + let payload = if let Ok(status) = status::parse_status(elem) { + Some(PresencePayload::Status(status)) + } else if let Ok(delay) = delay::parse_delay(elem) { Some(PresencePayload::Delay(delay)) } else if let Ok(ecaps2) = ecaps2::parse_ecaps2(elem) { Some(PresencePayload::ECaps2(ecaps2)) @@ -127,6 +131,7 @@ pub fn parse_presence(root: &Element) -> Result { pub fn serialise_payload(payload: &PresencePayload) -> Element { match *payload { + PresencePayload::Status(ref status) => status::serialise(status), PresencePayload::Delay(ref delay) => delay::serialise(delay), PresencePayload::ECaps2(ref ecaps2) => ecaps2::serialise(ecaps2), } From 04af2f3d264d13b729dd34c0632debae79c75972 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sun, 23 Apr 2017 20:38:13 +0100 Subject: [PATCH 073/698] Implement Iq stanzas parsing --- src/iq.rs | 257 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + 2 files changed, 259 insertions(+) create mode 100644 src/iq.rs diff --git a/src/iq.rs b/src/iq.rs new file mode 100644 index 0000000000000000000000000000000000000000..afdb463f91062388701caed13350226e5fa6252e --- /dev/null +++ b/src/iq.rs @@ -0,0 +1,257 @@ +use minidom::Element; +use minidom::IntoAttributeValue; + +use jid::Jid; + +use error::Error; + +use ns; + +/// Lists every known payload of a ``. +#[derive(Debug, Clone)] +pub enum IqPayload { +} + +#[derive(Debug, Clone)] +pub enum IqPayloadType { + XML(Element), + Parsed(IqPayload), +} + +#[derive(Debug, Clone)] +pub enum IqType { + Get(IqPayloadType), + Set(IqPayloadType), + Result(Option), + Error(IqPayloadType), +} + +impl IntoAttributeValue for IqType { + fn into_attribute_value(self) -> Option { + Some(match self { + IqType::Get(_) => "get", + IqType::Set(_) => "set", + IqType::Result(_) => "result", + IqType::Error(_) => "error", + }.to_owned()) + } +} + +#[derive(Debug, Clone)] +pub struct Iq { + pub from: Option, + pub to: Option, + pub id: Option, + pub payload: IqType, +} + +pub fn parse_iq(root: &Element) -> Result { + if !root.is("iq", ns::JABBER_CLIENT) { + return Err(Error::ParseError("This is not an iq element.")); + } + let from = root.attr("from") + .and_then(|value| value.parse().ok()); + let to = root.attr("to") + .and_then(|value| value.parse().ok()); + let id = root.attr("id") + .and_then(|value| value.parse().ok()); + let type_ = match root.attr("type") { + Some(type_) => type_, + None => return Err(Error::ParseError("Iq element requires a 'type' attribute.")), + }; + + let mut payload = None; + for elem in root.children() { + if payload.is_some() { + return Err(Error::ParseError("Wrong number of children in iq element.")); + } + if type_ == "error" { + if elem.is("error", ns::JABBER_CLIENT) { + payload = Some(elem); + } else if root.children().collect::>().len() != 2 { + return Err(Error::ParseError("Wrong number of children in iq element.")); + } + } else { + payload = Some(elem); + } + } + + let type_ = if type_ == "get" { + if let Some(payload) = payload.clone() { + IqType::Get(IqPayloadType::XML(payload.clone())) + } else { + return Err(Error::ParseError("Wrong number of children in iq element.")); + } + } else if type_ == "set" { + if let Some(payload) = payload.clone() { + IqType::Set(IqPayloadType::XML(payload.clone())) + } else { + return Err(Error::ParseError("Wrong number of children in iq element.")); + } + } else if type_ == "result" { + if let Some(payload) = payload.clone() { + IqType::Result(Some(IqPayloadType::XML(payload.clone()))) + } else { + IqType::Result(None) + } + } else if type_ == "error" { + if let Some(payload) = payload.clone() { + IqType::Error(IqPayloadType::XML(payload.clone())) + } else { + return Err(Error::ParseError("Wrong number of children in iq element.")); + } + } else { + panic!() + }; + + Ok(Iq { + from: from, + to: to, + id: id, + payload: type_, + }) +} + +pub fn serialise(iq: &Iq) -> Element { + let mut stanza = Element::builder("iq") + .ns(ns::JABBER_CLIENT) + .attr("from", iq.from.clone().and_then(|value| Some(String::from(value)))) + .attr("to", iq.to.clone().and_then(|value| Some(String::from(value)))) + .attr("id", iq.id.clone()) + .attr("type", iq.payload.clone()) + .build(); + let elem = match iq.payload.clone() { + IqType::Get(IqPayloadType::XML(elem)) => elem, + IqType::Set(IqPayloadType::XML(elem)) => elem, + IqType::Result(None) => return stanza, + IqType::Result(Some(IqPayloadType::XML(elem))) => elem, + IqType::Error(IqPayloadType::XML(elem)) => elem, + _ => panic!(), + }; + stanza.append_child(elem); + stanza +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use iq; + + #[test] + fn test_require_type() { + let elem: Element = "".parse().unwrap(); + let error = iq::parse_iq(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Iq element requires a 'type' attribute."); + } + + #[test] + fn test_get() { + let elem: Element = " + + ".parse().unwrap(); + let iq = iq::parse_iq(&elem).unwrap(); + let query: Element = "".parse().unwrap(); + assert_eq!(iq.from, None); + assert_eq!(iq.to, None); + assert_eq!(iq.id, None); + assert!(match iq.payload { + iq::IqType::Get(iq::IqPayloadType::XML(element)) => element == query, + _ => false + }); + } + + #[test] + fn test_set() { + let elem: Element = " + + ".parse().unwrap(); + let iq = iq::parse_iq(&elem).unwrap(); + let vcard: Element = "".parse().unwrap(); + assert_eq!(iq.from, None); + assert_eq!(iq.to, None); + assert_eq!(iq.id, None); + assert!(match iq.payload { + iq::IqType::Set(iq::IqPayloadType::XML(element)) => element == vcard, + _ => false + }); + } + + #[test] + fn test_result_empty() { + let elem: Element = "".parse().unwrap(); + let iq = iq::parse_iq(&elem).unwrap(); + assert_eq!(iq.from, None); + assert_eq!(iq.to, None); + assert_eq!(iq.id, None); + assert!(match iq.payload { + iq::IqType::Result(None) => true, + _ => false, + }); + } + + #[test] + fn test_result() { + let elem: Element = " + + ".parse().unwrap(); + let iq = iq::parse_iq(&elem).unwrap(); + let query: Element = "".parse().unwrap(); + assert_eq!(iq.from, None); + assert_eq!(iq.to, None); + assert_eq!(iq.id, None); + assert!(match iq.payload { + iq::IqType::Result(Some(iq::IqPayloadType::XML(element))) => element == query, + _ => false, + }); + } + + #[test] + fn test_error() { + let elem: Element = " + + + + + ".parse().unwrap(); + let iq = iq::parse_iq(&elem).unwrap(); + let error: Element = " + + ".parse().unwrap(); + assert_eq!(iq.from, None); + assert_eq!(iq.to, None); + assert_eq!(iq.id, None); + assert!(match iq.payload { + iq::IqType::Error(iq::IqPayloadType::XML(element)) => element == error, + _ => false, + }); + } + + #[test] + fn test_children_invalid() { + let elem: Element = "".parse().unwrap(); + let error = iq::parse_iq(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Wrong number of children in iq element."); + } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let iq2 = iq::Iq { + from: None, + to: None, + id: None, + payload: iq::IqType::Result(None), + }; + let elem2 = iq::serialise(&iq2); + assert_eq!(elem, elem2); + } +} diff --git a/src/lib.rs b/src/lib.rs index 2536aaa4b7eade372db77ed8ba5b048c253376f9..a21168fe5a93f0c0067b2b4bde00638ae02ab686 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,8 @@ pub mod ns; pub mod message; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod presence; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core +pub mod iq; /// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence pub mod body; From b259ab9ef254743c07fe770670c196b0f1937eb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sun, 23 Apr 2017 21:12:27 +0100 Subject: [PATCH 074/698] iq: Link disco parser to Iq. --- src/iq.rs | 48 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index afdb463f91062388701caed13350226e5fa6252e..87477c3ee5d4ad0473c12cf84fe14387bdba6664 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -7,9 +7,12 @@ use error::Error; use ns; +use disco; + /// Lists every known payload of a ``. #[derive(Debug, Clone)] pub enum IqPayload { + Disco(disco::Disco), } #[derive(Debug, Clone)] @@ -67,36 +70,45 @@ pub fn parse_iq(root: &Element) -> Result { } if type_ == "error" { if elem.is("error", ns::JABBER_CLIENT) { - payload = Some(elem); + payload = Some(IqPayloadType::XML(elem.clone())); } else if root.children().collect::>().len() != 2 { return Err(Error::ParseError("Wrong number of children in iq element.")); } } else { - payload = Some(elem); + let parsed_payload = if let Ok(disco) = disco::parse_disco(elem) { + Some(IqPayload::Disco(disco)) + } else { + None + }; + + payload = match parsed_payload { + Some(payload) => Some(IqPayloadType::Parsed(payload)), + None => Some(IqPayloadType::XML(elem.clone())), + }; } } let type_ = if type_ == "get" { if let Some(payload) = payload.clone() { - IqType::Get(IqPayloadType::XML(payload.clone())) + IqType::Get(payload.clone()) } else { return Err(Error::ParseError("Wrong number of children in iq element.")); } } else if type_ == "set" { if let Some(payload) = payload.clone() { - IqType::Set(IqPayloadType::XML(payload.clone())) + IqType::Set(payload.clone()) } else { return Err(Error::ParseError("Wrong number of children in iq element.")); } } else if type_ == "result" { if let Some(payload) = payload.clone() { - IqType::Result(Some(IqPayloadType::XML(payload.clone()))) + IqType::Result(Some(payload.clone())) } else { IqType::Result(None) } } else if type_ == "error" { if let Some(payload) = payload.clone() { - IqType::Error(IqPayloadType::XML(payload.clone())) + IqType::Error(payload.clone()) } else { return Err(Error::ParseError("Wrong number of children in iq element.")); } @@ -112,6 +124,12 @@ pub fn parse_iq(root: &Element) -> Result { }) } +pub fn serialise_payload(payload: &IqPayload) -> Element { + match *payload { + IqPayload::Disco(ref disco) => disco::serialise_disco(disco), + } +} + pub fn serialise(iq: &Iq) -> Element { let mut stanza = Element::builder("iq") .ns(ns::JABBER_CLIENT) @@ -122,9 +140,12 @@ pub fn serialise(iq: &Iq) -> Element { .build(); let elem = match iq.payload.clone() { IqType::Get(IqPayloadType::XML(elem)) => elem, + IqType::Get(IqPayloadType::Parsed(payload)) => serialise_payload(&payload), IqType::Set(IqPayloadType::XML(elem)) => elem, + IqType::Set(IqPayloadType::Parsed(payload)) => serialise_payload(&payload), IqType::Result(None) => return stanza, IqType::Result(Some(IqPayloadType::XML(elem))) => elem, + IqType::Result(Some(IqPayloadType::Parsed(payload))) => serialise_payload(&payload), IqType::Error(IqPayloadType::XML(elem)) => elem, _ => panic!(), }; @@ -137,6 +158,7 @@ mod tests { use minidom::Element; use error::Error; use iq; + use disco; #[test] fn test_require_type() { @@ -152,10 +174,10 @@ mod tests { #[test] fn test_get() { let elem: Element = " - + ".parse().unwrap(); let iq = iq::parse_iq(&elem).unwrap(); - let query: Element = "".parse().unwrap(); + let query: Element = "".parse().unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); assert_eq!(iq.id, None); @@ -254,4 +276,14 @@ mod tests { let elem2 = iq::serialise(&iq2); assert_eq!(elem, elem2); } + + #[test] + fn test_disco() { + let elem: Element = "".parse().unwrap(); + let iq = iq::parse_iq(&elem).unwrap(); + assert!(match iq.payload { + iq::IqType::Get(iq::IqPayloadType::Parsed(iq::IqPayload::Disco(disco::Disco { .. }))) => true, + _ => false, + }); + } } From c462d230f19bba7c44d32229408d6c7fd9b778a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sun, 23 Apr 2017 21:17:26 +0100 Subject: [PATCH 075/698] ping, iq: Implement ping::serialise_ping and link to Iq. --- src/iq.rs | 5 +++++ src/ping.rs | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/src/iq.rs b/src/iq.rs index 87477c3ee5d4ad0473c12cf84fe14387bdba6664..eda650ac9bcfc74f00a4747d16e430a41b7a6e47 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -8,11 +8,13 @@ use error::Error; use ns; use disco; +use ping; /// Lists every known payload of a ``. #[derive(Debug, Clone)] pub enum IqPayload { Disco(disco::Disco), + Ping(ping::Ping), } #[derive(Debug, Clone)] @@ -77,6 +79,8 @@ pub fn parse_iq(root: &Element) -> Result { } else { let parsed_payload = if let Ok(disco) = disco::parse_disco(elem) { Some(IqPayload::Disco(disco)) + } else if let Ok(ping) = ping::parse_ping(elem) { + Some(IqPayload::Ping(ping)) } else { None }; @@ -127,6 +131,7 @@ pub fn parse_iq(root: &Element) -> Result { pub fn serialise_payload(payload: &IqPayload) -> Element { match *payload { IqPayload::Disco(ref disco) => disco::serialise_disco(disco), + IqPayload::Ping(_) => ping::serialise_ping(), } } diff --git a/src/ping.rs b/src/ping.rs index a8c94c500f5fe5526c1dddde55fe3111e44a7af0..1d5c89ac52ef77a78de5a305e6b3f674beac1f00 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -18,6 +18,10 @@ pub fn parse_ping(root: &Element) -> Result { Ok(Ping { }) } +pub fn serialise_ping() -> Element { + Element::builder("ping").ns(ns::PING).build() +} + #[cfg(test)] mod tests { use minidom::Element; From 1b4c0c45895bd163a123cedd82974486a582b3e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Mon, 24 Apr 2017 19:20:35 +0100 Subject: [PATCH 076/698] iq: Clippify --- src/iq.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index eda650ac9bcfc74f00a4747d16e430a41b7a6e47..9d7fd79ffadfe13761730fa2150da89536d0d3a2 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -144,14 +144,14 @@ pub fn serialise(iq: &Iq) -> Element { .attr("type", iq.payload.clone()) .build(); let elem = match iq.payload.clone() { - IqType::Get(IqPayloadType::XML(elem)) => elem, - IqType::Get(IqPayloadType::Parsed(payload)) => serialise_payload(&payload), - IqType::Set(IqPayloadType::XML(elem)) => elem, - IqType::Set(IqPayloadType::Parsed(payload)) => serialise_payload(&payload), + IqType::Get(IqPayloadType::XML(elem)) + | IqType::Set(IqPayloadType::XML(elem)) + | IqType::Result(Some(IqPayloadType::XML(elem))) + | IqType::Error(IqPayloadType::XML(elem)) => elem, + IqType::Get(IqPayloadType::Parsed(payload)) + | IqType::Set(IqPayloadType::Parsed(payload)) + | IqType::Result(Some(IqPayloadType::Parsed(payload))) => serialise_payload(&payload), IqType::Result(None) => return stanza, - IqType::Result(Some(IqPayloadType::XML(elem))) => elem, - IqType::Result(Some(IqPayloadType::Parsed(payload))) => serialise_payload(&payload), - IqType::Error(IqPayloadType::XML(elem)) => elem, _ => panic!(), }; stanza.append_child(elem); From c71b32ae244d60323fc95322da85855adfaa2e4c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 24 Apr 2017 19:25:00 +0100 Subject: [PATCH 077/698] ibb: Implement IntoAttributeValue for Stanza. --- src/ibb.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/ibb.rs b/src/ibb.rs index 507e3aa4392eecce48043473b7b20d1bd97c73e9..ba8fcafb77d4a31ca205a674480dd348f92f9a21 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -1,6 +1,6 @@ use std::str::FromStr; -use minidom::Element; +use minidom::{Element, IntoAttributeValue}; use base64; use error::Error; @@ -32,6 +32,15 @@ impl FromStr for Stanza { } } +impl IntoAttributeValue for Stanza { + fn into_attribute_value(self) -> Option { + match self { + Stanza::Iq => None, + Stanza::Message => Some(String::from("message")), + } + } +} + #[derive(Debug, Clone)] pub enum IBB { Open { From 2b19a415d42751f5d2d985659ad553a51f7b3d02 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 24 Apr 2017 19:25:00 +0100 Subject: [PATCH 078/698] jingle_ibb: Implement serialise. --- src/jingle_ibb.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 4ab73e684bdeee9243e501f697e3fbd4b7d90348..29590cba83cebbf08f131c70532b82fb8ab49df7 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -44,6 +44,15 @@ pub fn parse_jingle_ibb(root: &Element) -> Result { } } +pub fn serialise(transport: &Transport) -> Element { + Element::builder("transport") + .ns(ns::JINGLE_IBB) + .attr("block-size", format!("{}", transport.block_size)) + .attr("sid", transport.sid.clone()) + .attr("stanza", transport.stanza.clone()) + .build() +} + #[cfg(test)] mod tests { use minidom::Element; From 90f1792ebcc851de3abf2e8d6aaeea63c4c0653d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 24 Apr 2017 19:25:00 +0100 Subject: [PATCH 079/698] jingle_ibb: Make the fields in Transport public. --- src/jingle_ibb.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 29590cba83cebbf08f131c70532b82fb8ab49df7..d6f009a9d6041c586d68863390df9b340f50e0ef 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -10,9 +10,9 @@ use ibb::Stanza; #[derive(Debug, Clone)] pub struct Transport { - block_size: u16, - sid: String, - stanza: Stanza, + pub block_size: u16, + pub sid: String, + pub stanza: Stanza, } fn optional_attr(root: &Element, attr: &str) -> Option { From fa10ab4ebc340ed0adcf173f9e851e035952cd09 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 24 Apr 2017 19:25:00 +0100 Subject: [PATCH 080/698] jingle: Implement From for String on all special attributes. --- src/jingle.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/jingle.rs b/src/jingle.rs index 9cea6cf821d07072fca39916f5fcb95bf0941ba3..c8a2dfc0506e33acaca91fa14493469e5890d6c6 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -52,6 +52,28 @@ impl FromStr for Action { } } +impl From for String { + fn from(action: Action) -> String { + String::from(match action { + Action::ContentAccept => "content-accept", + Action::ContentAdd => "content-add", + Action::ContentModify => "content-modify", + Action::ContentReject => "content-reject", + Action::ContentRemove => "content-remove", + Action::DescriptionInfo => "description-info", + Action::SecurityInfo => "security-info", + Action::SessionAccept => "session-accept", + Action::SessionInfo => "session-info", + Action::SessionInitiate => "session-initiate", + Action::SessionTerminate => "session-terminate", + Action::TransportAccept => "transport-accept", + Action::TransportInfo => "transport-info", + Action::TransportReject => "transport-reject", + Action::TransportReplace => "transport-replace", + }) + } +} + // TODO: use a real JID type. type Jid = String; @@ -74,6 +96,15 @@ impl FromStr for Creator { } } +impl From for String { + fn from(creator: Creator) -> String { + String::from(match creator { + Creator::Initiator => "initiator", + Creator::Responder => "responder", + }) + } +} + #[derive(Debug, Clone, PartialEq)] pub enum Senders { Both, @@ -97,6 +128,17 @@ impl FromStr for Senders { } } +impl From for String { + fn from(senders: Senders) -> String { + String::from(match senders { + Senders::Both => "both", + Senders::Initiator => "initiator", + Senders::None_ => "none", + Senders::Responder => "responder", + }) + } +} + #[derive(Debug, Clone)] pub struct Content { pub creator: Creator, From db1a87e2eec9812a6f69389b1b37c128eb79b251 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 24 Apr 2017 19:25:00 +0100 Subject: [PATCH 081/698] jingle: Implement serialise. --- src/jingle.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/jingle.rs b/src/jingle.rs index c8a2dfc0506e33acaca91fa14493469e5890d6c6..9e2549cf7a455d8867eb45459288b2a1c02df50a 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -347,6 +347,38 @@ pub fn parse_jingle(root: &Element) -> Result { }) } +pub fn serialise_content(content: &Content) -> Element { + let mut root = Element::builder("content") + .ns(ns::JINGLE) + .attr("creator", String::from(content.creator.clone())) + .attr("disposition", content.disposition.clone()) + .attr("name", content.name.clone()) + .attr("senders", String::from(content.senders.clone())) + .build(); + root.append_child(content.description.1.clone()); + root.append_child(content.transport.1.clone()); + if let Some(security) = content.security.clone() { + root.append_child(security.1.clone()); + } + root +} + +pub fn serialise(jingle: &Jingle) -> Element { + let mut root = Element::builder("jingle") + .ns(ns::JINGLE) + .attr("action", String::from(jingle.action.clone())) + .attr("initiator", jingle.initiator.clone()) + .attr("responder", jingle.responder.clone()) + .attr("sid", jingle.sid.clone()) + .build(); + for content in jingle.contents.clone() { + println!("{:?}", content); + let content_elem = serialise_content(&content); + root.append_child(content_elem); + } + root +} + #[cfg(test)] mod tests { use minidom::Element; From 5abf820fadb54e83a6cb178ed536a332ae05feb0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 24 Apr 2017 19:25:00 +0100 Subject: [PATCH 082/698] jingle_ft: Implement serialise. --- src/jingle_ft.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 82941b3606eedddb5971e07b221711c971be200a..05ea3b149dd07012604cee49a5c6170b78bafa41 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -121,6 +121,54 @@ pub fn parse_jingle_ft(root: &Element) -> Result { }) } +pub fn serialise_file(file: &File) -> Element { + let mut root = Element::builder("file") + .ns(ns::JINGLE_FT) + .build(); + println!("{:#?}", file); + if let Some(ref date) = file.date { + root.append_child(Element::builder("date") + .ns(ns::JINGLE_FT) + .append(date.clone()) + .build()); + } + if let Some(ref media_type) = file.media_type { + root.append_child(Element::builder("media-type") + .ns(ns::JINGLE_FT) + .append(media_type.clone()) + .build()); + } + if let Some(ref name) = file.name { + root.append_child(Element::builder("name") + .ns(ns::JINGLE_FT) + .append(name.clone()) + .build()); + } + if let Some(ref size) = file.size { + root.append_child(Element::builder("size") + .ns(ns::JINGLE_FT) + .append(format!("{}", size)) + .build()); + } + if let Some(ref range) = file.range { + root.append_child(Element::builder("range") + .ns(ns::JINGLE_FT) + .append(range.clone()) + .build()); + } + for hash in file.hashes.clone() { + root.append_child(hashes::serialise(&hash)); + } + root +} + +pub fn serialise(desc: &Description) -> Element { + Element::builder("description") + .ns(ns::JINGLE_FT) + .append(serialise_file(&desc.file)) + .build() +} + #[cfg(test)] mod tests { use minidom::Element; From 049ef2359598ae6922cfbace63d069e8ba1556b6 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 24 Apr 2017 19:52:41 +0100 Subject: [PATCH 083/698] jingle_ft: Implement IntoElements on Range, and change size to be an u64. --- src/jingle_ft.rs | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 05ea3b149dd07012604cee49a5c6170b78bafa41..596c1409476517829b01800f528c16084a6190f7 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -1,8 +1,10 @@ extern crate minidom; +use hashes; use hashes::{Hash, parse_hash}; -use minidom::Element; +use minidom::{Element, IntoElements}; +use minidom::convert::ElementEmitter; use error::Error; use ns; @@ -14,12 +16,29 @@ pub struct Range { pub hashes: Vec, } +impl IntoElements for Range { + fn into_elements(self, emitter: &mut ElementEmitter) { + let mut elem = Element::builder("range") + .ns(ns::JINGLE_FT) + .attr("offset", format!("{}", self.offset)) + .attr("length", match self.length { + Some(length) => Some(format!("{}", length)), + None => None + }) + .build(); + for hash in self.hashes { + elem.append_child(hashes::serialise(&hash)); + } + emitter.append_child(elem); + } +} + #[derive(Debug, Clone)] pub struct File { pub date: Option, pub media_type: Option, pub name: Option, - pub size: Option, + pub size: Option, pub range: Option, pub hashes: Vec, } @@ -79,7 +98,7 @@ pub fn parse_jingle_ft(root: &Element) -> Result { if size.is_some() { return Err(Error::ParseError("File must not have more than one size.")); } - size = Some(file_payload.text()); + size = Some(file_payload.text().parse()?); } else if file_payload.is("range", ns::JINGLE_FT) { if range.is_some() { return Err(Error::ParseError("File must not have more than one range.")); @@ -193,7 +212,7 @@ mod tests { assert_eq!(desc.file.media_type, Some(String::from("text/plain"))); assert_eq!(desc.file.name, Some(String::from("test.txt"))); assert_eq!(desc.file.date, Some(String::from("2015-07-26T21:46:00"))); - assert_eq!(desc.file.size, Some(String::from("6144"))); + assert_eq!(desc.file.size, Some(6144u64)); assert_eq!(desc.file.range, None); assert_eq!(desc.file.hashes[0].algo, "sha-1"); assert_eq!(desc.file.hashes[0].hash, "w0mcJylzCn+AfvuGdqkty2+KP48="); From 7fc40dbb191b48f342501f998e19e3cbf405dfa5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 24 Apr 2017 20:03:05 +0100 Subject: [PATCH 084/698] ecaps2: Remove extraneous borrowing on base64::encode. --- src/ecaps2.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index d9e9a7dedd23d84052dd621ee5119a65ae3ecfd9..fc9327a3d2913e8aae2e767537e12345e2c3a6ec 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -143,14 +143,14 @@ pub fn hash_ecaps2(data: &[u8], algo: &str) -> String { hasher.input(data); let mut buf: [u8; 32] = [0; 32]; let hash = hasher.variable_result(&mut buf).unwrap(); - base64::encode(&hash) + base64::encode(hash) }, "blake2b-512" => { let mut hasher = Blake2b::default(); hasher.input(data); let mut buf: [u8; 64] = [0; 64]; let hash = hasher.variable_result(&mut buf).unwrap(); - base64::encode(&hash) + base64::encode(hash) }, _ => panic!(), } From a0eab6c1afff3cd4ae954121f95ec4817dadcfcd Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 26 Apr 2017 00:20:50 +0100 Subject: [PATCH 085/698] ecaps2: Import Digest from digest. --- src/ecaps2.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index fc9327a3d2913e8aae2e767537e12345e2c3a6ec..bfd208cbaedf792e188ee5017eb7918d48fbb2b9 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -11,10 +11,10 @@ use minidom::Element; use error::Error; use ns; -use self::sha2::{Sha256, Sha512, Digest}; +use self::sha2::{Sha256, Sha512}; use self::sha3::{Sha3_256, Sha3_512}; use self::blake2::Blake2b; -use digest::VariableOutput; +use digest::{Digest, VariableOutput}; use base64; #[derive(Debug, Clone)] From 5d51dff9e66d03cd651144d831d0bb5d18464f7e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 26 Apr 2017 00:22:40 +0100 Subject: [PATCH 086/698] disco: Bump minidom dependency to 0.2.0, which fixes xml:lang parsing. --- Cargo.toml | 2 +- src/disco.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b46e00d702daffb61db3b4dcf9e906de10f17316..c049bd6157236e831e0a5f7631e687bb941bc400 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Emmanuel Gil Peyrot "] [dependencies] -minidom = "0.1.1" +minidom = "0.2.0" jid = "0.2.0" base64 = "0.4.1" digest = "0.5.0" diff --git a/src/disco.rs b/src/disco.rs index 4ac7240e163f6f901a7b6b995ac27542f7681a50..5961518c12de4bb80cd6f4b170e4219a927bcc61 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -60,8 +60,7 @@ pub fn parse_disco(root: &Element) -> Result { return Err(Error::ParseError("Identity must have a non-empty 'type' attribute.")) } - // TODO: this must check for the namespace of the attribute, but minidom doesn’t support that yet, see issue #2. - let xml_lang = child.attr("lang").unwrap_or(""); + let xml_lang = child.attr("xml:lang").unwrap_or(""); let name = child.attr("name") .and_then(|name| name.parse().ok()); identities.push(Identity { From 6a48a6bf0065608f47ca3d40c890206544b1643d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 27 Apr 2017 18:33:02 +0100 Subject: [PATCH 087/698] ecaps2, error: Update to base64 0.5. --- Cargo.toml | 2 +- src/ecaps2.rs | 12 ++++++------ src/error.rs | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c049bd6157236e831e0a5f7631e687bb941bc400..e236251d960c3c6b32654e879805a25c03c29f99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ authors = ["Emmanuel Gil Peyrot "] [dependencies] minidom = "0.2.0" jid = "0.2.0" -base64 = "0.4.1" +base64 = "0.5.0" digest = "0.5.0" sha2 = "0.5.0" sha3 = "0.5.0" diff --git a/src/ecaps2.rs b/src/ecaps2.rs index bfd208cbaedf792e188ee5017eb7918d48fbb2b9..d5a13dd39f748b753bb4cf38b2b64954f8fce703 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -118,25 +118,25 @@ pub fn hash_ecaps2(data: &[u8], algo: &str) -> String { let mut hasher = Sha256::default(); hasher.input(data); let hash = hasher.result(); - base64::encode(&hash) + base64::encode(&hash.as_slice()) }, "sha-512" => { let mut hasher = Sha512::default(); hasher.input(data); let hash = hasher.result(); - base64::encode(&hash) + base64::encode(&hash.as_slice()) }, "sha3-256" => { let mut hasher = Sha3_256::default(); hasher.input(data); let hash = hasher.result(); - base64::encode(&hash) + base64::encode(&hash.as_slice()) }, "sha3-512" => { let mut hasher = Sha3_512::default(); hasher.input(data); let hash = hasher.result(); - base64::encode(&hash) + base64::encode(&hash.as_slice()) }, "blake2b-256" => { let mut hasher = Blake2b::default(); @@ -438,12 +438,12 @@ mod tests { #[test] fn test_blake2b_512() { let hash = ecaps2::hash_ecaps2("abc".as_bytes(), "blake2b-512"); - let known_hash: [u8; 64] = [ + let known_hash: Vec = vec!( 0xBA, 0x80, 0xA5, 0x3F, 0x98, 0x1C, 0x4D, 0x0D, 0x6A, 0x27, 0x97, 0xB6, 0x9F, 0x12, 0xF6, 0xE9, 0x4C, 0x21, 0x2F, 0x14, 0x68, 0x5A, 0xC4, 0xB7, 0x4B, 0x12, 0xBB, 0x6F, 0xDB, 0xFF, 0xA2, 0xD1, 0x7D, 0x87, 0xC5, 0x39, 0x2A, 0xAB, 0x79, 0x2D, 0xC2, 0x52, 0xD5, 0xDE, 0x45, 0x33, 0xCC, 0x95, 0x18, 0xD3, 0x8A, 0xA8, 0xDB, 0xF1, 0x92, 0x5A, 0xB9, 0x23, 0x86, 0xED, 0xD4, 0x00, 0x99, 0x23, - ]; + ); let known_hash = base64::encode(&known_hash); assert_eq!(hash, known_hash); } diff --git a/src/error.rs b/src/error.rs index e9506bae476d052f24a9c2e4a6b34e2a60b91cbf..07b01c199a792537bfdc4c5cc89b09715b8cc0d8 100644 --- a/src/error.rs +++ b/src/error.rs @@ -10,7 +10,7 @@ pub enum Error { ParseError(&'static str), IoError(io::Error), XMLError(minidom::Error), - Base64Error(base64::Base64Error), + Base64Error(base64::DecodeError), ParseIntError(num::ParseIntError), } @@ -26,8 +26,8 @@ impl From for Error { } } -impl From for Error { - fn from(err: base64::Base64Error) -> Error { +impl From for Error { + fn from(err: base64::DecodeError) -> Error { Error::Base64Error(err) } } From 30a596cb262faab13f42126b9581184ce91d1b52 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 27 Apr 2017 19:05:51 +0100 Subject: [PATCH 088/698] ibb: Implement serialise. --- src/ibb.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/ibb.rs b/src/ibb.rs index ba8fcafb77d4a31ca205a674480dd348f92f9a21..6281d140f1bee2f0bd25a45d67b3ff5846168a86 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -105,6 +105,33 @@ pub fn parse_ibb(root: &Element) -> Result { } } +pub fn serialise(ibb: &IBB) -> Element { + match *ibb { + IBB::Open { ref block_size, ref sid, ref stanza } => { + Element::builder("open") + .ns(ns::IBB) + .attr("block-size", format!("{}", block_size)) + .attr("sid", sid.to_owned()) + .attr("stanza", stanza.to_owned()) + .build() + }, + IBB::Data { ref seq, ref sid, ref data } => { + Element::builder("data") + .ns(ns::IBB) + .attr("seq", format!("{}", seq)) + .attr("sid", sid.to_owned()) + .attr("data", base64::encode(&data)) + .build() + }, + IBB::Close { ref sid } => { + Element::builder("close") + .ns(ns::IBB) + .attr("sid", sid.to_owned()) + .build() + }, + } +} + #[cfg(test)] mod tests { use minidom::Element; From fe3300b4b00a5e368642a6f20d9064c747b8dadd Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 28 Apr 2017 23:42:27 +0100 Subject: [PATCH 089/698] jingle_ft: Add the forgotten desc element. --- src/jingle_ft.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 596c1409476517829b01800f528c16084a6190f7..ee5bb954f4d65d5cc5da7a9ec063859123b61e39 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -38,6 +38,7 @@ pub struct File { pub date: Option, pub media_type: Option, pub name: Option, + pub desc: Option, pub size: Option, pub range: Option, pub hashes: Vec, @@ -71,6 +72,7 @@ pub fn parse_jingle_ft(root: &Element) -> Result { let mut date = None; let mut media_type = None; let mut name = None; + let mut desc = None; let mut size = None; let mut range = None; let mut hashes = vec!(); @@ -94,6 +96,11 @@ pub fn parse_jingle_ft(root: &Element) -> Result { return Err(Error::ParseError("File must not have more than one name.")); } name = Some(file_payload.text()); + } else if file_payload.is("desc", ns::JINGLE_FT) { + if desc.is_some() { + return Err(Error::ParseError("File must not have more than one desc.")); + } + desc = Some(file_payload.text()); } else if file_payload.is("size", ns::JINGLE_FT) { if size.is_some() { return Err(Error::ParseError("File must not have more than one size.")); @@ -133,6 +140,7 @@ pub fn parse_jingle_ft(root: &Element) -> Result { date: date, media_type: media_type, name: name, + desc: desc, size: size, range: range, hashes: hashes, @@ -163,6 +171,12 @@ pub fn serialise_file(file: &File) -> Element { .append(name.clone()) .build()); } + if let Some(ref desc) = file.desc { + root.append_child(Element::builder("desc") + .ns(ns::JINGLE_FT) + .append(desc.clone()) + .build()); + } if let Some(ref size) = file.size { root.append_child(Element::builder("size") .ns(ns::JINGLE_FT) @@ -211,6 +225,7 @@ mod tests { let desc = jingle_ft::parse_jingle_ft(&elem).unwrap(); assert_eq!(desc.file.media_type, Some(String::from("text/plain"))); assert_eq!(desc.file.name, Some(String::from("test.txt"))); + assert_eq!(desc.file.desc, None); assert_eq!(desc.file.date, Some(String::from("2015-07-26T21:46:00"))); assert_eq!(desc.file.size, Some(6144u64)); assert_eq!(desc.file.range, None); @@ -232,6 +247,7 @@ mod tests { let desc = jingle_ft::parse_jingle_ft(&elem).unwrap(); assert_eq!(desc.file.media_type, None); assert_eq!(desc.file.name, None); + assert_eq!(desc.file.desc, None); assert_eq!(desc.file.date, None); assert_eq!(desc.file.size, None); assert_eq!(desc.file.range, None); From 9f6eea06ce97ca6119395f8370cae730ce573811 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 28 Apr 2017 23:42:50 +0100 Subject: [PATCH 090/698] ns: Fix the hashes text-names namespaces. --- src/ns.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ns.rs b/src/ns.rs index 130f1a19abf229b4c2265101c16a8719537183ca..415858c9e92801eb820e5c4b6e601243a7c84a4f 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -42,17 +42,17 @@ pub const JINGLE_IBB: &'static str = "urn:xmpp:jingle:transports:ibb:1"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP pub const HASHES: &'static str = "urn:xmpp:hashes:2"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP -pub const HASH_ALGO_SHA_256: &'static str = "urn:xmpp:hash-function-text-name:sha-256"; +pub const HASH_ALGO_SHA_256: &'static str = "urn:xmpp:hash-function-text-names:sha-256"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP -pub const HASH_ALGO_SHA_512: &'static str = "urn:xmpp:hash-function-text-name:sha-512"; +pub const HASH_ALGO_SHA_512: &'static str = "urn:xmpp:hash-function-text-names:sha-512"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP -pub const HASH_ALGO_SHA3_256: &'static str = "urn:xmpp:hash-function-text-name:sha3-256"; +pub const HASH_ALGO_SHA3_256: &'static str = "urn:xmpp:hash-function-text-names:sha3-256"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP -pub const HASH_ALGO_SHA3_512: &'static str = "urn:xmpp:hash-function-text-name:sha3-512"; +pub const HASH_ALGO_SHA3_512: &'static str = "urn:xmpp:hash-function-text-names:sha3-512"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP -pub const HASH_ALGO_BLAKE2B_256: &'static str = "urn:xmpp:hash-function-text-name:id-blake2b256"; +pub const HASH_ALGO_BLAKE2B_256: &'static str = "urn:xmpp:hash-function-text-names:id-blake2b256"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP -pub const HASH_ALGO_BLAKE2B_512: &'static str = "urn:xmpp:hash-function-text-name:id-blake2b512"; +pub const HASH_ALGO_BLAKE2B_512: &'static str = "urn:xmpp:hash-function-text-names:id-blake2b512"; /// XEP-0308: Last Message Correction pub const MESSAGE_CORRECT: &'static str = "urn:xmpp:message-correct:0"; From eeb9b36620dafd91061f4fa5269209ec8d613ac3 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 28 Apr 2017 23:43:24 +0100 Subject: [PATCH 091/698] jingle_ft: Make Creator into an enum, instead of a String. --- src/jingle_ft.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index ee5bb954f4d65d5cc5da7a9ec063859123b61e39..4cd73b1dd9349a494ddc9c8738d31233172c9cdd 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -50,8 +50,9 @@ pub struct Description { } #[derive(Debug, Clone)] -pub struct Creator { - pub creator: String, +pub enum Creator { + Initiator, + Responder, } #[derive(Debug, Clone)] From de93c32cb57bcf1560ad9c5d56beb92e655cfed2 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 28 Apr 2017 23:43:47 +0100 Subject: [PATCH 092/698] jingle_ft: Add a received parser. --- src/jingle_ft.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 4cd73b1dd9349a494ddc9c8738d31233172c9cdd..4d5a86a687a4bce490df12bbb415ca6851497512 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -62,6 +62,26 @@ pub struct Checksum { pub file: File, } +#[derive(Debug, Clone)] +pub struct Received { + pub name: String, + pub creator: Creator, +} + +impl IntoElements for Received { + fn into_elements(self, emitter: &mut ElementEmitter) { + let elem = Element::builder("received") + .ns(ns::JINGLE_FT) + .attr("name", self.name) + .attr("creator", match self.creator { + Creator::Initiator => "initiator", + Creator::Responder => "responder", + }) + .build(); + emitter.append_child(elem); + } +} + pub fn parse_jingle_ft(root: &Element) -> Result { if !root.is("description", ns::JINGLE_FT) { return Err(Error::ParseError("This is not a JingleFT description element.")); From c20d37a842f4824a5bb5a41760a0011b289f945e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 28 Apr 2017 23:45:02 +0100 Subject: [PATCH 093/698] jingle: Add serialisation for . --- src/jingle.rs | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/jingle.rs b/src/jingle.rs index 9e2549cf7a455d8867eb45459288b2a1c02df50a..800cb7aee03daa01b4236fe6232f7169ed8364a5 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -2,7 +2,8 @@ extern crate minidom; use std::str::FromStr; -use minidom::Element; +use minidom::{Element, IntoElements}; +use minidom::convert::ElementEmitter; use error::Error; use ns; @@ -199,6 +200,31 @@ impl FromStr for Reason { } } +impl IntoElements for Reason { + fn into_elements(self, emitter: &mut ElementEmitter) { + let elem = Element::builder(match self { + Reason::AlternativeSession => "alternative-session", + Reason::Busy => "busy", + Reason::Cancel => "cancel", + Reason::ConnectivityError => "connectivity-error", + Reason::Decline => "decline", + Reason::Expired => "expired", + Reason::FailedApplication => "failed-application", + Reason::FailedTransport => "failed-transport", + Reason::GeneralError => "general-error", + Reason::Gone => "gone", + Reason::IncompatibleParameters => "incompatible-parameters", + Reason::MediaError => "media-error", + Reason::SecurityError => "security-error", + Reason::Success => "success", + Reason::Timeout => "timeout", + Reason::UnsupportedApplications => "unsupported-applications", + Reason::UnsupportedTransports => "unsupported-transports", + }).build(); + emitter.append_child(elem); + } +} + #[derive(Debug, Clone)] pub struct ReasonElement { pub reason: Reason, @@ -376,6 +402,13 @@ pub fn serialise(jingle: &Jingle) -> Element { let content_elem = serialise_content(&content); root.append_child(content_elem); } + if let Some(ref reason) = jingle.reason { + let reason_elem = Element::builder("reason") + .append(reason.reason.clone()) + .append(reason.text.clone()) + .build(); + root.append_child(reason_elem); + } root } From 10a336f8749817db0e507a42711500bd7c79e3b0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 28 Apr 2017 23:45:27 +0100 Subject: [PATCH 094/698] jingle: Accept any unknown child, as per the specification. --- src/jingle.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index 800cb7aee03daa01b4236fe6232f7169ed8364a5..1437207034ba2d8794a1d66e7d1da61e6b59f931 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -239,7 +239,7 @@ pub struct Jingle { pub sid: String, pub contents: Vec, pub reason: Option, - //pub other: Vec, + pub other: Vec, } pub fn parse_jingle(root: &Element) -> Result { @@ -259,6 +259,7 @@ pub fn parse_jingle(root: &Element) -> Result { let sid = root.attr("sid") .ok_or(Error::ParseError("Jingle must have a 'sid' attribute."))?; let mut reason_element = None; + let mut other = vec!(); for child in root.children() { if child.is("content", ns::JINGLE) { @@ -359,7 +360,7 @@ pub fn parse_jingle(root: &Element) -> Result { text: text, }); } else { - return Err(Error::ParseError("Unknown element in jingle.")); + other.push(child.clone()); } } @@ -370,6 +371,7 @@ pub fn parse_jingle(root: &Element) -> Result { sid: sid.to_owned(), contents: contents, reason: reason_element, + other: other, }) } @@ -451,14 +453,6 @@ mod tests { _ => panic!(), }; assert_eq!(message, "Unknown action."); - - let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown element in jingle."); } #[test] From 4fb4727357fc006de836233d9ca21a95a9685f08 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 28 Apr 2017 23:45:43 +0100 Subject: [PATCH 095/698] jingle: Remove extraneous println!. --- src/jingle.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/jingle.rs b/src/jingle.rs index 1437207034ba2d8794a1d66e7d1da61e6b59f931..4c7a17c9f162ad5dfd3174ff1ed9d2ad13b23a49 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -400,7 +400,6 @@ pub fn serialise(jingle: &Jingle) -> Element { .attr("sid", jingle.sid.clone()) .build(); for content in jingle.contents.clone() { - println!("{:?}", content); let content_elem = serialise_content(&content); root.append_child(content_elem); } From 5e4ba2701bab08b148eccb9a0873ae8c062c43bc Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 28 Apr 2017 23:46:06 +0100 Subject: [PATCH 096/698] iq: Wire up the Jingle parser and serialiser. --- src/iq.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/iq.rs b/src/iq.rs index 9d7fd79ffadfe13761730fa2150da89536d0d3a2..2edf6c8640ea50f85d1a749dbec13f499de47a59 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -8,12 +8,14 @@ use error::Error; use ns; use disco; +use jingle; use ping; /// Lists every known payload of a ``. #[derive(Debug, Clone)] pub enum IqPayload { Disco(disco::Disco), + Jingle(jingle::Jingle), Ping(ping::Ping), } @@ -79,6 +81,8 @@ pub fn parse_iq(root: &Element) -> Result { } else { let parsed_payload = if let Ok(disco) = disco::parse_disco(elem) { Some(IqPayload::Disco(disco)) + } else if let Ok(jingle) = jingle::parse_jingle(elem) { + Some(IqPayload::Jingle(jingle)) } else if let Ok(ping) = ping::parse_ping(elem) { Some(IqPayload::Ping(ping)) } else { @@ -131,6 +135,7 @@ pub fn parse_iq(root: &Element) -> Result { pub fn serialise_payload(payload: &IqPayload) -> Element { match *payload { IqPayload::Disco(ref disco) => disco::serialise_disco(disco), + IqPayload::Jingle(ref jingle) => jingle::serialise(jingle), IqPayload::Ping(_) => ping::serialise_ping(), } } From 846148d618082bf3d20d7616dfeeffdd37de3e1d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 28 Apr 2017 23:46:32 +0100 Subject: [PATCH 097/698] ibb: Fix serialisation of data. --- src/ibb.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ibb.rs b/src/ibb.rs index 6281d140f1bee2f0bd25a45d67b3ff5846168a86..7152d722ce4f7058b16d5c6940d23a811b5fcd47 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -120,7 +120,7 @@ pub fn serialise(ibb: &IBB) -> Element { .ns(ns::IBB) .attr("seq", format!("{}", seq)) .attr("sid", sid.to_owned()) - .attr("data", base64::encode(&data)) + .append(base64::encode(&data)) .build() }, IBB::Close { ref sid } => { From 45b38bcef94e18d85c25be24a4571c8c903ba21b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 01:06:12 +0100 Subject: [PATCH 098/698] iq: Wire up the IBB parser and serialiser. --- src/iq.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/iq.rs b/src/iq.rs index 2edf6c8640ea50f85d1a749dbec13f499de47a59..e2ac6b356717c5f4d51ac92605f83b52417c124e 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -8,6 +8,7 @@ use error::Error; use ns; use disco; +use ibb; use jingle; use ping; @@ -15,6 +16,7 @@ use ping; #[derive(Debug, Clone)] pub enum IqPayload { Disco(disco::Disco), + IBB(ibb::IBB), Jingle(jingle::Jingle), Ping(ping::Ping), } @@ -81,6 +83,8 @@ pub fn parse_iq(root: &Element) -> Result { } else { let parsed_payload = if let Ok(disco) = disco::parse_disco(elem) { Some(IqPayload::Disco(disco)) + } else if let Ok(ibb) = ibb::parse_ibb(elem) { + Some(IqPayload::IBB(ibb)) } else if let Ok(jingle) = jingle::parse_jingle(elem) { Some(IqPayload::Jingle(jingle)) } else if let Ok(ping) = ping::parse_ping(elem) { @@ -135,6 +139,7 @@ pub fn parse_iq(root: &Element) -> Result { pub fn serialise_payload(payload: &IqPayload) -> Element { match *payload { IqPayload::Disco(ref disco) => disco::serialise_disco(disco), + IqPayload::IBB(ref ibb) => ibb::serialise(ibb), IqPayload::Jingle(ref jingle) => jingle::serialise(jingle), IqPayload::Ping(_) => ping::serialise_ping(), } From b09c57ec727069fd6399eda919d7f59ece21e6b4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 01:31:39 +0100 Subject: [PATCH 099/698] ecaps2: Move hashing crates to the main library. --- src/ecaps2.rs | 10 +++------- src/lib.rs | 3 +++ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index d5a13dd39f748b753bb4cf38b2b64954f8fce703..fade1f97f16d06edc1c9b2154ac2ed29a28221f3 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -1,7 +1,3 @@ -extern crate sha2; -extern crate sha3; -extern crate blake2; - use disco::{Feature, Identity, Disco}; use data_forms::DataForm; use hashes; @@ -11,9 +7,9 @@ use minidom::Element; use error::Error; use ns; -use self::sha2::{Sha256, Sha512}; -use self::sha3::{Sha3_256, Sha3_512}; -use self::blake2::Blake2b; +use sha2::{Sha256, Sha512}; +use sha3::{Sha3_256, Sha3_512}; +use blake2::Blake2b; use digest::{Digest, VariableOutput}; use base64; diff --git a/src/lib.rs b/src/lib.rs index a21168fe5a93f0c0067b2b4bde00638ae02ab686..19689510e84cd0cbda497508887ea1ac99d32697 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,9 @@ extern crate minidom; extern crate jid; extern crate base64; extern crate digest; +extern crate sha2; +extern crate sha3; +extern crate blake2; /// Error type returned by every parser on failure. pub mod error; From 8b964df645a856d25a07d808b24ddf78938b8bc8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 03:23:50 +0100 Subject: [PATCH 100/698] Add a stanza-id parser and serialiser. --- src/lib.rs | 3 ++ src/ns.rs | 3 ++ src/stanza_id.rs | 129 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 src/stanza_id.rs diff --git a/src/lib.rs b/src/lib.rs index 19689510e84cd0cbda497508887ea1ac99d32697..e2a656b39d08761257466933e5d50597fb2df4dd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,6 +74,9 @@ pub mod hashes; /// XEP-0308: Last Message Correction pub mod message_correct; +/// XEP-0359: Unique and Stable Stanza IDs +pub mod stanza_id; + /// XEP-0380: Explicit Message Encryption pub mod eme; diff --git a/src/ns.rs b/src/ns.rs index 415858c9e92801eb820e5c4b6e601243a7c84a4f..a34846370734d070eff6eabef48acf5c8f37fdfb 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -57,6 +57,9 @@ pub const HASH_ALGO_BLAKE2B_512: &'static str = "urn:xmpp:hash-function-text-nam /// XEP-0308: Last Message Correction pub const MESSAGE_CORRECT: &'static str = "urn:xmpp:message-correct:0"; +/// XEP-0359: Unique and Stable Stanza IDs +pub const SID: &'static str = "urn:xmpp:sid:0"; + /// XEP-0380: Explicit Message Encryption pub const EME: &'static str = "urn:xmpp:eme:0"; diff --git a/src/stanza_id.rs b/src/stanza_id.rs new file mode 100644 index 0000000000000000000000000000000000000000..14d842b7c296baecf39714e6aa10c411ee0c9b9e --- /dev/null +++ b/src/stanza_id.rs @@ -0,0 +1,129 @@ +use minidom::Element; +use jid::Jid; + +use error::Error; + +use ns; + +#[derive(Debug, Clone)] +pub enum StanzaId { + StanzaId { + id: String, + by: Jid, + }, + OriginId { + id: String, + }, +} + +pub fn parse_stanza_id(root: &Element) -> Result { + let is_stanza_id = root.is("stanza-id", ns::SID); + if !is_stanza_id && !root.is("origin-id", ns::SID) { + return Err(Error::ParseError("This is not a stanza-id or origin-id element.")); + } + for _ in root.children() { + return Err(Error::ParseError("Unknown child in stanza-id or origin-id element.")); + } + let id = match root.attr("id") { + Some(id) => id.to_owned(), + None => return Err(Error::ParseError("No 'id' attribute present in stanza-id or origin-id.")), + }; + Ok(if is_stanza_id { + let by = match root.attr("by") { + Some(by) => by.parse().unwrap(), + None => return Err(Error::ParseError("No 'by' attribute present in stanza-id.")), + }; + StanzaId::StanzaId { id, by } + } else { + StanzaId::OriginId { id } + }) +} + +pub fn serialise(stanza_id: &StanzaId) -> Element { + match *stanza_id { + StanzaId::StanzaId { ref id, ref by } => { + Element::builder("stanza-id") + .ns(ns::SID) + .attr("id", id.clone()) + .attr("by", String::from(by.clone())) + .build() + }, + StanzaId::OriginId { ref id } => { + Element::builder("origin-id") + .ns(ns::SID) + .attr("id", id.clone()) + .build() + }, + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use minidom::Element; + use jid::Jid; + use error::Error; + use stanza_id; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let stanza_id = stanza_id::parse_stanza_id(&elem).unwrap(); + if let stanza_id::StanzaId::StanzaId { id, by } = stanza_id { + assert_eq!(id, String::from("coucou")); + assert_eq!(by, Jid::from_str("coucou@coucou").unwrap()); + } else { + panic!(); + } + + let elem: Element = "".parse().unwrap(); + let stanza_id = stanza_id::parse_stanza_id(&elem).unwrap(); + if let stanza_id::StanzaId::OriginId { id } = stanza_id { + assert_eq!(id, String::from("coucou")); + } else { + panic!(); + } + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = stanza_id::parse_stanza_id(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in stanza-id or origin-id element."); + } + + #[test] + fn test_invalid_id() { + let elem: Element = "".parse().unwrap(); + let error = stanza_id::parse_stanza_id(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "No 'id' attribute present in stanza-id or origin-id."); + } + + #[test] + fn test_invalid_by() { + let elem: Element = "".parse().unwrap(); + let error = stanza_id::parse_stanza_id(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "No 'by' attribute present in stanza-id."); + } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let stanza_id = stanza_id::StanzaId::StanzaId { id: String::from("coucou"), by: Jid::from_str("coucou@coucou").unwrap() }; + let elem2 = stanza_id::serialise(&stanza_id); + assert_eq!(elem, elem2); + } +} From 4dc585f1c984e7af4027b5666f8f439430f4c46b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 03:50:02 +0100 Subject: [PATCH 101/698] delay: Implement IntoElements. --- src/delay.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/delay.rs b/src/delay.rs index 54fa7e87be4229ab8e7a6bc079de387d9618028f..6d0634a2a84ecb73e17133dd194be61c9833a81e 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -1,4 +1,5 @@ -use minidom::Element; +use minidom::{Element, IntoElements}; +use minidom::convert::ElementEmitter; use error::Error; @@ -40,6 +41,13 @@ pub fn serialise(delay: &Delay) -> Element { .build() } +impl IntoElements for Delay { + fn into_elements(self, emitter: &mut ElementEmitter) { + let elem = serialise(&self); + emitter.append_child(elem) + } +} + #[cfg(test)] mod tests { use minidom::Element; From d824a161b65c2064e9eddfd22de1ba6e057fd9a0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 03:50:20 +0100 Subject: [PATCH 102/698] message: Implement IntoElements. --- src/message.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/message.rs b/src/message.rs index 609aa77afe591f9020d91016ba05eff39bfa0d75..34dc49b5b61eb132af607a9c4b94d26ba53d2b03 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1,7 +1,7 @@ use std::str::FromStr; -use minidom::Element; -use minidom::IntoAttributeValue; +use minidom::{Element, IntoElements, IntoAttributeValue}; +use minidom::convert::ElementEmitter; use jid::Jid; @@ -164,6 +164,13 @@ pub fn serialise(message: &Message) -> Element { stanza } +impl IntoElements for Message { + fn into_elements(self, emitter: &mut ElementEmitter) { + let elem = serialise(&self); + emitter.append_child(elem); + } +} + #[cfg(test)] mod tests { use std::str::FromStr; From 0f92a11894c0b5f86b4781c6b147bd7608c7b575 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 03:50:49 +0100 Subject: [PATCH 103/698] Add a forwarding parser and serialiser. --- src/forwarding.rs | 78 +++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 ++ src/ns.rs | 3 ++ 3 files changed, 84 insertions(+) create mode 100644 src/forwarding.rs diff --git a/src/forwarding.rs b/src/forwarding.rs new file mode 100644 index 0000000000000000000000000000000000000000..d7925d7a9fbc212b78b1ae054a27e7295c584902 --- /dev/null +++ b/src/forwarding.rs @@ -0,0 +1,78 @@ +use minidom::{Element, IntoElements}; +use minidom::convert::ElementEmitter; + +use error::Error; + +use delay; +use message; + +use ns; + +#[derive(Debug, Clone)] +pub struct Forwarded { + pub delay: Option, + // XXX: really? Option? + pub stanza: Option, +} + +pub fn parse_forwarded(root: &Element) -> Result { + if !root.is("forwarded", ns::FORWARD) { + return Err(Error::ParseError("This is not a forwarded element.")); + } + let mut delay = None; + let mut stanza = None; + for child in root.children() { + if child.is("delay", ns::DELAY) { + delay = Some(delay::parse_delay(child)?); + } else if child.is("message", ns::JABBER_CLIENT) { + stanza = Some(message::parse_message(child)?); + // TODO: also handle the five other possibilities. + } else { + return Err(Error::ParseError("Unknown child in forwarded element.")); + } + } + Ok(Forwarded { + delay: delay, + stanza: stanza, + }) +} + +pub fn serialise(forwarded: &Forwarded) -> Element { + Element::builder("forwarded") + .ns(ns::FORWARD) + .append(forwarded.delay.clone()) + .append(forwarded.stanza.clone()) + .build() +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use forwarding; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + forwarding::parse_forwarded(&elem).unwrap(); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = forwarding::parse_forwarded(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in forwarded element."); + } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let forwarded = forwarding::Forwarded { delay: None, stanza: None }; + let elem2 = forwarding::serialise(&forwarded); + assert_eq!(elem, elem2); + } +} diff --git a/src/lib.rs b/src/lib.rs index e2a656b39d08761257466933e5d50597fb2df4dd..0dde87afdd43fb72e7e844b82736f58a6271d83d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -68,6 +68,9 @@ pub mod jingle_ft; /// XEP-0261: Jingle In-Band Bytestreams Transport Method pub mod jingle_ibb; +/// XEP-0297: Stanza Forwarding +pub mod forwarding; + /// XEP-0300: Use of Cryptographic Hash Functions in XMPP pub mod hashes; diff --git a/src/ns.rs b/src/ns.rs index a34846370734d070eff6eabef48acf5c8f37fdfb..16f73394b92a143289840d80aa8bcbe6b5e59dbe 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -39,6 +39,9 @@ pub const JINGLE_FT_ERROR: &'static str = "urn:xmpp:jingle:apps:file-transfer:er /// XEP-0261: Jingle In-Band Bytestreams Transport Method pub const JINGLE_IBB: &'static str = "urn:xmpp:jingle:transports:ibb:1"; +/// XEP-0297: Stanza Forwarding +pub const FORWARD: &'static str = "urn:xmpp:forward:0"; + /// XEP-0300: Use of Cryptographic Hash Functions in XMPP pub const HASHES: &'static str = "urn:xmpp:hashes:2"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP From 72a6eee2fd9600c28bb70609b9911074fac509a4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 04:37:18 +0100 Subject: [PATCH 104/698] Add a RSM parser and serialiser. --- src/lib.rs | 3 + src/ns.rs | 3 + src/rsm.rs | 186 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 192 insertions(+) create mode 100644 src/rsm.rs diff --git a/src/lib.rs b/src/lib.rs index 0dde87afdd43fb72e7e844b82736f58a6271d83d..81f62e2d6c89f9655bf4d21ab3e52f4623990843 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,6 +41,9 @@ pub mod disco; /// XEP-0047: In-Band Bytestreams pub mod ibb; +/// XEP-0059: Result Set Management +pub mod rsm; + /// XEP-0085: Chat State Notifications pub mod chatstates; diff --git a/src/ns.rs b/src/ns.rs index 16f73394b92a143289840d80aa8bcbe6b5e59dbe..d06aff62351595f8d360450acf75c761c32fd1ba 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -10,6 +10,9 @@ pub const DISCO_INFO: &'static str = "http://jabber.org/protocol/disco#info"; /// XEP-0047: In-Band Bytestreams pub const IBB: &'static str = "http://jabber.org/protocol/ibb"; +/// XEP-0059: Result Set Management +pub const RSM: &'static str = "http://jabber.org/protocol/rsm"; + /// XEP-0085: Chat State Notifications pub const CHATSTATES: &'static str = "http://jabber.org/protocol/chatstates"; diff --git a/src/rsm.rs b/src/rsm.rs new file mode 100644 index 0000000000000000000000000000000000000000..a70bdd89dc6bbad35a60661f2d74aef3defabe3b --- /dev/null +++ b/src/rsm.rs @@ -0,0 +1,186 @@ +use minidom::Element; + +use error::Error; + +use ns; + +#[derive(Debug, Clone)] +pub struct First { + pub index: Option, + pub base: String, +} + +#[derive(Debug, Clone)] +pub struct Set { + pub after: Option, + pub before: Option, + pub count: Option, + pub first: Option, + pub index: Option, + pub last: Option, + pub max: Option, +} + +pub fn parse_rsm(root: &Element) -> Result { + if !root.is("set", ns::RSM) { + return Err(Error::ParseError("This is not a RSM element.")); + } + let mut after = None; + let mut before = None; + let mut count = None; + let mut first = None; + let mut index = None; + let mut last = None; + let mut max = None; + for child in root.children() { + if child.is("after", ns::RSM) { + if after.is_some() { + return Err(Error::ParseError("Set can’t have more than one after.")); + } + after = Some(child.text()); + } else if child.is("before", ns::RSM) { + if before.is_some() { + return Err(Error::ParseError("Set can’t have more than one before.")); + } + before = Some(child.text()); + } else if child.is("count", ns::RSM) { + if count.is_some() { + return Err(Error::ParseError("Set can’t have more than one count.")); + } + count = Some(child.text().parse()?); + } else if child.is("first", ns::RSM) { + if first.is_some() { + return Err(Error::ParseError("Set can’t have more than one first.")); + } + first = Some(First { + index: match child.attr("index") { + Some(index) => Some(index.parse()?), + None => None, + }, + base: child.text(), + }); + } else if child.is("index", ns::RSM) { + if index.is_some() { + return Err(Error::ParseError("Set can’t have more than one index.")); + } + index = Some(child.text().parse()?); + } else if child.is("last", ns::RSM) { + if last.is_some() { + return Err(Error::ParseError("Set can’t have more than one last.")); + } + last = Some(child.text()); + } else if child.is("max", ns::RSM) { + if max.is_some() { + return Err(Error::ParseError("Set can’t have more than one max.")); + } + max = Some(child.text().parse()?); + } else { + return Err(Error::ParseError("Unknown child in set element.")); + } + } + Ok(Set { + after: after, + before: before, + count: count, + first: first, + index: index, + last: last, + max: max, + }) +} + +pub fn serialise(rsm: &Set) -> Element { + let mut elem = Element::builder("set") + .ns(ns::RSM) + .build(); + if rsm.after.is_some() { + elem.append_child(Element::builder("after").ns(ns::RSM).append(rsm.after.clone()).build()); + } + if rsm.before.is_some() { + elem.append_child(Element::builder("before").ns(ns::RSM).append(rsm.before.clone()).build()); + } + if rsm.count.is_some() { + elem.append_child(Element::builder("count").ns(ns::RSM).append(format!("{}", rsm.count.unwrap())).build()); + } + if rsm.first.is_some() { + let first = rsm.first.clone().unwrap(); + elem.append_child(Element::builder("first") + .ns(ns::RSM) + .attr("index", match first.index { + Some(index) => Some(format!("{}", index)), + None => None, + }) + .append(first.base.clone()).build()); + } + if rsm.index.is_some() { + elem.append_child(Element::builder("index").ns(ns::RSM).append(format!("{}", rsm.index.unwrap())).build()); + } + if rsm.last.is_some() { + elem.append_child(Element::builder("last").ns(ns::RSM).append(rsm.last.clone()).build()); + } + if rsm.max.is_some() { + elem.append_child(Element::builder("max").ns(ns::RSM).append(format!("{}", rsm.max.unwrap())).build()); + } + elem +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use rsm; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let set = rsm::parse_rsm(&elem).unwrap(); + assert_eq!(set.after, None); + assert_eq!(set.before, None); + assert_eq!(set.count, None); + match set.first { + Some(_) => panic!(), + None => (), + } + assert_eq!(set.index, None); + assert_eq!(set.last, None); + assert_eq!(set.max, None); + } + + #[test] + fn test_unknown() { + let elem: Element = "".parse().unwrap(); + let error = rsm::parse_rsm(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "This is not a RSM element."); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = rsm::parse_rsm(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in set element."); + } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let rsm = rsm::Set { + after: None, + before: None, + count: None, + first: None, + index: None, + last: None, + max: None, + }; + let elem2 = rsm::serialise(&rsm); + assert_eq!(elem, elem2); + } +} From 7cd4a49011fc3f0837eb0de50e7c5c7e5fe45026 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 06:06:41 +0100 Subject: [PATCH 105/698] error: Add JidParseError. --- src/error.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/error.rs b/src/error.rs index 07b01c199a792537bfdc4c5cc89b09715b8cc0d8..1f001ca5cd9141e24a375ba44cc49fd3a8fca8ad 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,6 +4,7 @@ use std::num; use base64; use minidom; +use jid; #[derive(Debug)] pub enum Error { @@ -12,6 +13,7 @@ pub enum Error { XMLError(minidom::Error), Base64Error(base64::DecodeError), ParseIntError(num::ParseIntError), + JidParseError(jid::JidParseError), } impl From for Error { @@ -37,3 +39,9 @@ impl From for Error { Error::ParseIntError(err) } } + +impl From for Error { + fn from(err: jid::JidParseError) -> Error { + Error::JidParseError(err) + } +} From 0b2d46aa3aeae35ab3ef23af2db2f22f1fd82673 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 06:07:00 +0100 Subject: [PATCH 106/698] Add a MAM parser and serialiser. --- src/lib.rs | 3 + src/mam.rs | 283 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/ns.rs | 3 + 3 files changed, 289 insertions(+) create mode 100644 src/mam.rs diff --git a/src/lib.rs b/src/lib.rs index 81f62e2d6c89f9655bf4d21ab3e52f4623990843..e3b19caee8e942a700f449a810a5ccaa13088818 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -80,6 +80,9 @@ pub mod hashes; /// XEP-0308: Last Message Correction pub mod message_correct; +/// XEP-0313: Message Archive Management +pub mod mam; + /// XEP-0359: Unique and Stable Stanza IDs pub mod stanza_id; diff --git a/src/mam.rs b/src/mam.rs new file mode 100644 index 0000000000000000000000000000000000000000..416aefc9ee65dad100c2091486d8627e2528a6d1 --- /dev/null +++ b/src/mam.rs @@ -0,0 +1,283 @@ +use minidom::Element; +use jid::Jid; + +use error::Error; + +use data_forms; +use data_forms::DataForm; +use rsm; +use rsm::Set; +use forwarding; +use forwarding::Forwarded; + +use ns; + +#[derive(Debug, Clone)] +pub struct Query { + pub queryid: Option, + pub node: Option, + pub form: Option, + pub set: Option, +} + +#[derive(Debug, Clone)] +pub struct Result_ { + pub queryid: String, + pub id: String, + pub forwarded: Forwarded, +} + +#[derive(Debug, Clone)] +pub struct Fin { + pub complete: bool, + pub set: Set, +} + +#[derive(Debug, Clone)] +pub enum DefaultPrefs { + Always, + Never, + Roster, +} + +#[derive(Debug, Clone)] +pub struct Prefs { + pub default_: Option, + pub always: Vec, + pub never: Vec, +} + +pub fn parse_query(root: &Element) -> Result { + if !root.is("query", ns::MAM) { + return Err(Error::ParseError("This is not a query element.")); + } + let mut form = None; + let mut set = None; + for child in root.children() { + if child.is("x", ns::DATA_FORMS) { + form = Some(data_forms::parse_data_form(child)?); + } else if child.is("set", ns::RSM) { + set = Some(rsm::parse_rsm(child)?); + } else { + return Err(Error::ParseError("Unknown child in query element.")); + } + } + let queryid = match root.attr("queryid") { + Some(queryid) => Some(queryid.to_owned()), + None => None, + }; + let node = match root.attr("node") { + Some(node) => Some(node.to_owned()), + None => None, + }; + Ok(Query { queryid, node, form, set }) +} + +pub fn parse_result(root: &Element) -> Result { + if !root.is("result", ns::MAM) { + return Err(Error::ParseError("This is not a result element.")); + } + let mut forwarded = None; + for child in root.children() { + if child.is("forwarded", ns::FORWARD) { + forwarded = Some(forwarding::parse_forwarded(child)?); + } else { + return Err(Error::ParseError("Unknown child in result element.")); + } + } + let queryid = match root.attr("queryid") { + Some(queryid) => queryid.to_owned(), + None => return Err(Error::ParseError("No 'queryid' attribute present in result.")), + }; + let id = match root.attr("id") { + Some(id) => id.to_owned(), + None => return Err(Error::ParseError("No 'id' attribute present in result.")), + }; + if forwarded.is_none() { + return Err(Error::ParseError("Mandatory forwarded element missing in result.")); + } + let forwarded = forwarded.unwrap(); + Ok(Result_ { + queryid, + id, + forwarded, + }) +} + +pub fn parse_fin(root: &Element) -> Result { + if !root.is("fin", ns::MAM) { + return Err(Error::ParseError("This is not a fin element.")); + } + let mut set = None; + for child in root.children() { + if child.is("set", ns::RSM) { + set = Some(rsm::parse_rsm(child)?); + } else { + return Err(Error::ParseError("Unknown child in fin element.")); + } + } + let complete = match root.attr("complete") { + Some(complete) => complete == "true", + None => false, + }; + if set.is_none() { + return Err(Error::ParseError("Mandatory set element missing in fin.")); + } + let set = set.unwrap(); + Ok(Fin { complete, set }) +} + +pub fn parse_prefs(root: &Element) -> Result { + if !root.is("prefs", ns::MAM) { + return Err(Error::ParseError("This is not a prefs element.")); + } + let mut always = vec!(); + let mut never = vec!(); + for child in root.children() { + if child.is("always", ns::MAM) { + for jid_elem in child.children() { + if !jid_elem.is("jid", ns::MAM) { + return Err(Error::ParseError("Invalid jid element in always.")); + } + always.push(jid_elem.text().parse()?); + } + } else if child.is("never", ns::MAM) { + for jid_elem in child.children() { + if !jid_elem.is("jid", ns::MAM) { + return Err(Error::ParseError("Invalid jid element in never.")); + } + never.push(jid_elem.text().parse()?); + } + } else { + return Err(Error::ParseError("Unknown child in prefs element.")); + } + } + let default_ = match root.attr("default") { + Some("always") => Some(DefaultPrefs::Always), + Some("never") => Some(DefaultPrefs::Never), + Some("roster") => Some(DefaultPrefs::Roster), + None => None, + + _ => return Err(Error::ParseError("Invalid 'default' attribute present in prefs.")), + }; + Ok(Prefs { default_, always, never }) +} + +pub fn serialise_query(query: &Query) -> Element { + let mut elem = Element::builder("query") + .ns(ns::MAM) + .attr("queryid", query.queryid.clone()) + .attr("node", query.node.clone()) + .build(); + //if let Some(form) = query.form { + // elem.append_child(data_forms::serialise(&form)); + //} + if let Some(ref set) = query.set { + elem.append_child(rsm::serialise(&set)); + } + elem +} + +pub fn serialise_result(result: &Result_) -> Element { + let mut elem = Element::builder("result") + .ns(ns::MAM) + .attr("queryid", result.queryid.clone()) + .attr("id", result.id.clone()) + .build(); + elem.append_child(forwarding::serialise(&result.forwarded)); + elem +} + +pub fn serialise_fin(fin: &Fin) -> Element { + let mut elem = Element::builder("fin") + .ns(ns::MAM) + .attr("complete", match fin.complete { + true => Some("true"), + false => None, + }) + .build(); + elem.append_child(rsm::serialise(&fin.set)); + elem +} + +pub fn serialise_prefs(prefs: &Prefs) -> Element { + let mut elem = Element::builder("prefs") + .ns(ns::MAM) + .attr("default", match prefs.default_ { + Some(DefaultPrefs::Always) => Some("always"), + Some(DefaultPrefs::Never) => Some("never"), + Some(DefaultPrefs::Roster) => Some("roster"), + None => None, + }) + .build(); + if !prefs.always.is_empty() { + let mut always = Element::builder("always") + .ns(ns::RSM) + .build(); + for jid in prefs.always.clone() { + always.append_child(Element::builder("jid") + .ns(ns::RSM) + .append(String::from(jid)) + .build()); + } + elem.append_child(always); + } + if !prefs.never.is_empty() { + let mut never = Element::builder("never") + .ns(ns::RSM) + .build(); + for jid in prefs.never.clone() { + never.append_child(Element::builder("jid") + .ns(ns::RSM) + .append(String::from(jid)) + .build()); + } + elem.append_child(never); + } + elem +} + +#[cfg(test)] +mod tests { + /* + use minidom::Element; + use error::Error; + use mam; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + mam::parse_query(&elem).unwrap(); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = mam::parse_query(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in replace element."); + } + + #[test] + fn test_invalid_id() { + let elem: Element = "".parse().unwrap(); + let error = mam::parse_query(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "No 'id' attribute present in replace."); + } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let replace = mam::Query { id: String::from("coucou") }; + let elem2 = mam::serialise(&replace); + assert_eq!(elem, elem2); + } + */ +} diff --git a/src/ns.rs b/src/ns.rs index d06aff62351595f8d360450acf75c761c32fd1ba..b9674c0fc3e2715d028dee178a842338134fda67 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -63,6 +63,9 @@ pub const HASH_ALGO_BLAKE2B_512: &'static str = "urn:xmpp:hash-function-text-nam /// XEP-0308: Last Message Correction pub const MESSAGE_CORRECT: &'static str = "urn:xmpp:message-correct:0"; +/// XEP-0313: Message Archive Management +pub const MAM: &'static str = "urn:xmpp:mam:2"; + /// XEP-0359: Unique and Stable Stanza IDs pub const SID: &'static str = "urn:xmpp:sid:0"; From eecb47f4eddf162aa213f2a6fe1745995a9f868b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 06:41:55 +0100 Subject: [PATCH 107/698] mam: Add tests. --- src/mam.rs | 117 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 99 insertions(+), 18 deletions(-) diff --git a/src/mam.rs b/src/mam.rs index 416aefc9ee65dad100c2091486d8627e2528a6d1..4c0441beed30322f8b6d121b9bac64954e5e2c79 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -239,45 +239,126 @@ pub fn serialise_prefs(prefs: &Prefs) -> Element { #[cfg(test)] mod tests { - /* use minidom::Element; use error::Error; use mam; #[test] - fn test_simple() { - let elem: Element = "".parse().unwrap(); + fn test_query() { + let elem: Element = "".parse().unwrap(); mam::parse_query(&elem).unwrap(); } #[test] - fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); - let error = mam::parse_query(&elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown child in replace element."); + fn test_result() { + let elem: Element = r#" + + + + + Hail to thee + + + +"#.parse().unwrap(); + mam::parse_result(&elem).unwrap(); + } + + #[test] + fn test_fin() { + let elem: Element = r#" + + + 28482-98726-73623 + 09af3-cc343-b409f + + +"#.parse().unwrap(); + mam::parse_fin(&elem).unwrap(); + } + + #[test] + fn test_query_x() { + let elem: Element = r#" + + + + urn:xmpp:mam:2 + + + juliet@capulet.lit + + + +"#.parse().unwrap(); + mam::parse_query(&elem).unwrap(); } #[test] - fn test_invalid_id() { - let elem: Element = "".parse().unwrap(); + fn test_query_x_set() { + let elem: Element = r#" + + + + urn:xmpp:mam:2 + + + 2010-08-07T00:00:00Z + + + + 10 + + +"#.parse().unwrap(); + mam::parse_query(&elem).unwrap(); + } + + #[test] + fn test_prefs_get() { + let elem: Element = "".parse().unwrap(); + mam::parse_prefs(&elem).unwrap(); + + let elem: Element = r#" + + + + +"#.parse().unwrap(); + mam::parse_prefs(&elem).unwrap(); + } + + #[test] + fn test_prefs_result() { + let elem: Element = r#" + + + romeo@montague.lit + + + montague@montague.lit + + +"#.parse().unwrap(); + mam::parse_prefs(&elem).unwrap(); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); let error = mam::parse_query(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "No 'id' attribute present in replace."); + assert_eq!(message, "Unknown child in query element."); } #[test] fn test_serialise() { - let elem: Element = "".parse().unwrap(); - let replace = mam::Query { id: String::from("coucou") }; - let elem2 = mam::serialise(&replace); + let elem: Element = "".parse().unwrap(); + let replace = mam::Query { queryid: None, node: None, form: None, set: None }; + let elem2 = mam::serialise_query(&replace); assert_eq!(elem, elem2); } - */ } From 61efeb827fcfcacfbb01e39ebe1c62aeaeb82976 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 06:50:57 +0100 Subject: [PATCH 108/698] forwarding: Remove unused imports. --- src/forwarding.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/forwarding.rs b/src/forwarding.rs index d7925d7a9fbc212b78b1ae054a27e7295c584902..d1d47a52d3eb121abf1e4b9f47ee1288891c6ed9 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -1,5 +1,4 @@ -use minidom::{Element, IntoElements}; -use minidom::convert::ElementEmitter; +use minidom::Element; use error::Error; From 7750aae5b54d8bf5dbe301c9e9f174a5cdb558a9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 06:57:20 +0100 Subject: [PATCH 109/698] presence, message, jingle_ft: Remove unneeded println!(). --- src/jingle_ft.rs | 1 - src/message.rs | 15 +++++---------- src/presence.rs | 6 ++---- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 4d5a86a687a4bce490df12bbb415ca6851497512..71b671c8cd2cd21886e530eb48e8d4dbf5e719a0 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -173,7 +173,6 @@ pub fn serialise_file(file: &File) -> Element { let mut root = Element::builder("file") .ns(ns::JINGLE_FT) .build(); - println!("{:#?}", file); if let Some(ref date) = file.date { root.append_child(Element::builder("date") .ns(ns::JINGLE_FT) diff --git a/src/message.rs b/src/message.rs index 34dc49b5b61eb132af607a9c4b94d26ba53d2b03..a915b2217dd41ae2ddcd82511fe109dbfc6dde56 100644 --- a/src/message.rs +++ b/src/message.rs @@ -192,31 +192,27 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let message = message::parse_message(&elem).unwrap(); - let message2 = message::Message { + let message = message::Message { from: None, to: None, id: None, type_: message::MessageType::Normal, payloads: vec!(), }; - let elem2 = message::serialise(&message2); + let elem2 = message::serialise(&message); assert_eq!(elem, elem2); - println!("{:#?}", message); } #[test] fn test_body() { let elem: Element = "Hello world!".parse().unwrap(); - let message = message::parse_message(&elem).unwrap(); - println!("{:#?}", message); + message::parse_message(&elem).unwrap(); } #[test] fn test_serialise_body() { let elem: Element = "Hello world!".parse().unwrap(); - let message = message::parse_message(&elem).unwrap(); - let message2 = message::Message { + let message = message::Message { from: None, to: Some(Jid::from_str("coucou@example.org").unwrap()), id: None, @@ -225,9 +221,8 @@ mod tests { message::MessagePayloadType::Parsed(message::MessagePayload::Body("Hello world!".to_owned())), ), }; - let elem2 = message::serialise(&message2); + let elem2 = message::serialise(&message); assert_eq!(elem, elem2); - println!("{:#?}", message); } #[test] diff --git a/src/presence.rs b/src/presence.rs index ea2da4a3fcdb1c0932febdc5b2ebafa628001ae2..a144e3716755281674d09a3dd3f8fa85252ccb71 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -174,16 +174,14 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let presence = presence::parse_presence(&elem).unwrap(); - let presence2 = presence::Presence { + let presence = presence::Presence { from: None, to: None, id: None, type_: presence::PresenceType::Unavailable, payloads: vec!(), }; - let elem2 = presence::serialise(&presence2); + let elem2 = presence::serialise(&presence); assert_eq!(elem, elem2); - println!("{:#?}", presence); } } From 6f2ee2f0ad846ec9d00ee5f70fc27ce821232df7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 21:41:18 +0100 Subject: [PATCH 110/698] Cargo.toml: Add some metadata. --- Cargo.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index e236251d960c3c6b32654e879805a25c03c29f99..7118c2037a0fee52a3a6af99c2bfc13612b54ff7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,11 @@ name = "xmpp-parsers" version = "0.1.0" authors = ["Emmanuel Gil Peyrot "] +description = "Collection of parsers and serialisers for XMPP extensions" +homepage = "https://hg.linkmauve.fr/xmpp-parsers" +repository = "https://hg.linkmauve.fr/xmpp-parsers" +keywords = ["xmpp"] +categories = ["parsing", "network-programming"] [dependencies] minidom = "0.2.0" From 407e4cceb4561135eac38aef5644d5ef4891a590 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 22:14:34 +0100 Subject: [PATCH 111/698] License the project under MPL-2.0. --- Cargo.toml | 2 + LICENSE | 373 +++++++++++++++++++++++++++++++++++++++++ src/attention.rs | 6 + src/body.rs | 6 + src/chatstates.rs | 6 + src/data_forms.rs | 6 + src/delay.rs | 6 + src/disco.rs | 6 + src/ecaps2.rs | 6 + src/eme.rs | 6 + src/error.rs | 6 + src/forwarding.rs | 6 + src/hashes.rs | 6 + src/ibb.rs | 6 + src/iq.rs | 7 + src/jingle.rs | 6 + src/jingle_ft.rs | 6 + src/jingle_ibb.rs | 6 + src/lib.rs | 6 + src/mam.rs | 6 + src/media_element.rs | 6 + src/message.rs | 6 + src/message_correct.rs | 6 + src/ns.rs | 6 + src/ping.rs | 7 + src/presence.rs | 6 + src/receipts.rs | 6 + src/rsm.rs | 6 + src/stanza_id.rs | 6 + src/status.rs | 6 + 30 files changed, 545 insertions(+) create mode 100644 LICENSE diff --git a/Cargo.toml b/Cargo.toml index 7118c2037a0fee52a3a6af99c2bfc13612b54ff7..bef0ab8f8a820eaa3bb5131f585c07aa0704f6d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,8 @@ homepage = "https://hg.linkmauve.fr/xmpp-parsers" repository = "https://hg.linkmauve.fr/xmpp-parsers" keywords = ["xmpp"] categories = ["parsing", "network-programming"] +license = "MPL-2.0" +license-file = "LICENSE" [dependencies] minidom = "0.2.0" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..14e2f777f6c395e7e04ab4aa306bbcc4b0c1120e --- /dev/null +++ b/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/src/attention.rs b/src/attention.rs index f64dbf5bddcda2f09edb94747509103a0e6778f0..2f12545ea436cbea67d58fcc9f6c4a449d5dc18c 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use error::Error; diff --git a/src/body.rs b/src/body.rs index 7703d655c48a444a753f254c6447b083911128a4..8f23b56927dad6d04827aad39bc06e2ff92d403f 100644 --- a/src/body.rs +++ b/src/body.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use error::Error; diff --git a/src/chatstates.rs b/src/chatstates.rs index 7632086e40f2d052af84ef2159a73de1485b1eb0..d26791ef67968e9f0a2540a5e8eb1b0f2c1c7f4b 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use error::Error; diff --git a/src/data_forms.rs b/src/data_forms.rs index f25c88188d1ab3d46456172a53301ada6f02712d..ebd2ff2df3dffdc3818ee645d9fe47d7381c970d 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + extern crate minidom; use std::str::FromStr; diff --git a/src/delay.rs b/src/delay.rs index 6d0634a2a84ecb73e17133dd194be61c9833a81e..8e8e382ea2276189c27c2506445492cb29a5550e 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::{Element, IntoElements}; use minidom::convert::ElementEmitter; diff --git a/src/disco.rs b/src/disco.rs index 5961518c12de4bb80cd6f4b170e4219a927bcc61..5bdaef8b0ddd3c664674bcacf6311d87b136e5e6 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + extern crate minidom; use minidom::Element; diff --git a/src/ecaps2.rs b/src/ecaps2.rs index fade1f97f16d06edc1c9b2154ac2ed29a28221f3..aa2cde7e10ca423e351ecb7b71baae6effe1c894 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use disco::{Feature, Identity, Disco}; use data_forms::DataForm; use hashes; diff --git a/src/eme.rs b/src/eme.rs index 79d8c89b58c60b90f198053ced988e9ea3aaaf24..879ddda0c6684f4f20992c8c21b6a0de3c59d813 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use error::Error; diff --git a/src/error.rs b/src/error.rs index 1f001ca5cd9141e24a375ba44cc49fd3a8fca8ad..a65f7e2df4aab5c0355fe569139a0af4007a6d12 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use std::convert::From; use std::io; use std::num; diff --git a/src/forwarding.rs b/src/forwarding.rs index d1d47a52d3eb121abf1e4b9f47ee1288891c6ed9..991d508ada8a4e16e622d0360cb9e42a36b0b000 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use error::Error; diff --git a/src/hashes.rs b/src/hashes.rs index febf8b3f7ec65ecf1c08750f9b704c50cfe17bd9..c489a32fcde701f203f219409bd372a3d6d76a2b 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use error::Error; diff --git a/src/ibb.rs b/src/ibb.rs index 7152d722ce4f7058b16d5c6940d23a811b5fcd47..d805eb810af168f1e0f416d952cf2d399e71754e 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; diff --git a/src/iq.rs b/src/iq.rs index e2ac6b356717c5f4d51ac92605f83b52417c124e..645c4ba0f96e97d0ad066ab675f6545c5e71642d 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -1,3 +1,10 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// Copyright (c) 2017 Maxime “pep” Buquet +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use minidom::IntoAttributeValue; diff --git a/src/jingle.rs b/src/jingle.rs index 4c7a17c9f162ad5dfd3174ff1ed9d2ad13b23a49..bd903d084edca8260fd64ec8b1993e372f8c0873 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + extern crate minidom; use std::str::FromStr; diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 71b671c8cd2cd21886e530eb48e8d4dbf5e719a0..678bca48359839072c87f947bb4f26515caffac9 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + extern crate minidom; use hashes; diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index d6f009a9d6041c586d68863390df9b340f50e0ef..8e0d04ca90b9f4dd6effecaf6d133cd2951cd806 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use std::str::FromStr; use minidom::Element; diff --git a/src/lib.rs b/src/lib.rs index e3b19caee8e942a700f449a810a5ccaa13088818..aadf4f79c9188a066567bc1a06e1672906828822 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,12 @@ //! Parsed structs can then be manipulated internally, and serialised back //! before being sent over the wire. +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + extern crate minidom; extern crate jid; extern crate base64; diff --git a/src/mam.rs b/src/mam.rs index 4c0441beed30322f8b6d121b9bac64954e5e2c79..cb0cc7ac048dce7a73f86daf56d53999bf37d33c 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use jid::Jid; diff --git a/src/media_element.rs b/src/media_element.rs index b48636a507ce0a80ac7e3ec252690b8eb60161a1..a3c360e576d91cffb4bb9aa8b9a2995fdefe45eb 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use error::Error; diff --git a/src/message.rs b/src/message.rs index a915b2217dd41ae2ddcd82511fe109dbfc6dde56..a88db4c9d7504c8fb0ba1b211b5bb514b1306005 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use std::str::FromStr; use minidom::{Element, IntoElements, IntoAttributeValue}; diff --git a/src/message_correct.rs b/src/message_correct.rs index 09596ec078b7b0d4f7b4542cd4192056ca8a71f3..27ac7e917fcad7f8186800e8c7855eac95a99d17 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use error::Error; diff --git a/src/ns.rs b/src/ns.rs index b9674c0fc3e2715d028dee178a842338134fda67..495c2856681576a1f605128bc7973efed83125f2 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub const JABBER_CLIENT: &'static str = "jabber:client"; diff --git a/src/ping.rs b/src/ping.rs index 1d5c89ac52ef77a78de5a305e6b3f674beac1f00..b6b6f617d1c60b3854e0ef3c1af806ee0d310422 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -1,3 +1,10 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// Copyright (c) 2017 Maxime “pep” Buquet +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use error::Error; diff --git a/src/presence.rs b/src/presence.rs index a144e3716755281674d09a3dd3f8fa85252ccb71..0f4379584339b7260959432df5a6e0de4c39abdd 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use std::str::FromStr; use minidom::Element; diff --git a/src/receipts.rs b/src/receipts.rs index 8eb07ff991a5d6f1fefa1c08e5d3ca38fb77691e..3dc3b787606aaf2a08ca9506f4a9a7e30abff7ff 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use error::Error; diff --git a/src/rsm.rs b/src/rsm.rs index a70bdd89dc6bbad35a60661f2d74aef3defabe3b..d23fa1b6bc58919b080182f28627e2c67be73460 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use error::Error; diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 14d842b7c296baecf39714e6aa10c411ee0c9b9e..5d34c3dc5c44b4bb47399e6543167d26572566a4 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use jid::Jid; diff --git a/src/status.rs b/src/status.rs index 6412ffc05a558ce53782866f6345c7a8a1e7afd8..66bde73e8ff3348838d2b66f69a3caac7d401b33 100644 --- a/src/status.rs +++ b/src/status.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use error::Error; From 4b90c1dc1f467054ef13880388797abfa2dc9f22 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 22:10:38 +0100 Subject: [PATCH 112/698] =?UTF-8?q?data=5Fforms,=20disco,=20jingle,=20jing?= =?UTF-8?q?le=5Fft:=20Remove=20superfluous=20=E2=80=9Cextern=20crate?= =?UTF-8?q?=E2=80=9D.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/data_forms.rs | 2 -- src/disco.rs | 2 -- src/jingle.rs | 2 -- src/jingle_ft.rs | 2 -- 4 files changed, 8 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index ebd2ff2df3dffdc3818ee645d9fe47d7381c970d..3b3757efea29cd00abeb4ccac66b6b9d0afe8b2a 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -extern crate minidom; - use std::str::FromStr; use minidom::Element; diff --git a/src/disco.rs b/src/disco.rs index 5bdaef8b0ddd3c664674bcacf6311d87b136e5e6..bc448091b830ee903356d444b05c1d046f1bd975 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -extern crate minidom; - use minidom::Element; use error::Error; diff --git a/src/jingle.rs b/src/jingle.rs index bd903d084edca8260fd64ec8b1993e372f8c0873..717a3b77217ebdc8b6f290c60e7df9ce348e86ce 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -extern crate minidom; - use std::str::FromStr; use minidom::{Element, IntoElements}; diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 678bca48359839072c87f947bb4f26515caffac9..2028fe930d81db97d0de164b921baa5fbb5249ef 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -extern crate minidom; - use hashes; use hashes::{Hash, parse_hash}; From 0e20810a832adf1523fb3f4056d3b8b1a9d0abe1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 30 Apr 2017 20:33:53 +0100 Subject: [PATCH 113/698] status, presence: Merge status into presence. --- src/lib.rs | 2 - src/presence.rs | 108 +++++++++++++++++++++++++++++++++++++++++------- src/status.rs | 86 -------------------------------------- 3 files changed, 93 insertions(+), 103 deletions(-) delete mode 100644 src/status.rs diff --git a/src/lib.rs b/src/lib.rs index aadf4f79c9188a066567bc1a06e1672906828822..0a5eac0ad7150d5d6954fbc86b146b1b8ea7f1b2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,8 +35,6 @@ pub mod iq; /// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence pub mod body; -/// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence -pub mod status; /// XEP-0004: Data Forms pub mod data_forms; diff --git a/src/presence.rs b/src/presence.rs index 0f4379584339b7260959432df5a6e0de4c39abdd..cafb7f6e92c8409ff6ec3476c51fa154faca2209 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -15,14 +15,15 @@ use error::Error; use ns; -use status; use delay; use ecaps2; +pub type Status = String; + /// Lists every known payload of a ``. #[derive(Debug, Clone)] pub enum PresencePayload { - Status(status::Status), + Status(Status), Delay(delay::Delay), ECaps2(ecaps2::ECaps2), } @@ -112,19 +113,25 @@ pub fn parse_presence(root: &Element) -> Result { }; let mut payloads = vec!(); for elem in root.children() { - let payload = if let Ok(status) = status::parse_status(elem) { - Some(PresencePayload::Status(status)) - } else if let Ok(delay) = delay::parse_delay(elem) { - Some(PresencePayload::Delay(delay)) - } else if let Ok(ecaps2) = ecaps2::parse_ecaps2(elem) { - Some(PresencePayload::ECaps2(ecaps2)) + if elem.is("status", ns::JABBER_CLIENT) { + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in status element.")); + } + let payload = PresencePayload::Status(elem.text()); + payloads.push(PresencePayloadType::Parsed(payload)); } else { - None - }; - payloads.push(match payload { - Some(payload) => PresencePayloadType::Parsed(payload), - None => PresencePayloadType::XML(elem.clone()), - }); + let payload = if let Ok(delay) = delay::parse_delay(elem) { + Some(PresencePayload::Delay(delay)) + } else if let Ok(ecaps2) = ecaps2::parse_ecaps2(elem) { + Some(PresencePayload::ECaps2(ecaps2)) + } else { + None + }; + payloads.push(match payload { + Some(payload) => PresencePayloadType::Parsed(payload), + None => PresencePayloadType::XML(elem.clone()), + }); + } } Ok(Presence { from: from, @@ -137,7 +144,12 @@ pub fn parse_presence(root: &Element) -> Result { pub fn serialise_payload(payload: &PresencePayload) -> Element { match *payload { - PresencePayload::Status(ref status) => status::serialise(status), + PresencePayload::Status(ref status) => { + Element::builder("status") + .ns(ns::JABBER_CLIENT) + .append(status.to_owned()) + .build() + }, PresencePayload::Delay(ref delay) => delay::serialise(delay), PresencePayload::ECaps2(ref ecaps2) => ecaps2::serialise(ecaps2), } @@ -164,7 +176,9 @@ pub fn serialise(presence: &Presence) -> Element { #[cfg(test)] mod tests { use minidom::Element; + use error::Error; use presence; + use ns; #[test] fn test_simple() { @@ -190,4 +204,68 @@ mod tests { let elem2 = presence::serialise(&presence); assert_eq!(elem, elem2); } + + #[test] + fn test_status() { + let elem: Element = "".parse().unwrap(); + let presence = presence::parse_presence(&elem).unwrap(); + assert_eq!(presence.payloads.len(), 1); + match presence.payloads[0] { + presence::PresencePayloadType::Parsed(presence::PresencePayload::Status(ref status)) => { + assert_eq!(*status, presence::Status::from("")); + }, + _ => panic!("Failed to parse status presence."), + } + } + + #[test] + fn test_unknown_child() { + let elem: Element = "".parse().unwrap(); + let presence = presence::parse_presence(&elem).unwrap(); + if let presence::PresencePayloadType::XML(ref payload) = presence.payloads[0] { + assert!(payload.is("test", "invalid")); + } else { + panic!("Did successfully parse an invalid element."); + } + } + + #[test] + #[ignore] + fn test_invalid_status_child() { + let elem: Element = "".parse().unwrap(); + let error = presence::parse_presence(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in status element."); + } + + #[test] + #[ignore] + fn test_invalid_attribute() { + let elem: Element = "".parse().unwrap(); + let error = presence::parse_presence(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in status element."); + } + + #[test] + fn test_serialise_status() { + let status = presence::Status::from("Hello world!"); + let payloads = vec!(presence::PresencePayloadType::Parsed(presence::PresencePayload::Status(status))); + let presence = presence::Presence { + from: None, + to: None, + id: None, + type_: presence::PresenceType::Unavailable, + payloads: payloads, + }; + let elem = presence::serialise(&presence); + assert!(elem.is("presence", ns::JABBER_CLIENT)); + assert!(elem.children().collect::>()[0].is("status", ns::JABBER_CLIENT)); + } } diff --git a/src/status.rs b/src/status.rs deleted file mode 100644 index 66bde73e8ff3348838d2b66f69a3caac7d401b33..0000000000000000000000000000000000000000 --- a/src/status.rs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) 2017 Emmanuel Gil Peyrot -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -use minidom::Element; - -use error::Error; - -use ns; - -pub type Status = String; - -pub fn parse_status(root: &Element) -> Result { - // TODO: also support components and servers. - if !root.is("status", ns::JABBER_CLIENT) { - return Err(Error::ParseError("This is not a status element.")); - } - for _ in root.children() { - return Err(Error::ParseError("Unknown child in status element.")); - } - Ok(root.text()) -} - -pub fn serialise(status: &Status) -> Element { - Element::builder("status") - .ns(ns::JABBER_CLIENT) - .append(status.to_owned()) - .build() -} - -#[cfg(test)] -mod tests { - use minidom::Element; - use error::Error; - use status; - use ns; - - #[test] - fn test_simple() { - let elem: Element = "".parse().unwrap(); - status::parse_status(&elem).unwrap(); - } - - #[test] - fn test_invalid() { - let elem: Element = "".parse().unwrap(); - let error = status::parse_status(&elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "This is not a status element."); - } - - #[test] - fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); - let error = status::parse_status(&elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown child in status element."); - } - - #[test] - #[ignore] - fn test_invalid_attribute() { - let elem: Element = "".parse().unwrap(); - let error = status::parse_status(&elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown attribute in status element."); - } - - #[test] - fn test_serialise() { - let status = status::Status::from("Hello world!"); - let elem = status::serialise(&status); - assert!(elem.is("status", ns::JABBER_CLIENT)); - } -} From 4ec3898c2ff4ffaba27a639904ff71d8bfd45e8e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 30 Apr 2017 21:03:04 +0100 Subject: [PATCH 114/698] presence: Implement show parsing. --- src/presence.rs | 83 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 3 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index cafb7f6e92c8409ff6ec3476c51fa154faca2209..ad6301ebc729eef58e6bdfd7709dc36900c75e83 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -6,8 +6,8 @@ use std::str::FromStr; -use minidom::Element; -use minidom::IntoAttributeValue; +use minidom::{Element, IntoElements, IntoAttributeValue}; +use minidom::convert::ElementEmitter; use jid::Jid; @@ -18,11 +18,32 @@ use ns; use delay; use ecaps2; +#[derive(Debug, Clone, PartialEq)] +pub enum Show { + Away, + Chat, + Dnd, + Xa, +} + +impl IntoElements for Show { + fn into_elements(self, emitter: &mut ElementEmitter) { + let elem = Element::builder(match self { + Show::Away => "away", + Show::Chat => "chat", + Show::Dnd => "dnd", + Show::Xa => "xa", + }).build(); + emitter.append_child(elem); + } +} + pub type Status = String; /// Lists every known payload of a ``. #[derive(Debug, Clone)] pub enum PresencePayload { + Show(Show), Status(Status), Delay(delay::Delay), ECaps2(ecaps2::ECaps2), @@ -113,7 +134,20 @@ pub fn parse_presence(root: &Element) -> Result { }; let mut payloads = vec!(); for elem in root.children() { - if elem.is("status", ns::JABBER_CLIENT) { + if elem.is("show", ns::JABBER_CLIENT) { + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in show element.")); + } + let payload = PresencePayload::Show(match elem.text().as_ref() { + "away" => Show::Away, + "chat" => Show::Chat, + "dnd" => Show::Dnd, + "xa" => Show::Xa, + + _ => return Err(Error::ParseError("Invalid value for show.")), + }); + payloads.push(PresencePayloadType::Parsed(payload)); + } else if elem.is("status", ns::JABBER_CLIENT) { for _ in elem.children() { return Err(Error::ParseError("Unknown child in status element.")); } @@ -144,6 +178,12 @@ pub fn parse_presence(root: &Element) -> Result { pub fn serialise_payload(payload: &PresencePayload) -> Element { match *payload { + PresencePayload::Show(ref show) => { + Element::builder("status") + .ns(ns::JABBER_CLIENT) + .append(show.to_owned()) + .build() + }, PresencePayload::Status(ref status) => { Element::builder("status") .ns(ns::JABBER_CLIENT) @@ -205,6 +245,43 @@ mod tests { assert_eq!(elem, elem2); } + #[test] + fn test_show() { + let elem: Element = "chat".parse().unwrap(); + let presence = presence::parse_presence(&elem).unwrap(); + assert_eq!(presence.payloads.len(), 1); + match presence.payloads[0] { + presence::PresencePayloadType::Parsed(presence::PresencePayload::Show(ref show)) => { + assert_eq!(*show, presence::Show::Chat); + }, + _ => panic!("Failed to parse show presence."), + } + } + + #[test] + fn test_missing_show_value() { + // "online" used to be a pretty common mistake. + let elem: Element = "".parse().unwrap(); + let error = presence::parse_presence(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Invalid value for show."); + } + + #[test] + fn test_invalid_show() { + // "online" used to be a pretty common mistake. + let elem: Element = "online".parse().unwrap(); + let error = presence::parse_presence(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Invalid value for show."); + } + #[test] fn test_status() { let elem: Element = "".parse().unwrap(); From 21398447e7fe96c287aeed3bc448857fa80b24af Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 30 Apr 2017 21:29:55 +0100 Subject: [PATCH 115/698] presence: Implement priority parsing. --- src/presence.rs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/presence.rs b/src/presence.rs index ad6301ebc729eef58e6bdfd7709dc36900c75e83..3f298333c99592d203e08ea9534ee522c57b8621 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -40,11 +40,14 @@ impl IntoElements for Show { pub type Status = String; +pub type Priority = i8; + /// Lists every known payload of a ``. #[derive(Debug, Clone)] pub enum PresencePayload { Show(Show), Status(Status), + Priority(Priority), Delay(delay::Delay), ECaps2(ecaps2::ECaps2), } @@ -153,6 +156,13 @@ pub fn parse_presence(root: &Element) -> Result { } let payload = PresencePayload::Status(elem.text()); payloads.push(PresencePayloadType::Parsed(payload)); + } else if elem.is("priority", ns::JABBER_CLIENT) { + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in priority element.")); + } + let priority = Priority::from_str(elem.text().as_ref())?; + let payload = PresencePayload::Priority(priority); + payloads.push(PresencePayloadType::Parsed(payload)); } else { let payload = if let Ok(delay) = delay::parse_delay(elem) { Some(PresencePayload::Delay(delay)) @@ -190,6 +200,12 @@ pub fn serialise_payload(payload: &PresencePayload) -> Element { .append(status.to_owned()) .build() }, + PresencePayload::Priority(ref priority) => { + Element::builder("status") + .ns(ns::JABBER_CLIENT) + .append(format!("{}", priority)) + .build() + }, PresencePayload::Delay(ref delay) => delay::serialise(delay), PresencePayload::ECaps2(ref ecaps2) => ecaps2::serialise(ecaps2), } @@ -295,6 +311,29 @@ mod tests { } } + #[test] + fn test_priority() { + let elem: Element = "-1".parse().unwrap(); + let presence = presence::parse_presence(&elem).unwrap(); + assert_eq!(presence.payloads.len(), 1); + match presence.payloads[0] { + presence::PresencePayloadType::Parsed(presence::PresencePayload::Priority(ref priority)) => { + assert_eq!(*priority, presence::Priority::from(-1i8)); + }, + _ => panic!("Failed to parse priority."), + } + } + + #[test] + fn test_invalid_priority() { + let elem: Element = "128".parse().unwrap(); + let error = presence::parse_presence(&elem).unwrap_err(); + match error { + Error::ParseIntError(_) => (), + _ => panic!(), + }; + } + #[test] fn test_unknown_child() { let elem: Element = "".parse().unwrap(); From 1909ae33dcd64f729c37bbe53835bf2567b188e1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 30 Apr 2017 22:00:29 +0100 Subject: [PATCH 116/698] presence: make show, statuses and priority first-class elements. --- src/presence.rs | 80 +++++++++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 29 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index 3f298333c99592d203e08ea9534ee522c57b8621..13517929a17fffea7b2f3a5a1be301c614ccfea3 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -118,6 +118,9 @@ pub struct Presence { pub to: Option, pub id: Option, pub type_: PresenceType, + pub show: Option, + pub statuses: Vec, + pub priority: Priority, pub payloads: Vec, } @@ -135,13 +138,19 @@ pub fn parse_presence(root: &Element) -> Result { Some(type_) => type_.parse()?, None => Default::default(), }; + let mut show = None; + let mut statuses = vec!(); + let mut priority = None; let mut payloads = vec!(); for elem in root.children() { if elem.is("show", ns::JABBER_CLIENT) { + if show.is_some() { + return Err(Error::ParseError("More than one show element in a presence.")); + } for _ in elem.children() { return Err(Error::ParseError("Unknown child in show element.")); } - let payload = PresencePayload::Show(match elem.text().as_ref() { + show = Some(match elem.text().as_ref() { "away" => Show::Away, "chat" => Show::Chat, "dnd" => Show::Dnd, @@ -149,20 +158,19 @@ pub fn parse_presence(root: &Element) -> Result { _ => return Err(Error::ParseError("Invalid value for show.")), }); - payloads.push(PresencePayloadType::Parsed(payload)); } else if elem.is("status", ns::JABBER_CLIENT) { for _ in elem.children() { return Err(Error::ParseError("Unknown child in status element.")); } - let payload = PresencePayload::Status(elem.text()); - payloads.push(PresencePayloadType::Parsed(payload)); + statuses.push(elem.text()); } else if elem.is("priority", ns::JABBER_CLIENT) { + if priority.is_some() { + return Err(Error::ParseError("More than one priority element in a presence.")); + } for _ in elem.children() { return Err(Error::ParseError("Unknown child in priority element.")); } - let priority = Priority::from_str(elem.text().as_ref())?; - let payload = PresencePayload::Priority(priority); - payloads.push(PresencePayloadType::Parsed(payload)); + priority = Some(Priority::from_str(elem.text().as_ref())?); } else { let payload = if let Ok(delay) = delay::parse_delay(elem) { Some(PresencePayload::Delay(delay)) @@ -182,6 +190,9 @@ pub fn parse_presence(root: &Element) -> Result { to: to, id: id, type_: type_, + show: show, + statuses: statuses, + priority: priority.unwrap_or(0i8), payloads: payloads, }) } @@ -255,6 +266,9 @@ mod tests { to: None, id: None, type_: presence::PresenceType::Unavailable, + show: None, + statuses: vec!(), + priority: 0i8, payloads: vec!(), }; let elem2 = presence::serialise(&presence); @@ -265,13 +279,8 @@ mod tests { fn test_show() { let elem: Element = "chat".parse().unwrap(); let presence = presence::parse_presence(&elem).unwrap(); - assert_eq!(presence.payloads.len(), 1); - match presence.payloads[0] { - presence::PresencePayloadType::Parsed(presence::PresencePayload::Show(ref show)) => { - assert_eq!(*show, presence::Show::Chat); - }, - _ => panic!("Failed to parse show presence."), - } + assert_eq!(presence.payloads.len(), 0); + assert_eq!(presence.show, Some(presence::Show::Chat)); } #[test] @@ -298,30 +307,40 @@ mod tests { assert_eq!(message, "Invalid value for show."); } + #[test] + fn test_empty_status() { + let elem: Element = "".parse().unwrap(); + let presence = presence::parse_presence(&elem).unwrap(); + assert_eq!(presence.payloads.len(), 0); + assert_eq!(presence.statuses.len(), 1); + assert_eq!(presence.statuses[0], ""); + } + #[test] fn test_status() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "Here!".parse().unwrap(); let presence = presence::parse_presence(&elem).unwrap(); - assert_eq!(presence.payloads.len(), 1); - match presence.payloads[0] { - presence::PresencePayloadType::Parsed(presence::PresencePayload::Status(ref status)) => { - assert_eq!(*status, presence::Status::from("")); - }, - _ => panic!("Failed to parse status presence."), - } + assert_eq!(presence.payloads.len(), 0); + assert_eq!(presence.statuses.len(), 1); + assert_eq!(presence.statuses[0], "Here!"); + } + + #[test] + fn test_multiple_statuses() { + let elem: Element = "Here!Là!".parse().unwrap(); + let presence = presence::parse_presence(&elem).unwrap(); + assert_eq!(presence.payloads.len(), 0); + assert_eq!(presence.statuses.len(), 2); + assert_eq!(presence.statuses[0], "Here!"); + assert_eq!(presence.statuses[1], "Là!"); } #[test] fn test_priority() { let elem: Element = "-1".parse().unwrap(); let presence = presence::parse_presence(&elem).unwrap(); - assert_eq!(presence.payloads.len(), 1); - match presence.payloads[0] { - presence::PresencePayloadType::Parsed(presence::PresencePayload::Priority(ref priority)) => { - assert_eq!(*priority, presence::Priority::from(-1i8)); - }, - _ => panic!("Failed to parse priority."), - } + assert_eq!(presence.payloads.len(), 0); + assert_eq!(presence.priority, -1i8); } #[test] @@ -378,6 +397,9 @@ mod tests { to: None, id: None, type_: presence::PresenceType::Unavailable, + show: None, + statuses: vec!(), + priority: 0i8, payloads: payloads, }; let elem = presence::serialise(&presence); From 0abac5ad2d86f54c3cbdaf498ecec3997c4b5c84 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 30 Apr 2017 22:07:32 +0100 Subject: [PATCH 117/698] presence: Make statuses addressable by their xml:lang. --- src/presence.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index 13517929a17fffea7b2f3a5a1be301c614ccfea3..16c5f0fc6d2e4244d3752e0a78c4d3cab94bd56b 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -5,6 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use std::str::FromStr; +use std::collections::BTreeMap; use minidom::{Element, IntoElements, IntoAttributeValue}; use minidom::convert::ElementEmitter; @@ -38,6 +39,7 @@ impl IntoElements for Show { } } +pub type Lang = String; pub type Status = String; pub type Priority = i8; @@ -119,7 +121,7 @@ pub struct Presence { pub id: Option, pub type_: PresenceType, pub show: Option, - pub statuses: Vec, + pub statuses: BTreeMap, pub priority: Priority, pub payloads: Vec, } @@ -139,7 +141,7 @@ pub fn parse_presence(root: &Element) -> Result { None => Default::default(), }; let mut show = None; - let mut statuses = vec!(); + let mut statuses = BTreeMap::new(); let mut priority = None; let mut payloads = vec!(); for elem in root.children() { @@ -162,7 +164,8 @@ pub fn parse_presence(root: &Element) -> Result { for _ in elem.children() { return Err(Error::ParseError("Unknown child in status element.")); } - statuses.push(elem.text()); + let lang = elem.attr("xml:lang").unwrap_or("").to_owned(); + statuses.insert(lang, elem.text()); } else if elem.is("priority", ns::JABBER_CLIENT) { if priority.is_some() { return Err(Error::ParseError("More than one priority element in a presence.")); @@ -242,6 +245,7 @@ pub fn serialise(presence: &Presence) -> Element { #[cfg(test)] mod tests { + use std::collections::BTreeMap; use minidom::Element; use error::Error; use presence; @@ -267,7 +271,7 @@ mod tests { id: None, type_: presence::PresenceType::Unavailable, show: None, - statuses: vec!(), + statuses: BTreeMap::new(), priority: 0i8, payloads: vec!(), }; @@ -313,7 +317,7 @@ mod tests { let presence = presence::parse_presence(&elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.statuses.len(), 1); - assert_eq!(presence.statuses[0], ""); + assert_eq!(presence.statuses[""], ""); } #[test] @@ -322,7 +326,7 @@ mod tests { let presence = presence::parse_presence(&elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.statuses.len(), 1); - assert_eq!(presence.statuses[0], "Here!"); + assert_eq!(presence.statuses[""], "Here!"); } #[test] @@ -331,8 +335,8 @@ mod tests { let presence = presence::parse_presence(&elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.statuses.len(), 2); - assert_eq!(presence.statuses[0], "Here!"); - assert_eq!(presence.statuses[1], "Là!"); + assert_eq!(presence.statuses[""], "Here!"); + assert_eq!(presence.statuses["fr"], "Là!"); } #[test] @@ -398,7 +402,7 @@ mod tests { id: None, type_: presence::PresenceType::Unavailable, show: None, - statuses: vec!(), + statuses: BTreeMap::new(), priority: 0i8, payloads: payloads, }; From e03a5a89e78c30951dbfb584c8f2e54adc7a43c3 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 30 Apr 2017 22:45:39 +0100 Subject: [PATCH 118/698] presence: Prevent two statuses from having the same lang. --- src/presence.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/presence.rs b/src/presence.rs index 16c5f0fc6d2e4244d3752e0a78c4d3cab94bd56b..fb26d94f3b4c18c3439b3ee200b3d1055a981004 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -165,7 +165,9 @@ pub fn parse_presence(root: &Element) -> Result { return Err(Error::ParseError("Unknown child in status element.")); } let lang = elem.attr("xml:lang").unwrap_or("").to_owned(); - statuses.insert(lang, elem.text()); + if let Some(_) = statuses.insert(lang, elem.text()) { + return Err(Error::ParseError("Status element present twice for the same xml:lang.")); + } } else if elem.is("priority", ns::JABBER_CLIENT) { if priority.is_some() { return Err(Error::ParseError("More than one priority element in a presence.")); @@ -339,6 +341,17 @@ mod tests { assert_eq!(presence.statuses["fr"], "Là!"); } + #[test] + fn test_invalid_multiple_statuses() { + let elem: Element = "Here!Là!".parse().unwrap(); + let error = presence::parse_presence(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Status element present twice for the same xml:lang."); + } + #[test] fn test_priority() { let elem: Element = "-1".parse().unwrap(); From 24658859753cbd094f9d585bc2f3fc04455d5396 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 1 May 2017 01:24:45 +0100 Subject: [PATCH 119/698] Add a stanza error parser and serialiser. --- src/lib.rs | 2 + src/ns.rs | 2 + src/stanza_error.rs | 275 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 279 insertions(+) create mode 100644 src/stanza_error.rs diff --git a/src/lib.rs b/src/lib.rs index 0a5eac0ad7150d5d6954fbc86b146b1b8ea7f1b2..67a335fc6f3342481831afe4cc387d6af5054518 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,6 +32,8 @@ pub mod message; pub mod presence; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod iq; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core +pub mod stanza_error; /// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence pub mod body; diff --git a/src/ns.rs b/src/ns.rs index 495c2856681576a1f605128bc7973efed83125f2..8e7c08b1ae6e2443838d13bac259554a4c27a1f1 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -6,6 +6,8 @@ /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub const JABBER_CLIENT: &'static str = "jabber:client"; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core +pub const XMPP_STANZAS: &'static str = "urn:ietf:params:xml:ns:xmpp-stanzas"; /// XEP-0004: Data Forms pub const DATA_FORMS: &'static str = "jabber:x:data"; diff --git a/src/stanza_error.rs b/src/stanza_error.rs new file mode 100644 index 0000000000000000000000000000000000000000..0a1eb42566a042ab0198ed8ead421cfb10b8b1d3 --- /dev/null +++ b/src/stanza_error.rs @@ -0,0 +1,275 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::str::FromStr; +use std::collections::BTreeMap; + +use minidom::Element; + +use error::Error; +use jid::Jid; +use ns; + +#[derive(Debug, Clone, PartialEq)] +pub enum ErrorType { + Auth, + Cancel, + Continue, + Modify, + Wait, +} + +impl FromStr for ErrorType { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "auth" => ErrorType::Auth, + "cancel" => ErrorType::Cancel, + "continue" => ErrorType::Continue, + "modify" => ErrorType::Modify, + "wait" => ErrorType::Wait, + + _ => return Err(Error::ParseError("Unknown error type.")), + }) + } +} + +impl From for String { + fn from(type_: ErrorType) -> String { + String::from(match type_ { + ErrorType::Auth => "auth", + ErrorType::Cancel => "cancel", + ErrorType::Continue => "continue", + ErrorType::Modify => "modify", + ErrorType::Wait => "wait", + }) + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum DefinedCondition { + BadRequest, + Conflict, + FeatureNotImplemented, + Forbidden, + Gone, + InternalServerError, + ItemNotFound, + JidMalformed, + NotAcceptable, + NotAllowed, + NotAuthorized, + PolicyViolation, + RecipientUnavailable, + Redirect, + RegistrationRequired, + RemoteServerNotFound, + RemoteServerTimeout, + ResourceConstraint, + ServiceUnavailable, + SubscriptionRequired, + UndefinedCondition, + UnexpectedRequest, +} + +impl FromStr for DefinedCondition { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "bad-request" => DefinedCondition::BadRequest, + "conflict" => DefinedCondition::Conflict, + "feature-not-implemented" => DefinedCondition::FeatureNotImplemented, + "forbidden" => DefinedCondition::Forbidden, + "gone" => DefinedCondition::Gone, + "internal-server-error" => DefinedCondition::InternalServerError, + "item-not-found" => DefinedCondition::ItemNotFound, + "jid-malformed" => DefinedCondition::JidMalformed, + "not-acceptable" => DefinedCondition::NotAcceptable, + "not-allowed" => DefinedCondition::NotAllowed, + "not-authorized" => DefinedCondition::NotAuthorized, + "policy-violation" => DefinedCondition::PolicyViolation, + "recipient-unavailable" => DefinedCondition::RecipientUnavailable, + "redirect" => DefinedCondition::Redirect, + "registration-required" => DefinedCondition::RegistrationRequired, + "remote-server-not-found" => DefinedCondition::RemoteServerNotFound, + "remote-server-timeout" => DefinedCondition::RemoteServerTimeout, + "resource-constraint" => DefinedCondition::ResourceConstraint, + "service-unavailable" => DefinedCondition::ServiceUnavailable, + "subscription-required" => DefinedCondition::SubscriptionRequired, + "undefined-condition" => DefinedCondition::UndefinedCondition, + "unexpected-request" => DefinedCondition::UnexpectedRequest, + + _ => return Err(Error::ParseError("Unknown defined-condition.")), + }) + } +} + +impl From for String { + fn from(defined_condition: DefinedCondition) -> String { + String::from(match defined_condition { + DefinedCondition::BadRequest => "bad-request", + DefinedCondition::Conflict => "conflict", + DefinedCondition::FeatureNotImplemented => "feature-not-implemented", + DefinedCondition::Forbidden => "forbidden", + DefinedCondition::Gone => "gone", + DefinedCondition::InternalServerError => "internal-server-error", + DefinedCondition::ItemNotFound => "item-not-found", + DefinedCondition::JidMalformed => "jid-malformed", + DefinedCondition::NotAcceptable => "not-acceptable", + DefinedCondition::NotAllowed => "not-allowed", + DefinedCondition::NotAuthorized => "not-authorized", + DefinedCondition::PolicyViolation => "policy-violation", + DefinedCondition::RecipientUnavailable => "recipient-unavailable", + DefinedCondition::Redirect => "redirect", + DefinedCondition::RegistrationRequired => "registration-required", + DefinedCondition::RemoteServerNotFound => "remote-server-not-found", + DefinedCondition::RemoteServerTimeout => "remote-server-timeout", + DefinedCondition::ResourceConstraint => "resource-constraint", + DefinedCondition::ServiceUnavailable => "service-unavailable", + DefinedCondition::SubscriptionRequired => "subscription-required", + DefinedCondition::UndefinedCondition => "undefined-condition", + DefinedCondition::UnexpectedRequest => "unexpected-request", + }) + } +} + +pub type Lang = String; + +#[derive(Debug, Clone)] +pub struct StanzaError { + pub type_: ErrorType, + pub by: Option, + pub defined_condition: DefinedCondition, + pub texts: BTreeMap, + pub other: Option, +} + +pub fn parse_stanza_error(root: &Element) -> Result { + if !root.is("error", ns::JABBER_CLIENT) { + return Err(Error::ParseError("This is not an error element.")); + } + + let type_ = root.attr("type") + .ok_or(Error::ParseError("Error must have a 'type' attribute."))? + .parse()?; + let by = root.attr("by") + .and_then(|by| by.parse().ok()); + let mut defined_condition = None; + let mut texts = BTreeMap::new(); + let mut other = None; + + for child in root.children() { + if child.is("text", ns::XMPP_STANZAS) { + for _ in child.children() { + return Err(Error::ParseError("Unknown element in error text.")); + } + let lang = child.attr("xml:lang").unwrap_or("").to_owned(); + if let Some(_) = texts.insert(lang, child.text()) { + return Err(Error::ParseError("Text element present twice for the same xml:lang.")); + } + } else if child.ns() == Some(ns::XMPP_STANZAS) { + if defined_condition.is_some() { + return Err(Error::ParseError("Error must not have more than one defined-condition.")); + } + for _ in child.children() { + return Err(Error::ParseError("Unknown element in defined-condition.")); + } + let condition = DefinedCondition::from_str(child.name())?; + defined_condition = Some(condition); + } else { + if other.is_some() { + return Err(Error::ParseError("Error must not have more than one other element.")); + } + other = Some(child.clone()); + } + } + + if defined_condition.is_none() { + return Err(Error::ParseError("Error must have a defined-condition.")); + } + let defined_condition = defined_condition.unwrap(); + + Ok(StanzaError { + type_: type_, + by: by, + defined_condition: defined_condition, + texts: texts, + other: other, + }) +} + +pub fn serialise(error: &StanzaError) -> Element { + let mut root = Element::builder("error") + .ns(ns::JABBER_CLIENT) + .attr("type", String::from(error.type_.clone())) + .attr("by", match error.by { + Some(ref by) => Some(String::from(by.clone())), + None => None, + }) + .append(Element::builder(error.defined_condition.clone()) + .ns(ns::XMPP_STANZAS) + .build()) + .build(); + for (lang, text) in error.texts.clone() { + let elem = Element::builder("text") + .ns(ns::XMPP_STANZAS) + .attr("xml:lang", lang) + .append(text) + .build(); + root.append_child(elem); + } + if let Some(ref other) = error.other { + root.append_child(other.clone()); + } + root +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use stanza_error; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let error = stanza_error::parse_stanza_error(&elem).unwrap(); + assert_eq!(error.type_, stanza_error::ErrorType::Cancel); + assert_eq!(error.defined_condition, stanza_error::DefinedCondition::UndefinedCondition); + } + + #[test] + fn test_invalid_type() { + let elem: Element = "".parse().unwrap(); + let error = stanza_error::parse_stanza_error(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Error must have a 'type' attribute."); + + let elem: Element = "".parse().unwrap(); + let error = stanza_error::parse_stanza_error(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown error type."); + } + + #[test] + fn test_invalid_condition() { + let elem: Element = "".parse().unwrap(); + let error = stanza_error::parse_stanza_error(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Error must have a defined-condition."); + } +} From 3dd0eb3a9b2adfc3afede97b273633d7513f6cae Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 1 May 2017 01:02:35 +0100 Subject: [PATCH 120/698] presence: Wire up stanza_error. --- src/presence.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/presence.rs b/src/presence.rs index fb26d94f3b4c18c3439b3ee200b3d1055a981004..3b3c1faa2fe4793bddc40c75a80c725ead85ee59 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -16,6 +16,7 @@ use error::Error; use ns; +use stanza_error; use delay; use ecaps2; @@ -50,6 +51,7 @@ pub enum PresencePayload { Show(Show), Status(Status), Priority(Priority), + StanzaError(stanza_error::StanzaError), Delay(delay::Delay), ECaps2(ecaps2::ECaps2), } @@ -177,7 +179,9 @@ pub fn parse_presence(root: &Element) -> Result { } priority = Some(Priority::from_str(elem.text().as_ref())?); } else { - let payload = if let Ok(delay) = delay::parse_delay(elem) { + let payload = if let Ok(stanza_error) = stanza_error::parse_stanza_error(elem) { + Some(PresencePayload::StanzaError(stanza_error)) + } else if let Ok(delay) = delay::parse_delay(elem) { Some(PresencePayload::Delay(delay)) } else if let Ok(ecaps2) = ecaps2::parse_ecaps2(elem) { Some(PresencePayload::ECaps2(ecaps2)) @@ -222,6 +226,7 @@ pub fn serialise_payload(payload: &PresencePayload) -> Element { .append(format!("{}", priority)) .build() }, + PresencePayload::StanzaError(ref stanza_error) => stanza_error::serialise(stanza_error), PresencePayload::Delay(ref delay) => delay::serialise(delay), PresencePayload::ECaps2(ref ecaps2) => ecaps2::serialise(ecaps2), } From 42abbe2927ebd67aa2b9effeb92931c4475274e4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 1 May 2017 01:04:15 +0100 Subject: [PATCH 121/698] message: Wire up stanza_error. --- src/message.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/message.rs b/src/message.rs index a88db4c9d7504c8fb0ba1b211b5bb514b1306005..9d3ea6fe26bd7e5e51f0e824c397d09d9401b365 100644 --- a/src/message.rs +++ b/src/message.rs @@ -16,6 +16,7 @@ use error::Error; use ns; use body; +use stanza_error; use chatstates; use receipts; use delay; @@ -27,6 +28,7 @@ use eme; #[derive(Debug, Clone)] pub enum MessagePayload { Body(body::Body), + StanzaError(stanza_error::StanzaError), ChatState(chatstates::ChatState), Receipt(receipts::Receipt), Delay(delay::Delay), @@ -111,6 +113,8 @@ pub fn parse_message(root: &Element) -> Result { for elem in root.children() { let payload = if let Ok(body) = body::parse_body(elem) { Some(MessagePayload::Body(body)) + } else if let Ok(stanza_error) = stanza_error::parse_stanza_error(elem) { + Some(MessagePayload::StanzaError(stanza_error)) } else if let Ok(chatstate) = chatstates::parse_chatstate(elem) { Some(MessagePayload::ChatState(chatstate)) } else if let Ok(receipt) = receipts::parse_receipt(elem) { @@ -143,6 +147,7 @@ pub fn parse_message(root: &Element) -> Result { pub fn serialise_payload(payload: &MessagePayload) -> Element { match *payload { MessagePayload::Body(ref body) => body::serialise(body), + MessagePayload::StanzaError(ref stanza_error) => stanza_error::serialise(stanza_error), MessagePayload::Attention(ref attention) => attention::serialise(attention), MessagePayload::ChatState(ref chatstate) => chatstates::serialise(chatstate), MessagePayload::Receipt(ref receipt) => receipts::serialise(receipt), From f3b55350adbf5fe25ac2e6ee0087967216499de6 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 1 May 2017 01:23:56 +0100 Subject: [PATCH 122/698] iq: Wire up stanza_error. --- src/iq.rs | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index 645c4ba0f96e97d0ad066ab675f6545c5e71642d..cf97055343d4c9af49b89a135f918a350a623026 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -14,6 +14,7 @@ use error::Error; use ns; +use stanza_error; use disco; use ibb; use jingle; @@ -39,7 +40,7 @@ pub enum IqType { Get(IqPayloadType), Set(IqPayloadType), Result(Option), - Error(IqPayloadType), + Error(stanza_error::StanzaError), } impl IntoAttributeValue for IqType { @@ -77,13 +78,17 @@ pub fn parse_iq(root: &Element) -> Result { }; let mut payload = None; + let mut error_payload = None; for elem in root.children() { if payload.is_some() { return Err(Error::ParseError("Wrong number of children in iq element.")); } if type_ == "error" { if elem.is("error", ns::JABBER_CLIENT) { - payload = Some(IqPayloadType::XML(elem.clone())); + if error_payload.is_some() { + return Err(Error::ParseError("Wrong number of children in iq element.")); + } + error_payload = Some(stanza_error::parse_stanza_error(elem)?); } else if root.children().collect::>().len() != 2 { return Err(Error::ParseError("Wrong number of children in iq element.")); } @@ -126,7 +131,7 @@ pub fn parse_iq(root: &Element) -> Result { IqType::Result(None) } } else if type_ == "error" { - if let Some(payload) = payload.clone() { + if let Some(payload) = error_payload.clone() { IqType::Error(payload.clone()) } else { return Err(Error::ParseError("Wrong number of children in iq element.")); @@ -163,13 +168,12 @@ pub fn serialise(iq: &Iq) -> Element { let elem = match iq.payload.clone() { IqType::Get(IqPayloadType::XML(elem)) | IqType::Set(IqPayloadType::XML(elem)) - | IqType::Result(Some(IqPayloadType::XML(elem))) - | IqType::Error(IqPayloadType::XML(elem)) => elem, + | IqType::Result(Some(IqPayloadType::XML(elem))) => elem, + IqType::Error(error) => stanza_error::serialise(&error), IqType::Get(IqPayloadType::Parsed(payload)) | IqType::Set(IqPayloadType::Parsed(payload)) | IqType::Result(Some(IqPayloadType::Parsed(payload))) => serialise_payload(&payload), IqType::Result(None) => return stanza, - _ => panic!(), }; stanza.append_child(elem); stanza @@ -180,6 +184,7 @@ mod tests { use minidom::Element; use error::Error; use iq; + use stanza_error; use disco; #[test] @@ -269,10 +274,16 @@ mod tests { assert_eq!(iq.from, None); assert_eq!(iq.to, None); assert_eq!(iq.id, None); - assert!(match iq.payload { - iq::IqType::Error(iq::IqPayloadType::XML(element)) => element == error, - _ => false, - }); + match iq.payload { + iq::IqType::Error(element) => { + assert_eq!(element.type_, stanza_error::ErrorType::Cancel); + assert_eq!(element.by, None); + assert_eq!(element.defined_condition, stanza_error::DefinedCondition::ServiceUnavailable); + assert_eq!(element.texts.len(), 0); + assert_eq!(element.other, None); + }, + _ => panic!(), + } } #[test] From ed458ba694458ebca05442b5e0045d720c35f157 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 1 May 2017 01:39:52 +0100 Subject: [PATCH 123/698] Add a ChangeLog file, to let users know about the changes between major versions. --- ChangeLog | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 ChangeLog diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000000000000000000000000000000000000..83fad57c8c820b87e4c3b31269e9ffbaf8d55ca6 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,13 @@ +Version XXX: +2017-0X-XX Emmanuel Gil Peyrot + * New parsers/serialisers: + - Stanza error, as per RFC 6120 §8.3. + * Incompatible changes: + - Presence has got an overhaul, it now hosts show, statuses and + priority in its struct. The status module has also been + dropped. + - Iq now gets a proper StanzaError when the type is error. + +Version 0.1.0: +2017-04-29 Emmanuel Gil Peyrot + * Implement many extensions. From 7b6d444f034f0b67a5f47737f51b8b381e0a977e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 1 May 2017 01:50:18 +0100 Subject: [PATCH 124/698] delay: Use Jid for from attribute. --- src/delay.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index 8e8e382ea2276189c27c2506445492cb29a5550e..06c980f642ebde26d682e885768b16da9aa75b5d 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -8,12 +8,13 @@ use minidom::{Element, IntoElements}; use minidom::convert::ElementEmitter; use error::Error; +use jid::Jid; use ns; #[derive(Debug, Clone)] pub struct Delay { - pub from: Option, + pub from: Option, pub stamp: String, pub data: Option, } @@ -41,7 +42,7 @@ pub fn parse_delay(root: &Element) -> Result { pub fn serialise(delay: &Delay) -> Element { Element::builder("delay") .ns(ns::DELAY) - .attr("from", delay.from.clone()) + .attr("from", delay.from.clone().and_then(|value| Some(String::from(value)))) .attr("stamp", delay.stamp.clone()) .append(delay.data.clone()) .build() @@ -56,15 +57,17 @@ impl IntoElements for Delay { #[cfg(test)] mod tests { + use std::str::FromStr; use minidom::Element; use error::Error; + use jid::Jid; use delay; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); let delay = delay::parse_delay(&elem).unwrap(); - assert_eq!(delay.from, Some(String::from("capulet.com"))); + assert_eq!(delay.from, Some(Jid::from_str("capulet.com").unwrap())); assert_eq!(delay.stamp, "2002-09-10T23:08:25Z"); assert_eq!(delay.data, None); } @@ -107,7 +110,7 @@ mod tests { fn test_serialise_data() { let elem: Element = "Reason".parse().unwrap(); let delay = delay::Delay { - from: Some(String::from("juliet@example.org")), + from: Some(Jid::from_str("juliet@example.org").unwrap()), stamp: "2002-09-10T23:08:25Z".to_owned(), data: Some(String::from("Reason")), }; From c0b7c9da884555e4b1863e6f7f6a37a7f658d1d8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 1 May 2017 01:50:38 +0100 Subject: [PATCH 125/698] iq: Remove unused variable causing a warning. --- src/iq.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index cf97055343d4c9af49b89a135f918a350a623026..c7728ae2cc05e479f3c59d9fff6288fdf87302d1 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -268,19 +268,16 @@ mod tests { ".parse().unwrap(); let iq = iq::parse_iq(&elem).unwrap(); - let error: Element = " - - ".parse().unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); assert_eq!(iq.id, None); match iq.payload { - iq::IqType::Error(element) => { - assert_eq!(element.type_, stanza_error::ErrorType::Cancel); - assert_eq!(element.by, None); - assert_eq!(element.defined_condition, stanza_error::DefinedCondition::ServiceUnavailable); - assert_eq!(element.texts.len(), 0); - assert_eq!(element.other, None); + iq::IqType::Error(error) => { + assert_eq!(error.type_, stanza_error::ErrorType::Cancel); + assert_eq!(error.by, None); + assert_eq!(error.defined_condition, stanza_error::DefinedCondition::ServiceUnavailable); + assert_eq!(error.texts.len(), 0); + assert_eq!(error.other, None); }, _ => panic!(), } From 765e8c333308d2dd5291b15d3a9625512274a2c3 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 1 May 2017 23:49:44 +0100 Subject: [PATCH 126/698] attention: Replace parse_* and serialise with TryFrom and Into. --- src/attention.rs | 43 +++++++++++++++++++++++++++---------------- src/lib.rs | 2 ++ src/message.rs | 9 +++++---- 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/attention.rs b/src/attention.rs index 2f12545ea436cbea67d58fcc9f6c4a449d5dc18c..21f8e0b5d29c3411bb3a1c6cc47df8b184308088 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use error::Error; @@ -13,38 +15,45 @@ use ns; #[derive(Debug, Clone)] pub struct Attention; -pub fn parse_attention(root: &Element) -> Result { - if !root.is("attention", ns::ATTENTION) { - return Err(Error::ParseError("This is not an attention element.")); - } - for _ in root.children() { - return Err(Error::ParseError("Unknown child in attention element.")); +impl<'a> TryFrom<&'a Element> for Attention { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if !elem.is("attention", ns::ATTENTION) { + return Err(Error::ParseError("This is not an attention element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in attention element.")); + } + Ok(Attention) } - Ok(Attention) } -pub fn serialise(_: &Attention) -> Element { - Element::builder("attention") - .ns(ns::ATTENTION) - .build() +impl<'a> Into for &'a Attention { + fn into(self) -> Element { + Element::builder("attention") + .ns(ns::ATTENTION) + .build() + } } #[cfg(test)] mod tests { + use std::convert::TryFrom; use minidom::Element; use error::Error; - use attention; + use super::Attention; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - attention::parse_attention(&elem).unwrap(); + Attention::try_from(&elem).unwrap(); } #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = attention::parse_attention(&elem).unwrap_err(); + let error = Attention::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -55,8 +64,10 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let attention = attention::Attention; - let elem2 = attention::serialise(&attention); + let attention = Attention; + let elem2: Element = (&attention).into(); + let elem3: Element = (&attention).into(); assert_eq!(elem, elem2); + assert_eq!(elem2, elem3); } } diff --git a/src/lib.rs b/src/lib.rs index 67a335fc6f3342481831afe4cc387d6af5054518..3076fccfa3b346d63150393ff1317654da90bdeb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#![feature(try_from)] + extern crate minidom; extern crate jid; extern crate base64; diff --git a/src/message.rs b/src/message.rs index 9d3ea6fe26bd7e5e51f0e824c397d09d9401b365..3550e810c1159f82891f75707cc116074794dbd9 100644 --- a/src/message.rs +++ b/src/message.rs @@ -4,6 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; use std::str::FromStr; use minidom::{Element, IntoElements, IntoAttributeValue}; @@ -20,7 +21,7 @@ use stanza_error; use chatstates; use receipts; use delay; -use attention; +use attention::Attention; use message_correct; use eme; @@ -32,7 +33,7 @@ pub enum MessagePayload { ChatState(chatstates::ChatState), Receipt(receipts::Receipt), Delay(delay::Delay), - Attention(attention::Attention), + Attention(Attention), MessageCorrect(message_correct::Replace), ExplicitMessageEncryption(eme::ExplicitMessageEncryption), } @@ -121,7 +122,7 @@ pub fn parse_message(root: &Element) -> Result { Some(MessagePayload::Receipt(receipt)) } else if let Ok(delay) = delay::parse_delay(elem) { Some(MessagePayload::Delay(delay)) - } else if let Ok(attention) = attention::parse_attention(elem) { + } else if let Ok(attention) = Attention::try_from(elem) { Some(MessagePayload::Attention(attention)) } else if let Ok(replace) = message_correct::parse_replace(elem) { Some(MessagePayload::MessageCorrect(replace)) @@ -148,7 +149,7 @@ pub fn serialise_payload(payload: &MessagePayload) -> Element { match *payload { MessagePayload::Body(ref body) => body::serialise(body), MessagePayload::StanzaError(ref stanza_error) => stanza_error::serialise(stanza_error), - MessagePayload::Attention(ref attention) => attention::serialise(attention), + MessagePayload::Attention(ref attention) => attention.into(), MessagePayload::ChatState(ref chatstate) => chatstates::serialise(chatstate), MessagePayload::Receipt(ref receipt) => receipts::serialise(receipt), MessagePayload::Delay(ref delay) => delay::serialise(delay), From 1cea1987a21c901e1438f8cb8b30a5b0afe1efff Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 4 May 2017 01:20:01 +0100 Subject: [PATCH 127/698] Cargo.toml: Remove superfluous license-file. --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index bef0ab8f8a820eaa3bb5131f585c07aa0704f6d6..3f13f303aa98175821340f40d01ef13eca23a404 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,6 @@ repository = "https://hg.linkmauve.fr/xmpp-parsers" keywords = ["xmpp"] categories = ["parsing", "network-programming"] license = "MPL-2.0" -license-file = "LICENSE" [dependencies] minidom = "0.2.0" From 29725b9d4d553d499fc3686acb39ab2e0d6de572 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 4 May 2017 01:20:22 +0100 Subject: [PATCH 128/698] Update to minidom 0.3.0. --- Cargo.toml | 2 +- src/delay.rs | 3 +-- src/jingle_ft.rs | 3 +-- src/message.rs | 3 +-- src/presence.rs | 3 +-- 5 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3f13f303aa98175821340f40d01ef13eca23a404..ea7eecef2b81b19673c0ad916bafc0032d0341c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ categories = ["parsing", "network-programming"] license = "MPL-2.0" [dependencies] -minidom = "0.2.0" +minidom = "0.3.0" jid = "0.2.0" base64 = "0.5.0" digest = "0.5.0" diff --git a/src/delay.rs b/src/delay.rs index 06c980f642ebde26d682e885768b16da9aa75b5d..2b2149eb6a5526b9249ceba9b82bcaca1135ba93 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -4,8 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::{Element, IntoElements}; -use minidom::convert::ElementEmitter; +use minidom::{Element, IntoElements, ElementEmitter}; use error::Error; use jid::Jid; diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 2028fe930d81db97d0de164b921baa5fbb5249ef..7cd729e1041530bdc2b8fca0136e97780a307138 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -7,8 +7,7 @@ use hashes; use hashes::{Hash, parse_hash}; -use minidom::{Element, IntoElements}; -use minidom::convert::ElementEmitter; +use minidom::{Element, IntoElements, ElementEmitter}; use error::Error; use ns; diff --git a/src/message.rs b/src/message.rs index 3550e810c1159f82891f75707cc116074794dbd9..6fcc681de1ac2d68ee09b91d593a4bd2847e4a6d 100644 --- a/src/message.rs +++ b/src/message.rs @@ -7,8 +7,7 @@ use std::convert::TryFrom; use std::str::FromStr; -use minidom::{Element, IntoElements, IntoAttributeValue}; -use minidom::convert::ElementEmitter; +use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; use jid::Jid; diff --git a/src/presence.rs b/src/presence.rs index 3b3c1faa2fe4793bddc40c75a80c725ead85ee59..bf7b57dfc495c53d1722e030a126df451cbe3e15 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -7,8 +7,7 @@ use std::str::FromStr; use std::collections::BTreeMap; -use minidom::{Element, IntoElements, IntoAttributeValue}; -use minidom::convert::ElementEmitter; +use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; use jid::Jid; From 6c4fd8da014f99abaf6eabb82eaa567e60b9056a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 4 May 2017 01:20:28 +0100 Subject: [PATCH 129/698] jingle: Update to Into and TryFrom. --- src/iq.rs | 10 +- src/jingle.rs | 392 ++++++++++++++++++++++++++------------------------ 2 files changed, 208 insertions(+), 194 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index c7728ae2cc05e479f3c59d9fff6288fdf87302d1..8a4e70e7d69e79882795e2ff3ad20b9be047b710 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -5,6 +5,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use minidom::IntoAttributeValue; @@ -17,7 +19,7 @@ use ns; use stanza_error; use disco; use ibb; -use jingle; +use jingle::Jingle; use ping; /// Lists every known payload of a ``. @@ -25,7 +27,7 @@ use ping; pub enum IqPayload { Disco(disco::Disco), IBB(ibb::IBB), - Jingle(jingle::Jingle), + Jingle(Jingle), Ping(ping::Ping), } @@ -97,7 +99,7 @@ pub fn parse_iq(root: &Element) -> Result { Some(IqPayload::Disco(disco)) } else if let Ok(ibb) = ibb::parse_ibb(elem) { Some(IqPayload::IBB(ibb)) - } else if let Ok(jingle) = jingle::parse_jingle(elem) { + } else if let Ok(jingle) = Jingle::try_from(elem) { Some(IqPayload::Jingle(jingle)) } else if let Ok(ping) = ping::parse_ping(elem) { Some(IqPayload::Ping(ping)) @@ -152,7 +154,7 @@ pub fn serialise_payload(payload: &IqPayload) -> Element { match *payload { IqPayload::Disco(ref disco) => disco::serialise_disco(disco), IqPayload::IBB(ref ibb) => ibb::serialise(ibb), - IqPayload::Jingle(ref jingle) => jingle::serialise(jingle), + IqPayload::Jingle(ref jingle) => jingle.into(), IqPayload::Ping(_) => ping::serialise_ping(), } } diff --git a/src/jingle.rs b/src/jingle.rs index 717a3b77217ebdc8b6f290c60e7df9ce348e86ce..dfe0d71046138649fb33f97e499a17319ef771f5 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -4,10 +4,10 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; use std::str::FromStr; -use minidom::{Element, IntoElements}; -use minidom::convert::ElementEmitter; +use minidom::Element; use error::Error; use ns; @@ -204,9 +204,9 @@ impl FromStr for Reason { } } -impl IntoElements for Reason { - fn into_elements(self, emitter: &mut ElementEmitter) { - let elem = Element::builder(match self { +impl<'a> Into for &'a Reason { + fn into(self) -> Element { + Element::builder(match *self { Reason::AlternativeSession => "alternative-session", Reason::Busy => "busy", Reason::Cancel => "cancel", @@ -224,8 +224,7 @@ impl IntoElements for Reason { Reason::Timeout => "timeout", Reason::UnsupportedApplications => "unsupported-applications", Reason::UnsupportedTransports => "unsupported-transports", - }).build(); - emitter.append_child(elem); + }).build() } } @@ -246,195 +245,208 @@ pub struct Jingle { pub other: Vec, } -pub fn parse_jingle(root: &Element) -> Result { - if !root.is("jingle", ns::JINGLE) { - return Err(Error::ParseError("This is not a Jingle element.")); - } +impl<'a> TryFrom<&'a Element> for Jingle { + type Error = Error; - let mut contents: Vec = vec!(); - - let action = root.attr("action") - .ok_or(Error::ParseError("Jingle must have an 'action' attribute."))? - .parse()?; - let initiator = root.attr("initiator") - .and_then(|initiator| initiator.parse().ok()); - let responder = root.attr("responder") - .and_then(|responder| responder.parse().ok()); - let sid = root.attr("sid") - .ok_or(Error::ParseError("Jingle must have a 'sid' attribute."))?; - let mut reason_element = None; - let mut other = vec!(); - - for child in root.children() { - if child.is("content", ns::JINGLE) { - let creator = child.attr("creator") - .ok_or(Error::ParseError("Content must have a 'creator' attribute."))? - .parse()?; - let disposition = child.attr("disposition") - .unwrap_or("session"); - let name = child.attr("name") - .ok_or(Error::ParseError("Content must have a 'name' attribute."))?; - let senders = child.attr("senders") - .unwrap_or("both") - .parse()?; - let mut description = None; - let mut transport = None; - let mut security = None; - for stuff in child.children() { - if stuff.name() == "description" { - if description.is_some() { - return Err(Error::ParseError("Content must not have more than one description.")); - } - let namespace = stuff.ns() - .and_then(|ns| ns.parse().ok()) - // TODO: is this even reachable? - .ok_or(Error::ParseError("Invalid namespace on description element."))?; - description = Some(( - namespace, - stuff.clone(), - )); - } else if stuff.name() == "transport" { - if transport.is_some() { - return Err(Error::ParseError("Content must not have more than one transport.")); - } - let namespace = stuff.ns() - .and_then(|ns| ns.parse().ok()) - // TODO: is this even reachable? - .ok_or(Error::ParseError("Invalid namespace on transport element."))?; - transport = Some(( - namespace, - stuff.clone(), - )); - } else if stuff.name() == "security" { - if security.is_some() { - return Err(Error::ParseError("Content must not have more than one security.")); + fn try_from(root: &'a Element) -> Result { + if !root.is("jingle", ns::JINGLE) { + return Err(Error::ParseError("This is not a Jingle element.")); + } + + let mut contents: Vec = vec!(); + + let action = root.attr("action") + .ok_or(Error::ParseError("Jingle must have an 'action' attribute."))? + .parse()?; + let initiator = root.attr("initiator") + .and_then(|initiator| initiator.parse().ok()); + let responder = root.attr("responder") + .and_then(|responder| responder.parse().ok()); + let sid = root.attr("sid") + .ok_or(Error::ParseError("Jingle must have a 'sid' attribute."))?; + let mut reason_element = None; + let mut other = vec!(); + + for child in root.children() { + if child.is("content", ns::JINGLE) { + let creator = child.attr("creator") + .ok_or(Error::ParseError("Content must have a 'creator' attribute."))? + .parse()?; + let disposition = child.attr("disposition") + .unwrap_or("session"); + let name = child.attr("name") + .ok_or(Error::ParseError("Content must have a 'name' attribute."))?; + let senders = child.attr("senders") + .unwrap_or("both") + .parse()?; + let mut description = None; + let mut transport = None; + let mut security = None; + for stuff in child.children() { + if stuff.name() == "description" { + if description.is_some() { + return Err(Error::ParseError("Content must not have more than one description.")); + } + let namespace = stuff.ns() + .and_then(|ns| ns.parse().ok()) + // TODO: is this even reachable? + .ok_or(Error::ParseError("Invalid namespace on description element."))?; + description = Some(( + namespace, + stuff.clone(), + )); + } else if stuff.name() == "transport" { + if transport.is_some() { + return Err(Error::ParseError("Content must not have more than one transport.")); + } + let namespace = stuff.ns() + .and_then(|ns| ns.parse().ok()) + // TODO: is this even reachable? + .ok_or(Error::ParseError("Invalid namespace on transport element."))?; + transport = Some(( + namespace, + stuff.clone(), + )); + } else if stuff.name() == "security" { + if security.is_some() { + return Err(Error::ParseError("Content must not have more than one security.")); + } + let namespace = stuff.ns() + .and_then(|ns| ns.parse().ok()) + // TODO: is this even reachable? + .ok_or(Error::ParseError("Invalid namespace on security element."))?; + security = Some(( + namespace, + stuff.clone(), + )); } - let namespace = stuff.ns() - .and_then(|ns| ns.parse().ok()) - // TODO: is this even reachable? - .ok_or(Error::ParseError("Invalid namespace on security element."))?; - security = Some(( - namespace, - stuff.clone(), - )); } - } - if description.is_none() { - return Err(Error::ParseError("Content must have one description.")); - } - if transport.is_none() { - return Err(Error::ParseError("Content must have one transport.")); - } - let description = description.unwrap().to_owned(); - let transport = transport.unwrap().to_owned(); - contents.push(Content { - creator: creator, - disposition: disposition.to_owned(), - name: name.to_owned(), - senders: senders, - description: description, - transport: transport, - security: security, - }); - } else if child.is("reason", ns::JINGLE) { - if reason_element.is_some() { - return Err(Error::ParseError("Jingle must not have more than one reason.")); - } - let mut reason = None; - let mut text = None; - for stuff in child.children() { - if stuff.ns() != Some(ns::JINGLE) { - return Err(Error::ParseError("Reason contains a foreign element.")); + if description.is_none() { + return Err(Error::ParseError("Content must have one description.")); + } + if transport.is_none() { + return Err(Error::ParseError("Content must have one transport.")); + } + let description = description.unwrap().to_owned(); + let transport = transport.unwrap().to_owned(); + contents.push(Content { + creator: creator, + disposition: disposition.to_owned(), + name: name.to_owned(), + senders: senders, + description: description, + transport: transport, + security: security, + }); + } else if child.is("reason", ns::JINGLE) { + if reason_element.is_some() { + return Err(Error::ParseError("Jingle must not have more than one reason.")); } - let name = stuff.name(); - if name == "text" { - if text.is_some() { - return Err(Error::ParseError("Reason must not have more than one text.")); + let mut reason = None; + let mut text = None; + for stuff in child.children() { + if stuff.ns() != Some(ns::JINGLE) { + return Err(Error::ParseError("Reason contains a foreign element.")); + } + let name = stuff.name(); + if name == "text" { + if text.is_some() { + return Err(Error::ParseError("Reason must not have more than one text.")); + } + text = Some(stuff.text()); + } else { + reason = Some(name.parse()?); } - text = Some(stuff.text()); - } else { - reason = Some(name.parse()?); } + if reason.is_none() { + return Err(Error::ParseError("Reason doesn’t contain a valid reason.")); + } + reason_element = Some(ReasonElement { + reason: reason.unwrap(), + text: text, + }); + } else { + other.push(child.clone()); } - if reason.is_none() { - return Err(Error::ParseError("Reason doesn’t contain a valid reason.")); - } - reason_element = Some(ReasonElement { - reason: reason.unwrap(), - text: text, - }); - } else { - other.push(child.clone()); } - } - Ok(Jingle { - action: action, - initiator: initiator, - responder: responder, - sid: sid.to_owned(), - contents: contents, - reason: reason_element, - other: other, - }) + Ok(Jingle { + action: action, + initiator: initiator, + responder: responder, + sid: sid.to_owned(), + contents: contents, + reason: reason_element, + other: other, + }) + } } -pub fn serialise_content(content: &Content) -> Element { - let mut root = Element::builder("content") - .ns(ns::JINGLE) - .attr("creator", String::from(content.creator.clone())) - .attr("disposition", content.disposition.clone()) - .attr("name", content.name.clone()) - .attr("senders", String::from(content.senders.clone())) - .build(); - root.append_child(content.description.1.clone()); - root.append_child(content.transport.1.clone()); - if let Some(security) = content.security.clone() { - root.append_child(security.1.clone()); +impl<'a> Into for &'a Content { + fn into(self) -> Element { + let mut root = Element::builder("content") + .ns(ns::JINGLE) + .attr("creator", String::from(self.creator.clone())) + .attr("disposition", self.disposition.clone()) + .attr("name", self.name.clone()) + .attr("senders", String::from(self.senders.clone())) + .build(); + root.append_child(self.description.1.clone()); + root.append_child(self.transport.1.clone()); + if let Some(security) = self.security.clone() { + root.append_child(security.1.clone()); + } + root } - root } -pub fn serialise(jingle: &Jingle) -> Element { - let mut root = Element::builder("jingle") - .ns(ns::JINGLE) - .attr("action", String::from(jingle.action.clone())) - .attr("initiator", jingle.initiator.clone()) - .attr("responder", jingle.responder.clone()) - .attr("sid", jingle.sid.clone()) - .build(); - for content in jingle.contents.clone() { - let content_elem = serialise_content(&content); - root.append_child(content_elem); +impl<'a> Into for &'a Jingle { + fn into(self) -> Element { + let mut root = Element::builder("jingle") + .ns(ns::JINGLE) + .attr("action", String::from(self.action.clone())) + .attr("initiator", self.initiator.clone()) + .attr("responder", self.responder.clone()) + .attr("sid", self.sid.clone()) + .build(); + for content in self.contents.clone() { + let content_elem = (&content).into(); + root.append_child(content_elem); + } + if let Some(ref reason) = self.reason { + let reason2: Element = (&reason.reason).into(); + let reason_elem = Element::builder("reason") + .append(reason2) + .append(reason.text.clone()) + .build(); + root.append_child(reason_elem); + } + root } - if let Some(ref reason) = jingle.reason { - let reason_elem = Element::builder("reason") - .append(reason.reason.clone()) - .append(reason.text.clone()) - .build(); - root.append_child(reason_elem); +} + +impl Into for Jingle { + fn into(self) -> Element { + (&self).into() } - root } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use jingle; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let jingle = jingle::parse_jingle(&elem).unwrap(); - assert_eq!(jingle.action, jingle::Action::SessionInitiate); + let jingle = Jingle::try_from(&elem).unwrap(); + assert_eq!(jingle.action, Action::SessionInitiate); assert_eq!(jingle.sid, "coucou"); } #[test] fn test_invalid_jingle() { let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -442,7 +454,7 @@ mod tests { assert_eq!(message, "Jingle must have an 'action' attribute."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -450,7 +462,7 @@ mod tests { assert_eq!(message, "Jingle must have a 'sid' attribute."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -461,25 +473,25 @@ mod tests { #[test] fn test_content() { let elem: Element = "".parse().unwrap(); - let jingle = jingle::parse_jingle(&elem).unwrap(); - assert_eq!(jingle.contents[0].creator, jingle::Creator::Initiator); + let jingle = Jingle::try_from(&elem).unwrap(); + assert_eq!(jingle.contents[0].creator, Creator::Initiator); assert_eq!(jingle.contents[0].name, "coucou"); - assert_eq!(jingle.contents[0].senders, jingle::Senders::Both); + assert_eq!(jingle.contents[0].senders, Senders::Both); assert_eq!(jingle.contents[0].disposition, "session"); let elem: Element = "".parse().unwrap(); - let jingle = jingle::parse_jingle(&elem).unwrap(); - assert_eq!(jingle.contents[0].senders, jingle::Senders::Both); + let jingle = Jingle::try_from(&elem).unwrap(); + assert_eq!(jingle.contents[0].senders, Senders::Both); let elem: Element = "".parse().unwrap(); - let jingle = jingle::parse_jingle(&elem).unwrap(); + let jingle = Jingle::try_from(&elem).unwrap(); assert_eq!(jingle.contents[0].disposition, "early-session"); } #[test] fn test_invalid_content() { let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -487,7 +499,7 @@ mod tests { assert_eq!(message, "Content must have a 'creator' attribute."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -495,7 +507,7 @@ mod tests { assert_eq!(message, "Content must have a 'name' attribute."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -503,7 +515,7 @@ mod tests { assert_eq!(message, "Unknown creator."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -511,7 +523,7 @@ mod tests { assert_eq!(message, "Unknown senders."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -519,7 +531,7 @@ mod tests { assert_eq!(message, "Unknown senders."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -527,7 +539,7 @@ mod tests { assert_eq!(message, "Content must have one description."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -538,22 +550,22 @@ mod tests { #[test] fn test_reason() { let elem: Element = "".parse().unwrap(); - let jingle = jingle::parse_jingle(&elem).unwrap(); + let jingle = Jingle::try_from(&elem).unwrap(); let reason = jingle.reason.unwrap(); - assert_eq!(reason.reason, jingle::Reason::Success); + assert_eq!(reason.reason, Reason::Success); assert_eq!(reason.text, None); let elem: Element = "coucou".parse().unwrap(); - let jingle = jingle::parse_jingle(&elem).unwrap(); + let jingle = Jingle::try_from(&elem).unwrap(); let reason = jingle.reason.unwrap(); - assert_eq!(reason.reason, jingle::Reason::Success); + assert_eq!(reason.reason, Reason::Success); assert_eq!(reason.text, Some(String::from("coucou"))); } #[test] fn test_invalid_reason() { let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -561,7 +573,7 @@ mod tests { assert_eq!(message, "Reason doesn’t contain a valid reason."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -569,7 +581,7 @@ mod tests { assert_eq!(message, "Unknown reason."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -577,7 +589,7 @@ mod tests { assert_eq!(message, "Reason contains a foreign element."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -585,7 +597,7 @@ mod tests { assert_eq!(message, "Jingle must not have more than one reason."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), From 033cbe777bc3908d2420f35669d7e9d515b31a5e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 4 May 2017 01:31:13 +0100 Subject: [PATCH 130/698] ping: Port to TryFrom/Into. --- src/iq.rs | 8 ++++---- src/ping.rs | 37 ++++++++++++++++++++++--------------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index 8a4e70e7d69e79882795e2ff3ad20b9be047b710..d1e3eeaa5be33dbf1821a3e2a62481eeac997241 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -20,7 +20,7 @@ use stanza_error; use disco; use ibb; use jingle::Jingle; -use ping; +use ping::Ping; /// Lists every known payload of a ``. #[derive(Debug, Clone)] @@ -28,7 +28,7 @@ pub enum IqPayload { Disco(disco::Disco), IBB(ibb::IBB), Jingle(Jingle), - Ping(ping::Ping), + Ping(Ping), } #[derive(Debug, Clone)] @@ -101,7 +101,7 @@ pub fn parse_iq(root: &Element) -> Result { Some(IqPayload::IBB(ibb)) } else if let Ok(jingle) = Jingle::try_from(elem) { Some(IqPayload::Jingle(jingle)) - } else if let Ok(ping) = ping::parse_ping(elem) { + } else if let Ok(ping) = Ping::try_from(elem) { Some(IqPayload::Ping(ping)) } else { None @@ -155,7 +155,7 @@ pub fn serialise_payload(payload: &IqPayload) -> Element { IqPayload::Disco(ref disco) => disco::serialise_disco(disco), IqPayload::IBB(ref ibb) => ibb::serialise(ibb), IqPayload::Jingle(ref jingle) => jingle.into(), - IqPayload::Ping(_) => ping::serialise_ping(), + IqPayload::Ping(ref ping) => ping.into(), } } diff --git a/src/ping.rs b/src/ping.rs index b6b6f617d1c60b3854e0ef3c1af806ee0d310422..4fe2fd86792ffa9f877ccedb8a7eca6d68dadc53 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -5,6 +5,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use error::Error; @@ -14,37 +16,42 @@ use ns; #[derive(Debug, Clone)] pub struct Ping; -pub fn parse_ping(root: &Element) -> Result { - if !root.is("ping", ns::PING) { - return Err(Error::ParseError("This is not a ping element.")); - } +impl<'a> TryFrom<&'a Element> for Ping { + type Error = Error; - for _ in root.children() { - return Err(Error::ParseError("Unknown child in ping element.")); + fn try_from(elem: &'a Element) -> Result { + if !elem.is("ping", ns::PING) { + return Err(Error::ParseError("This is not a ping element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in ping element.")); + } + Ok(Ping) } - Ok(Ping { }) } -pub fn serialise_ping() -> Element { - Element::builder("ping").ns(ns::PING).build() +impl<'a> Into for &'a Ping { + fn into(self) -> Element { + Element::builder("ping") + .ns(ns::PING) + .build() + } } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use ping; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - ping::parse_ping(&elem).unwrap(); + Ping::try_from(&elem).unwrap(); } #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); - let error = ping::parse_ping(&elem).unwrap_err(); + let error = Ping::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -56,7 +63,7 @@ mod tests { #[ignore] fn test_invalid_attribute() { let elem: Element = "".parse().unwrap(); - let error = ping::parse_ping(&elem).unwrap_err(); + let error = Ping::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), From e3acb55b49dcb3d718b1ab47c162285d11a1267d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 4 May 2017 23:11:10 +0100 Subject: [PATCH 131/698] ibb: Port to TryFrom/Into. --- src/ibb.rs | 159 +++++++++++++++++++++++++++-------------------------- src/iq.rs | 8 +-- 2 files changed, 86 insertions(+), 81 deletions(-) diff --git a/src/ibb.rs b/src/ibb.rs index d805eb810af168f1e0f416d952cf2d399e71754e..0156cfee764230ed366373184c996b23bb319f7c 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -4,6 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; @@ -64,103 +65,107 @@ pub enum IBB { }, } -fn required_attr(root: &Element, attr: &str, err: Error) -> Result { - root.attr(attr) +fn required_attr(elem: &Element, attr: &str, err: Error) -> Result { + elem.attr(attr) .and_then(|value| value.parse().ok()) .ok_or(err) } -pub fn parse_ibb(root: &Element) -> Result { - if root.is("open", ns::IBB) { - for _ in root.children() { - return Err(Error::ParseError("Unknown child in open element.")); +impl<'a> TryFrom<&'a Element> for IBB { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if elem.is("open", ns::IBB) { + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in open element.")); + } + let block_size = required_attr(elem, "block-size", Error::ParseError("Required attribute 'block-size' missing in open element."))?; + let sid = required_attr(elem, "sid", Error::ParseError("Required attribute 'sid' missing in open element."))?; + let stanza = match elem.attr("stanza") { + Some(stanza) => stanza.parse()?, + None => Default::default(), + }; + Ok(IBB::Open { + block_size: block_size, + sid: sid, + stanza: stanza + }) + } else if elem.is("data", ns::IBB) { + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in data element.")); + } + let seq = required_attr(elem, "seq", Error::ParseError("Required attribute 'seq' missing in data element."))?; + let sid = required_attr(elem, "sid", Error::ParseError("Required attribute 'sid' missing in data element."))?; + let data = base64::decode(&elem.text())?; + Ok(IBB::Data { + seq: seq, + sid: sid, + data: data + }) + } else if elem.is("close", ns::IBB) { + let sid = required_attr(elem, "sid", Error::ParseError("Required attribute 'sid' missing in data element."))?; + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in close element.")); + } + Ok(IBB::Close { + sid: sid, + }) + } else { + Err(Error::ParseError("This is not an ibb element.")) } - let block_size = required_attr(root, "block-size", Error::ParseError("Required attribute 'block-size' missing in open element."))?; - let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in open element."))?; - let stanza = match root.attr("stanza") { - Some(stanza) => stanza.parse()?, - None => Default::default(), - }; - Ok(IBB::Open { - block_size: block_size, - sid: sid, - stanza: stanza - }) - } else if root.is("data", ns::IBB) { - for _ in root.children() { - return Err(Error::ParseError("Unknown child in data element.")); - } - let seq = required_attr(root, "seq", Error::ParseError("Required attribute 'seq' missing in data element."))?; - let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in data element."))?; - let data = base64::decode(&root.text())?; - Ok(IBB::Data { - seq: seq, - sid: sid, - data: data - }) - } else if root.is("close", ns::IBB) { - let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in data element."))?; - for _ in root.children() { - return Err(Error::ParseError("Unknown child in close element.")); - } - Ok(IBB::Close { - sid: sid, - }) - } else { - Err(Error::ParseError("This is not an ibb element.")) } } -pub fn serialise(ibb: &IBB) -> Element { - match *ibb { - IBB::Open { ref block_size, ref sid, ref stanza } => { - Element::builder("open") - .ns(ns::IBB) - .attr("block-size", format!("{}", block_size)) - .attr("sid", sid.to_owned()) - .attr("stanza", stanza.to_owned()) - .build() - }, - IBB::Data { ref seq, ref sid, ref data } => { - Element::builder("data") - .ns(ns::IBB) - .attr("seq", format!("{}", seq)) - .attr("sid", sid.to_owned()) - .append(base64::encode(&data)) - .build() - }, - IBB::Close { ref sid } => { - Element::builder("close") - .ns(ns::IBB) - .attr("sid", sid.to_owned()) - .build() - }, +impl<'a> Into for &'a IBB { + fn into(self) -> Element { + match *self { + IBB::Open { ref block_size, ref sid, ref stanza } => { + Element::builder("open") + .ns(ns::IBB) + .attr("block-size", format!("{}", block_size)) + .attr("sid", sid.to_owned()) + .attr("stanza", stanza.to_owned()) + .build() + }, + IBB::Data { ref seq, ref sid, ref data } => { + Element::builder("data") + .ns(ns::IBB) + .attr("seq", format!("{}", seq)) + .attr("sid", sid.to_owned()) + .append(base64::encode(&data)) + .build() + }, + IBB::Close { ref sid } => { + Element::builder("close") + .ns(ns::IBB) + .attr("sid", sid.to_owned()) + .build() + }, + } } } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use ibb; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let open = ibb::parse_ibb(&elem).unwrap(); + let open = IBB::try_from(&elem).unwrap(); match open { - ibb::IBB::Open { block_size, sid, stanza } => { + IBB::Open { block_size, sid, stanza } => { assert_eq!(block_size, 3); assert_eq!(sid, "coucou"); - assert_eq!(stanza, ibb::Stanza::Iq); + assert_eq!(stanza, Stanza::Iq); }, _ => panic!(), } let elem: Element = "AAAA".parse().unwrap(); - let data = ibb::parse_ibb(&elem).unwrap(); + let data = IBB::try_from(&elem).unwrap(); match data { - ibb::IBB::Data { seq, sid, data } => { + IBB::Data { seq, sid, data } => { assert_eq!(seq, 0); assert_eq!(sid, "coucou"); assert_eq!(data, vec!(0, 0, 0)); @@ -169,9 +174,9 @@ mod tests { } let elem: Element = "".parse().unwrap(); - let close = ibb::parse_ibb(&elem).unwrap(); + let close = IBB::try_from(&elem).unwrap(); match close { - ibb::IBB::Close { sid } => { + IBB::Close { sid } => { assert_eq!(sid, "coucou"); }, _ => panic!(), @@ -181,7 +186,7 @@ mod tests { #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); - let error = ibb::parse_ibb(&elem).unwrap_err(); + let error = IBB::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -190,7 +195,7 @@ mod tests { // TODO: maybe make a better error message here. let elem: Element = "".parse().unwrap(); - let error = ibb::parse_ibb(&elem).unwrap_err(); + let error = IBB::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -198,7 +203,7 @@ mod tests { assert_eq!(message, "Required attribute 'block-size' missing in open element."); let elem: Element = "".parse().unwrap(); - let error = ibb::parse_ibb(&elem).unwrap_err(); + let error = IBB::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -209,7 +214,7 @@ mod tests { #[test] fn test_invalid_stanza() { let elem: Element = "".parse().unwrap(); - let error = ibb::parse_ibb(&elem).unwrap_err(); + let error = IBB::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/iq.rs b/src/iq.rs index d1e3eeaa5be33dbf1821a3e2a62481eeac997241..65b49cdbce0148609c63fc745dcba0a764f76594 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -18,7 +18,7 @@ use ns; use stanza_error; use disco; -use ibb; +use ibb::IBB; use jingle::Jingle; use ping::Ping; @@ -26,7 +26,7 @@ use ping::Ping; #[derive(Debug, Clone)] pub enum IqPayload { Disco(disco::Disco), - IBB(ibb::IBB), + IBB(IBB), Jingle(Jingle), Ping(Ping), } @@ -97,7 +97,7 @@ pub fn parse_iq(root: &Element) -> Result { } else { let parsed_payload = if let Ok(disco) = disco::parse_disco(elem) { Some(IqPayload::Disco(disco)) - } else if let Ok(ibb) = ibb::parse_ibb(elem) { + } else if let Ok(ibb) = IBB::try_from(elem) { Some(IqPayload::IBB(ibb)) } else if let Ok(jingle) = Jingle::try_from(elem) { Some(IqPayload::Jingle(jingle)) @@ -153,7 +153,7 @@ pub fn parse_iq(root: &Element) -> Result { pub fn serialise_payload(payload: &IqPayload) -> Element { match *payload { IqPayload::Disco(ref disco) => disco::serialise_disco(disco), - IqPayload::IBB(ref ibb) => ibb::serialise(ibb), + IqPayload::IBB(ref ibb) => ibb.into(), IqPayload::Jingle(ref jingle) => jingle.into(), IqPayload::Ping(ref ping) => ping.into(), } From 51eecda4fd10025970aafd481a6bd3a52c2231bd Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 12:48:42 +0100 Subject: [PATCH 132/698] error: Implement From. --- src/error.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/error.rs b/src/error.rs index a65f7e2df4aab5c0355fe569139a0af4007a6d12..2f7b254f40da4948bb7812cb7f9b6d9fdc5f7192 100644 --- a/src/error.rs +++ b/src/error.rs @@ -7,6 +7,7 @@ use std::convert::From; use std::io; use std::num; +use std::string; use base64; use minidom; @@ -19,6 +20,7 @@ pub enum Error { XMLError(minidom::Error), Base64Error(base64::DecodeError), ParseIntError(num::ParseIntError), + ParseStringError(string::ParseError), JidParseError(jid::JidParseError), } @@ -46,6 +48,12 @@ impl From for Error { } } +impl From for Error { + fn from(err: string::ParseError) -> Error { + Error::ParseStringError(err) + } +} + impl From for Error { fn from(err: jid::JidParseError) -> Error { Error::JidParseError(err) From 828b88e5b203b9021b4d1dd8dc63cd99ec32ee91 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 12:49:30 +0100 Subject: [PATCH 133/698] Add a Jingle SOCKS5 Bytestreams Transport implementation. --- src/jingle_s5b.rs | 315 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 + src/ns.rs | 3 + 3 files changed, 321 insertions(+) create mode 100644 src/jingle_s5b.rs diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs new file mode 100644 index 0000000000000000000000000000000000000000..6a6ee50f71a0f511cd5a0dd6763be16df8accb6b --- /dev/null +++ b/src/jingle_s5b.rs @@ -0,0 +1,315 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::convert::TryFrom; +use std::str::FromStr; + +use minidom::{Element, IntoAttributeValue}; + +use error::Error; + +use ns; + +#[derive(Debug, Clone, PartialEq)] +pub enum Type { + Assisted, + Direct, + Proxy, + Tunnel, +} + +impl Default for Type { + fn default() -> Type { + Type::Direct + } +} + +impl FromStr for Type { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "assisted" => Type::Assisted, + "direct" => Type::Direct, + "proxy" => Type::Proxy, + "tunnel" => Type::Tunnel, + + _ => return Err(Error::ParseError("Invalid 'type' attribute in candidate element.")), + }) + } +} + +impl IntoAttributeValue for Type { + fn into_attribute_value(self) -> Option { + Some(match self { + Type::Assisted => String::from("assisted"), + Type::Direct => return None, + Type::Proxy => String::from("proxy"), + Type::Tunnel => String::from("tunnel"), + }) + } +} + +#[derive(Debug, Clone)] +pub struct Candidate { + pub cid: String, + pub host: String, + pub jid: String, + pub port: Option, + pub priority: u32, + pub type_: Type, +} + +impl<'a> Into for &'a Candidate { + fn into(self) -> Element { + Element::builder("candidate") + .ns(ns::JINGLE_S5B) + .attr("cid", self.cid.clone()) + .attr("host", self.host.clone()) + .attr("jid", self.jid.clone()) + .attr("port", match self.port { Some(port) => Some(format!("{}", port)), None => None }) + .attr("priority", format!("{}", self.priority)) + .attr("type", self.type_.clone()) + .build() + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum Mode { + Tcp, + Udp, +} + +impl Default for Mode { + fn default() -> Mode { + Mode::Tcp + } +} + +impl FromStr for Mode { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "tcp" => Mode::Tcp, + "udp" => Mode::Udp, + + _ => return Err(Error::ParseError("Invalid 'mode' attribute.")), + }) + } +} + +impl IntoAttributeValue for Mode { + fn into_attribute_value(self) -> Option { + match self { + Mode::Tcp => None, + Mode::Udp => Some(String::from("udp")), + } + } +} + +#[derive(Debug, Clone)] +pub enum TransportPayload { + Activated(String), + Candidates(Vec), + CandidateError, + CandidateUsed(String), + ProxyError, +} + +#[derive(Debug, Clone)] +pub struct Transport { + pub sid: String, + pub dstaddr: Option, + pub mode: Mode, + pub payload: TransportPayload, +} + +impl<'a> TryFrom<&'a Element> for Transport { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if elem.is("transport", ns::JINGLE_S5B) { + let sid = elem.attr("sid") + .ok_or(Error::ParseError("Required attribute 'sid' missing in JingleS5B transport element."))? + .parse()?; + let dstaddr = elem.attr("dstaddr") + .and_then(|value| Some(value.to_owned())); + let mode = match elem.attr("mode") { + None => Default::default(), + Some(mode) => mode.parse()?, + }; + + let mut payload = None; + for child in elem.children() { + payload = Some(if child.is("candidate", ns::JINGLE_S5B) { + let mut candidates = match payload { + Some(TransportPayload::Candidates(candidates)) => candidates, + Some(_) => return Err(Error::ParseError("Non-activated child already present in JingleS5B transport element.")), + None => vec!(), + }; + let cid = child.attr("cid") + .ok_or(Error::ParseError("Required attribute 'cid' missing in JingleS5B candidate element."))? + .parse()?; + let host = child.attr("host") + .ok_or(Error::ParseError("Required attribute 'host' missing in JingleS5B candidate element."))? + .parse()?; + let jid = child.attr("jid") + .ok_or(Error::ParseError("Required attribute 'jid' missing in JingleS5B candidate element."))? + .parse()?; + let port = match child.attr("port") { + Some(s) => Some(s.parse()?), + None => None, + }; + let priority = child.attr("priority") + .ok_or(Error::ParseError("Required attribute 'priority' missing in JingleS5B candidate element."))? + .parse()?; + let type_ = match child.attr("type") { + Some(s) => s.parse()?, + None => Default::default(), + }; + candidates.push(Candidate { + cid: cid, + host: host, + jid: jid, + port: port, + priority: priority, + type_: type_, + }); + TransportPayload::Candidates(candidates) + } else if child.is("activated", ns::JINGLE_S5B) { + if let Some(_) = payload { + return Err(Error::ParseError("Non-activated child already present in JingleS5B transport element.")); + } + let cid = child.attr("cid") + .ok_or(Error::ParseError("Required attribute 'cid' missing in JingleS5B activated element."))? + .parse()?; + TransportPayload::Activated(cid) + } else if child.is("candidate-error", ns::JINGLE_S5B) { + if let Some(_) = payload { + return Err(Error::ParseError("Non-candidate-error child already present in JingleS5B transport element.")); + } + TransportPayload::CandidateError + } else if child.is("candidate-used", ns::JINGLE_S5B) { + if let Some(_) = payload { + return Err(Error::ParseError("Non-candidate-used child already present in JingleS5B transport element.")); + } + let cid = child.attr("cid") + .ok_or(Error::ParseError("Required attribute 'cid' missing in JingleS5B candidate-used element."))? + .parse()?; + TransportPayload::CandidateUsed(cid) + } else if child.is("proxy-error", ns::JINGLE_S5B) { + if let Some(_) = payload { + return Err(Error::ParseError("Non-proxy-error child already present in JingleS5B transport element.")); + } + TransportPayload::ProxyError + } else { + return Err(Error::ParseError("Unknown child in JingleS5B transport element.")); + }); + } + let payload = payload.ok_or(Error::ParseError("No child in JingleS5B transport element."))?; + Ok(Transport { + sid: sid, + dstaddr: dstaddr, + mode: mode, + payload: payload, + }) + } else { + Err(Error::ParseError("This is not an JingleS5B transport element.")) + } + } +} + +impl<'a> Into for &'a Transport { + fn into(self) -> Element { + Element::builder("transport") + .ns(ns::JINGLE_S5B) + .attr("sid", self.sid.clone()) + .attr("dstaddr", self.dstaddr.clone()) + .attr("mode", self.mode.clone()) + .append(match self.payload { + TransportPayload::Candidates(ref candidates) => { + candidates.iter() + .map(|candidate| -> Element { candidate.into() }) + .collect::>() + }, + TransportPayload::Activated(ref cid) => { + vec!(Element::builder("activated") + .ns(ns::JINGLE_S5B) + .attr("cid", cid.to_owned()) + .build()) + }, + TransportPayload::CandidateError => { + vec!(Element::builder("candidate-error") + .ns(ns::JINGLE_S5B) + .build()) + }, + TransportPayload::CandidateUsed(ref cid) => { + vec!(Element::builder("candidate-used") + .ns(ns::JINGLE_S5B) + .attr("cid", cid.to_owned()) + .build()) + }, + TransportPayload::ProxyError => { + vec!(Element::builder("proxy-error") + .ns(ns::JINGLE_S5B) + .build()) + }, + }) + .build() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let transport = Transport::try_from(&elem).unwrap(); + assert_eq!(transport.sid, "coucou"); + assert_eq!(transport.dstaddr, None); + assert_eq!(transport.mode, Mode::Tcp); + match transport.payload { + TransportPayload::ProxyError => (), + _ => panic!("Wrong element inside transport!"), + } + } + + #[test] + fn test_serialise_activated() { + let elem: Element = "".parse().unwrap(); + let transport = Transport { + sid: String::from("coucou"), + dstaddr: None, + mode: Mode::Tcp, + payload: TransportPayload::Activated(String::from("coucou")), + }; + let elem2: Element = (&transport).into(); + assert_eq!(elem, elem2); + } + + #[test] + fn test_serialise_candidate() { + let elem: Element = "".parse().unwrap(); + let transport = Transport { + sid: String::from("coucou"), + dstaddr: None, + mode: Mode::Tcp, + payload: TransportPayload::Candidates(vec!(Candidate { + cid: String::from("coucou"), + host: String::from("coucou"), + jid: String::from("coucou@coucou"), + port: None, + priority: 0u32, + type_: Type::Direct, + })), + }; + let elem2: Element = (&transport).into(); + assert_eq!(elem, elem2); + } +} diff --git a/src/lib.rs b/src/lib.rs index 3076fccfa3b346d63150393ff1317654da90bdeb..e3fb5356424a05396f7d8c6d3a21b7fd8c4fdd6d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,6 +76,9 @@ pub mod attention; /// XEP-0234: Jingle File Transfer pub mod jingle_ft; +/// XEP-0260: Jingle SOCKS5 Bytestreams Transport Method +pub mod jingle_s5b; + /// XEP-0261: Jingle In-Band Bytestreams Transport Method pub mod jingle_ibb; diff --git a/src/ns.rs b/src/ns.rs index 8e7c08b1ae6e2443838d13bac259554a4c27a1f1..c98bff5839c560fba551fad366bc942358e8f797 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -47,6 +47,9 @@ pub const JINGLE_FT: &'static str = "urn:xmpp:jingle:apps:file-transfer:5"; /// XEP-0234: Jingle File Transfer pub const JINGLE_FT_ERROR: &'static str = "urn:xmpp:jingle:apps:file-transfer:errors:0"; +/// XEP-0260: Jingle SOCKS5 Bytestreams Transport Method +pub const JINGLE_S5B: &'static str = "urn:xmpp:jingle:transports:s5b:1"; + /// XEP-0261: Jingle In-Band Bytestreams Transport Method pub const JINGLE_IBB: &'static str = "urn:xmpp:jingle:transports:ibb:1"; From bb12168c4159edcb6e0d7a8511be171bc02092e1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 13:54:16 +0100 Subject: [PATCH 134/698] =?UTF-8?q?jingle=5Fs5b:=20Handle=20the=20forgotte?= =?UTF-8?q?n=20case=20of=20an=20empty=20transport=20element,=20see=20?= =?UTF-8?q?=C2=A72.2.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/jingle_s5b.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 6a6ee50f71a0f511cd5a0dd6763be16df8accb6b..b0d7266f42e16e5ad1c531ef08a7e32307407bfb 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -118,6 +118,7 @@ pub enum TransportPayload { CandidateError, CandidateUsed(String), ProxyError, + None, } #[derive(Debug, Clone)] @@ -210,7 +211,7 @@ impl<'a> TryFrom<&'a Element> for Transport { return Err(Error::ParseError("Unknown child in JingleS5B transport element.")); }); } - let payload = payload.ok_or(Error::ParseError("No child in JingleS5B transport element."))?; + let payload = payload.unwrap_or(TransportPayload::None); Ok(Transport { sid: sid, dstaddr: dstaddr, @@ -258,6 +259,7 @@ impl<'a> Into for &'a Transport { .ns(ns::JINGLE_S5B) .build()) }, + TransportPayload::None => vec!(), }) .build() } @@ -269,13 +271,13 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); let transport = Transport::try_from(&elem).unwrap(); assert_eq!(transport.sid, "coucou"); assert_eq!(transport.dstaddr, None); assert_eq!(transport.mode, Mode::Tcp); match transport.payload { - TransportPayload::ProxyError => (), + TransportPayload::None => (), _ => panic!("Wrong element inside transport!"), } } From 8fbda37f6b6990a6db322693e95599ebe4b6ef3b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 14:03:41 +0100 Subject: [PATCH 135/698] jingle_ibb: Switch to Into/TryFrom. --- src/jingle_ibb.rs | 70 +++++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 8e0d04ca90b9f4dd6effecaf6d133cd2951cd806..4ac847cc70d3d8534a07defe16e8cfb80e207207 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -4,6 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; use std::str::FromStr; use minidom::Element; @@ -30,55 +31,58 @@ fn required_attr(root: &Element, attr: &str, err: Error) -> Result Result { - if root.is("transport", ns::JINGLE_IBB) { - for _ in root.children() { - return Err(Error::ParseError("Unknown child in JingleIBB element.")); +impl<'a> TryFrom<&'a Element> for Transport { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if elem.is("transport", ns::JINGLE_IBB) { + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in JingleIBB element.")); + } + let block_size = required_attr(elem, "block-size", Error::ParseError("Required attribute 'block-size' missing in JingleIBB element."))?; + let sid = required_attr(elem, "sid", Error::ParseError("Required attribute 'sid' missing in JingleIBB element."))?; + let stanza = elem.attr("stanza") + .unwrap_or("iq") + .parse()?; + Ok(Transport { + block_size: block_size, + sid: sid, + stanza: stanza + }) + } else { + Err(Error::ParseError("This is not an JingleIBB element.")) } - let block_size = required_attr(root, "block-size", Error::ParseError("Required attribute 'block-size' missing in JingleIBB element."))?; - let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in JingleIBB element."))?; - let stanza = root.attr("stanza") - .unwrap_or("iq") - .parse()?; - Ok(Transport { - block_size: block_size, - sid: sid, - stanza: stanza - }) - } else { - Err(Error::ParseError("This is not an JingleIBB element.")) } } -pub fn serialise(transport: &Transport) -> Element { - Element::builder("transport") - .ns(ns::JINGLE_IBB) - .attr("block-size", format!("{}", transport.block_size)) - .attr("sid", transport.sid.clone()) - .attr("stanza", transport.stanza.clone()) - .build() +impl<'a> Into for &'a Transport { + fn into(self) -> Element { + Element::builder("transport") + .ns(ns::JINGLE_IBB) + .attr("block-size", format!("{}", self.block_size)) + .attr("sid", self.sid.clone()) + .attr("stanza", self.stanza.clone()) + .build() + } } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use ibb; - use jingle_ibb; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let transport = jingle_ibb::parse_jingle_ibb(&elem).unwrap(); + let transport = Transport::try_from(&elem).unwrap(); assert_eq!(transport.block_size, 3); assert_eq!(transport.sid, "coucou"); - assert_eq!(transport.stanza, ibb::Stanza::Iq); + assert_eq!(transport.stanza, Stanza::Iq); } #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); - let error = jingle_ibb::parse_jingle_ibb(&elem).unwrap_err(); + let error = Transport::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -87,7 +91,7 @@ mod tests { // TODO: maybe make a better error message here. let elem: Element = "".parse().unwrap(); - let error = jingle_ibb::parse_jingle_ibb(&elem).unwrap_err(); + let error = Transport::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -95,7 +99,7 @@ mod tests { assert_eq!(message, "Required attribute 'block-size' missing in JingleIBB element."); let elem: Element = "".parse().unwrap(); - let error = jingle_ibb::parse_jingle_ibb(&elem).unwrap_err(); + let error = Transport::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -106,7 +110,7 @@ mod tests { #[test] fn test_invalid_stanza() { let elem: Element = "".parse().unwrap(); - let error = jingle_ibb::parse_jingle_ibb(&elem).unwrap_err(); + let error = Transport::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), From f99c667eabadd9085df00afc2b0f60232a05b151 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 14:58:18 +0100 Subject: [PATCH 136/698] jingle: Make description and transport optional in content. --- src/jingle.rs | 67 ++++++++++----------------------------------------- 1 file changed, 13 insertions(+), 54 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index dfe0d71046138649fb33f97e499a17319ef771f5..fb928aee94f0a113f05027a6e8abee5105cd154d 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -150,9 +150,9 @@ pub struct Content { pub disposition: String, pub name: String, pub senders: Senders, - pub description: (String, Element), - pub transport: (String, Element), - pub security: Option<(String, Element)>, + pub description: Option, + pub transport: Option, + pub security: Option, } #[derive(Debug, Clone, PartialEq)] @@ -287,48 +287,19 @@ impl<'a> TryFrom<&'a Element> for Jingle { if description.is_some() { return Err(Error::ParseError("Content must not have more than one description.")); } - let namespace = stuff.ns() - .and_then(|ns| ns.parse().ok()) - // TODO: is this even reachable? - .ok_or(Error::ParseError("Invalid namespace on description element."))?; - description = Some(( - namespace, - stuff.clone(), - )); + description = Some(stuff.clone()); } else if stuff.name() == "transport" { if transport.is_some() { return Err(Error::ParseError("Content must not have more than one transport.")); } - let namespace = stuff.ns() - .and_then(|ns| ns.parse().ok()) - // TODO: is this even reachable? - .ok_or(Error::ParseError("Invalid namespace on transport element."))?; - transport = Some(( - namespace, - stuff.clone(), - )); + transport = Some(stuff.clone()); } else if stuff.name() == "security" { if security.is_some() { return Err(Error::ParseError("Content must not have more than one security.")); } - let namespace = stuff.ns() - .and_then(|ns| ns.parse().ok()) - // TODO: is this even reachable? - .ok_or(Error::ParseError("Invalid namespace on security element."))?; - security = Some(( - namespace, - stuff.clone(), - )); + security = Some(stuff.clone()); } } - if description.is_none() { - return Err(Error::ParseError("Content must have one description.")); - } - if transport.is_none() { - return Err(Error::ParseError("Content must have one transport.")); - } - let description = description.unwrap().to_owned(); - let transport = transport.unwrap().to_owned(); contents.push(Content { creator: creator, disposition: disposition.to_owned(), @@ -391,10 +362,14 @@ impl<'a> Into for &'a Content { .attr("name", self.name.clone()) .attr("senders", String::from(self.senders.clone())) .build(); - root.append_child(self.description.1.clone()); - root.append_child(self.transport.1.clone()); + if let Some(description) = self.description.clone() { + root.append_child(description); + } + if let Some(transport) = self.transport.clone() { + root.append_child(transport); + } if let Some(security) = self.security.clone() { - root.append_child(security.1.clone()); + root.append_child(security); } root } @@ -529,22 +504,6 @@ mod tests { _ => panic!(), }; assert_eq!(message, "Unknown senders."); - - let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Content must have one description."); - - let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Content must have one transport."); } #[test] From 151635f5fb433fa3aaea4a3614907b928c3fb254 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 20:07:03 +0100 Subject: [PATCH 137/698] receipts: Switch to Into/TryFrom. --- src/message.rs | 8 +++--- src/receipts.rs | 67 ++++++++++++++++++++++++++----------------------- 2 files changed, 40 insertions(+), 35 deletions(-) diff --git a/src/message.rs b/src/message.rs index 6fcc681de1ac2d68ee09b91d593a4bd2847e4a6d..48001452a985cf99bc5d5c95c52f8fcf09fcd61a 100644 --- a/src/message.rs +++ b/src/message.rs @@ -18,7 +18,7 @@ use ns; use body; use stanza_error; use chatstates; -use receipts; +use receipts::Receipt; use delay; use attention::Attention; use message_correct; @@ -30,7 +30,7 @@ pub enum MessagePayload { Body(body::Body), StanzaError(stanza_error::StanzaError), ChatState(chatstates::ChatState), - Receipt(receipts::Receipt), + Receipt(Receipt), Delay(delay::Delay), Attention(Attention), MessageCorrect(message_correct::Replace), @@ -117,7 +117,7 @@ pub fn parse_message(root: &Element) -> Result { Some(MessagePayload::StanzaError(stanza_error)) } else if let Ok(chatstate) = chatstates::parse_chatstate(elem) { Some(MessagePayload::ChatState(chatstate)) - } else if let Ok(receipt) = receipts::parse_receipt(elem) { + } else if let Ok(receipt) = Receipt::try_from(elem) { Some(MessagePayload::Receipt(receipt)) } else if let Ok(delay) = delay::parse_delay(elem) { Some(MessagePayload::Delay(delay)) @@ -150,7 +150,7 @@ pub fn serialise_payload(payload: &MessagePayload) -> Element { MessagePayload::StanzaError(ref stanza_error) => stanza_error::serialise(stanza_error), MessagePayload::Attention(ref attention) => attention.into(), MessagePayload::ChatState(ref chatstate) => chatstates::serialise(chatstate), - MessagePayload::Receipt(ref receipt) => receipts::serialise(receipt), + MessagePayload::Receipt(ref receipt) => receipt.into(), MessagePayload::Delay(ref delay) => delay::serialise(delay), MessagePayload::MessageCorrect(ref replace) => message_correct::serialise(replace), MessagePayload::ExplicitMessageEncryption(ref eme) => eme::serialise(eme), diff --git a/src/receipts.rs b/src/receipts.rs index 3dc3b787606aaf2a08ca9506f4a9a7e30abff7ff..27392fb8cb161c2f1cea195907754b35d06d876b 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use error::Error; @@ -16,59 +18,62 @@ pub enum Receipt { Received(String), } -pub fn parse_receipt(root: &Element) -> Result { - for _ in root.children() { - return Err(Error::ParseError("Unknown child in receipt element.")); - } - if root.is("request", ns::RECEIPTS) { - Ok(Receipt::Request) - } else if root.is("received", ns::RECEIPTS) { - let id = root.attr("id").unwrap_or("").to_owned(); - Ok(Receipt::Received(id)) - } else { - Err(Error::ParseError("This is not a receipt element.")) +impl<'a> TryFrom<&'a Element> for Receipt { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in receipt element.")); + } + if elem.is("request", ns::RECEIPTS) { + Ok(Receipt::Request) + } else if elem.is("received", ns::RECEIPTS) { + let id = elem.attr("id").unwrap_or("").to_owned(); + Ok(Receipt::Received(id)) + } else { + Err(Error::ParseError("This is not a receipt element.")) + } } } -pub fn serialise(receipt: &Receipt) -> Element { - match *receipt { - Receipt::Request => Element::builder("request") - .ns(ns::RECEIPTS) - .build(), - Receipt::Received(ref id) => Element::builder("received") - .ns(ns::RECEIPTS) - .attr("id", id.clone()) - .build(), +impl<'a> Into for &'a Receipt { + fn into(self) -> Element { + match *self { + Receipt::Request => Element::builder("request") + .ns(ns::RECEIPTS) + .build(), + Receipt::Received(ref id) => Element::builder("received") + .ns(ns::RECEIPTS) + .attr("id", id.clone()) + .build(), + } } } #[cfg(test)] mod tests { - use minidom::Element; - //use error::Error; - use receipts; - use ns; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - receipts::parse_receipt(&elem).unwrap(); + Receipt::try_from(&elem).unwrap(); let elem: Element = "".parse().unwrap(); - receipts::parse_receipt(&elem).unwrap(); + Receipt::try_from(&elem).unwrap(); let elem: Element = "".parse().unwrap(); - receipts::parse_receipt(&elem).unwrap(); + Receipt::try_from(&elem).unwrap(); } #[test] fn test_serialise() { - let receipt = receipts::Receipt::Request; - let elem = receipts::serialise(&receipt); + let receipt = Receipt::Request; + let elem: Element = (&receipt).into(); assert!(elem.is("request", ns::RECEIPTS)); - let receipt = receipts::Receipt::Received("coucou".to_owned()); - let elem = receipts::serialise(&receipt); + let receipt = Receipt::Received("coucou".to_owned()); + let elem: Element = (&receipt).into(); assert!(elem.is("received", ns::RECEIPTS)); assert_eq!(elem.attr("id"), Some("coucou")); } From 7ebabf7e9103b2938caf5c7a1819dad7a095bf7c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 20:10:35 +0100 Subject: [PATCH 138/698] stanza_id: Switch to Into/TryFrom. --- src/stanza_id.rs | 102 ++++++++++++++++++++++++----------------------- 1 file changed, 53 insertions(+), 49 deletions(-) diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 5d34c3dc5c44b4bb47399e6543167d26572566a4..d478b1aae17af0f50f5bceab5febaf3e98689657 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use jid::Jid; @@ -22,61 +24,63 @@ pub enum StanzaId { }, } -pub fn parse_stanza_id(root: &Element) -> Result { - let is_stanza_id = root.is("stanza-id", ns::SID); - if !is_stanza_id && !root.is("origin-id", ns::SID) { - return Err(Error::ParseError("This is not a stanza-id or origin-id element.")); - } - for _ in root.children() { - return Err(Error::ParseError("Unknown child in stanza-id or origin-id element.")); - } - let id = match root.attr("id") { - Some(id) => id.to_owned(), - None => return Err(Error::ParseError("No 'id' attribute present in stanza-id or origin-id.")), - }; - Ok(if is_stanza_id { - let by = match root.attr("by") { - Some(by) => by.parse().unwrap(), - None => return Err(Error::ParseError("No 'by' attribute present in stanza-id.")), +impl<'a> TryFrom<&'a Element> for StanzaId { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + let is_stanza_id = elem.is("stanza-id", ns::SID); + if !is_stanza_id && !elem.is("origin-id", ns::SID) { + return Err(Error::ParseError("This is not a stanza-id or origin-id element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in stanza-id or origin-id element.")); + } + let id = match elem.attr("id") { + Some(id) => id.to_owned(), + None => return Err(Error::ParseError("No 'id' attribute present in stanza-id or origin-id.")), }; - StanzaId::StanzaId { id, by } - } else { - StanzaId::OriginId { id } - }) + Ok(if is_stanza_id { + let by = match elem.attr("by") { + Some(by) => by.parse().unwrap(), + None => return Err(Error::ParseError("No 'by' attribute present in stanza-id.")), + }; + StanzaId::StanzaId { id, by } + } else { + StanzaId::OriginId { id } + }) + } } -pub fn serialise(stanza_id: &StanzaId) -> Element { - match *stanza_id { - StanzaId::StanzaId { ref id, ref by } => { - Element::builder("stanza-id") - .ns(ns::SID) - .attr("id", id.clone()) - .attr("by", String::from(by.clone())) - .build() - }, - StanzaId::OriginId { ref id } => { - Element::builder("origin-id") - .ns(ns::SID) - .attr("id", id.clone()) - .build() - }, +impl<'a> Into for &'a StanzaId { + fn into(self) -> Element { + match *self { + StanzaId::StanzaId { ref id, ref by } => { + Element::builder("stanza-id") + .ns(ns::SID) + .attr("id", id.clone()) + .attr("by", String::from(by.clone())) + .build() + }, + StanzaId::OriginId { ref id } => { + Element::builder("origin-id") + .ns(ns::SID) + .attr("id", id.clone()) + .build() + }, + } } } #[cfg(test)] mod tests { + use super::*; use std::str::FromStr; - use minidom::Element; - use jid::Jid; - use error::Error; - use stanza_id; - #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let stanza_id = stanza_id::parse_stanza_id(&elem).unwrap(); - if let stanza_id::StanzaId::StanzaId { id, by } = stanza_id { + let stanza_id = StanzaId::try_from(&elem).unwrap(); + if let StanzaId::StanzaId { id, by } = stanza_id { assert_eq!(id, String::from("coucou")); assert_eq!(by, Jid::from_str("coucou@coucou").unwrap()); } else { @@ -84,8 +88,8 @@ mod tests { } let elem: Element = "".parse().unwrap(); - let stanza_id = stanza_id::parse_stanza_id(&elem).unwrap(); - if let stanza_id::StanzaId::OriginId { id } = stanza_id { + let stanza_id = StanzaId::try_from(&elem).unwrap(); + if let StanzaId::OriginId { id } = stanza_id { assert_eq!(id, String::from("coucou")); } else { panic!(); @@ -95,7 +99,7 @@ mod tests { #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = stanza_id::parse_stanza_id(&elem).unwrap_err(); + let error = StanzaId::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -106,7 +110,7 @@ mod tests { #[test] fn test_invalid_id() { let elem: Element = "".parse().unwrap(); - let error = stanza_id::parse_stanza_id(&elem).unwrap_err(); + let error = StanzaId::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -117,7 +121,7 @@ mod tests { #[test] fn test_invalid_by() { let elem: Element = "".parse().unwrap(); - let error = stanza_id::parse_stanza_id(&elem).unwrap_err(); + let error = StanzaId::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -128,8 +132,8 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let stanza_id = stanza_id::StanzaId::StanzaId { id: String::from("coucou"), by: Jid::from_str("coucou@coucou").unwrap() }; - let elem2 = stanza_id::serialise(&stanza_id); + let stanza_id = StanzaId::StanzaId { id: String::from("coucou"), by: Jid::from_str("coucou@coucou").unwrap() }; + let elem2 = (&stanza_id).into(); assert_eq!(elem, elem2); } } From de8fe4bf02728b862732dc8bdc54a5490158ab57 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 20:14:45 +0100 Subject: [PATCH 139/698] rsm: Switch to Into/TryFrom. --- src/mam.rs | 11 +-- src/rsm.rs | 212 +++++++++++++++++++++++++++-------------------------- 2 files changed, 115 insertions(+), 108 deletions(-) diff --git a/src/mam.rs b/src/mam.rs index cb0cc7ac048dce7a73f86daf56d53999bf37d33c..77a3b6cf0caf988d0ee8294e2f7ca1519bbafcd3 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use jid::Jid; @@ -11,7 +13,6 @@ use error::Error; use data_forms; use data_forms::DataForm; -use rsm; use rsm::Set; use forwarding; use forwarding::Forwarded; @@ -63,7 +64,7 @@ pub fn parse_query(root: &Element) -> Result { if child.is("x", ns::DATA_FORMS) { form = Some(data_forms::parse_data_form(child)?); } else if child.is("set", ns::RSM) { - set = Some(rsm::parse_rsm(child)?); + set = Some(Set::try_from(child)?); } else { return Err(Error::ParseError("Unknown child in query element.")); } @@ -117,7 +118,7 @@ pub fn parse_fin(root: &Element) -> Result { let mut set = None; for child in root.children() { if child.is("set", ns::RSM) { - set = Some(rsm::parse_rsm(child)?); + set = Some(Set::try_from(child)?); } else { return Err(Error::ParseError("Unknown child in fin element.")); } @@ -179,7 +180,7 @@ pub fn serialise_query(query: &Query) -> Element { // elem.append_child(data_forms::serialise(&form)); //} if let Some(ref set) = query.set { - elem.append_child(rsm::serialise(&set)); + elem.append_child(set.into()); } elem } @@ -202,7 +203,7 @@ pub fn serialise_fin(fin: &Fin) -> Element { false => None, }) .build(); - elem.append_child(rsm::serialise(&fin.set)); + elem.append_child((&fin.set).into()); elem } diff --git a/src/rsm.rs b/src/rsm.rs index d23fa1b6bc58919b080182f28627e2c67be73460..4caab08096b9bc75be4ffb687c931c0d2f82e8a6 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use error::Error; @@ -27,119 +29,123 @@ pub struct Set { pub max: Option, } -pub fn parse_rsm(root: &Element) -> Result { - if !root.is("set", ns::RSM) { - return Err(Error::ParseError("This is not a RSM element.")); - } - let mut after = None; - let mut before = None; - let mut count = None; - let mut first = None; - let mut index = None; - let mut last = None; - let mut max = None; - for child in root.children() { - if child.is("after", ns::RSM) { - if after.is_some() { - return Err(Error::ParseError("Set can’t have more than one after.")); - } - after = Some(child.text()); - } else if child.is("before", ns::RSM) { - if before.is_some() { - return Err(Error::ParseError("Set can’t have more than one before.")); - } - before = Some(child.text()); - } else if child.is("count", ns::RSM) { - if count.is_some() { - return Err(Error::ParseError("Set can’t have more than one count.")); - } - count = Some(child.text().parse()?); - } else if child.is("first", ns::RSM) { - if first.is_some() { - return Err(Error::ParseError("Set can’t have more than one first.")); - } - first = Some(First { - index: match child.attr("index") { - Some(index) => Some(index.parse()?), - None => None, - }, - base: child.text(), - }); - } else if child.is("index", ns::RSM) { - if index.is_some() { - return Err(Error::ParseError("Set can’t have more than one index.")); - } - index = Some(child.text().parse()?); - } else if child.is("last", ns::RSM) { - if last.is_some() { - return Err(Error::ParseError("Set can’t have more than one last.")); - } - last = Some(child.text()); - } else if child.is("max", ns::RSM) { - if max.is_some() { - return Err(Error::ParseError("Set can’t have more than one max.")); +impl<'a> TryFrom<&'a Element> for Set { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if !elem.is("set", ns::RSM) { + return Err(Error::ParseError("This is not a RSM element.")); + } + let mut after = None; + let mut before = None; + let mut count = None; + let mut first = None; + let mut index = None; + let mut last = None; + let mut max = None; + for child in elem.children() { + if child.is("after", ns::RSM) { + if after.is_some() { + return Err(Error::ParseError("Set can’t have more than one after.")); + } + after = Some(child.text()); + } else if child.is("before", ns::RSM) { + if before.is_some() { + return Err(Error::ParseError("Set can’t have more than one before.")); + } + before = Some(child.text()); + } else if child.is("count", ns::RSM) { + if count.is_some() { + return Err(Error::ParseError("Set can’t have more than one count.")); + } + count = Some(child.text().parse()?); + } else if child.is("first", ns::RSM) { + if first.is_some() { + return Err(Error::ParseError("Set can’t have more than one first.")); + } + first = Some(First { + index: match child.attr("index") { + Some(index) => Some(index.parse()?), + None => None, + }, + base: child.text(), + }); + } else if child.is("index", ns::RSM) { + if index.is_some() { + return Err(Error::ParseError("Set can’t have more than one index.")); + } + index = Some(child.text().parse()?); + } else if child.is("last", ns::RSM) { + if last.is_some() { + return Err(Error::ParseError("Set can’t have more than one last.")); + } + last = Some(child.text()); + } else if child.is("max", ns::RSM) { + if max.is_some() { + return Err(Error::ParseError("Set can’t have more than one max.")); + } + max = Some(child.text().parse()?); + } else { + return Err(Error::ParseError("Unknown child in set element.")); } - max = Some(child.text().parse()?); - } else { - return Err(Error::ParseError("Unknown child in set element.")); } + Ok(Set { + after: after, + before: before, + count: count, + first: first, + index: index, + last: last, + max: max, + }) } - Ok(Set { - after: after, - before: before, - count: count, - first: first, - index: index, - last: last, - max: max, - }) } -pub fn serialise(rsm: &Set) -> Element { - let mut elem = Element::builder("set") - .ns(ns::RSM) - .build(); - if rsm.after.is_some() { - elem.append_child(Element::builder("after").ns(ns::RSM).append(rsm.after.clone()).build()); - } - if rsm.before.is_some() { - elem.append_child(Element::builder("before").ns(ns::RSM).append(rsm.before.clone()).build()); - } - if rsm.count.is_some() { - elem.append_child(Element::builder("count").ns(ns::RSM).append(format!("{}", rsm.count.unwrap())).build()); - } - if rsm.first.is_some() { - let first = rsm.first.clone().unwrap(); - elem.append_child(Element::builder("first") - .ns(ns::RSM) - .attr("index", match first.index { - Some(index) => Some(format!("{}", index)), - None => None, - }) - .append(first.base.clone()).build()); - } - if rsm.index.is_some() { - elem.append_child(Element::builder("index").ns(ns::RSM).append(format!("{}", rsm.index.unwrap())).build()); - } - if rsm.last.is_some() { - elem.append_child(Element::builder("last").ns(ns::RSM).append(rsm.last.clone()).build()); - } - if rsm.max.is_some() { - elem.append_child(Element::builder("max").ns(ns::RSM).append(format!("{}", rsm.max.unwrap())).build()); +impl<'a> Into for &'a Set { + fn into(self) -> Element { + let mut elem = Element::builder("set") + .ns(ns::RSM) + .build(); + if self.after.is_some() { + elem.append_child(Element::builder("after").ns(ns::RSM).append(self.after.clone()).build()); + } + if self.before.is_some() { + elem.append_child(Element::builder("before").ns(ns::RSM).append(self.before.clone()).build()); + } + if self.count.is_some() { + elem.append_child(Element::builder("count").ns(ns::RSM).append(format!("{}", self.count.unwrap())).build()); + } + if self.first.is_some() { + let first = self.first.clone().unwrap(); + elem.append_child(Element::builder("first") + .ns(ns::RSM) + .attr("index", match first.index { + Some(index) => Some(format!("{}", index)), + None => None, + }) + .append(first.base.clone()).build()); + } + if self.index.is_some() { + elem.append_child(Element::builder("index").ns(ns::RSM).append(format!("{}", self.index.unwrap())).build()); + } + if self.last.is_some() { + elem.append_child(Element::builder("last").ns(ns::RSM).append(self.last.clone()).build()); + } + if self.max.is_some() { + elem.append_child(Element::builder("max").ns(ns::RSM).append(format!("{}", self.max.unwrap())).build()); + } + elem } - elem } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use rsm; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let set = rsm::parse_rsm(&elem).unwrap(); + let set = Set::try_from(&elem).unwrap(); assert_eq!(set.after, None); assert_eq!(set.before, None); assert_eq!(set.count, None); @@ -155,7 +161,7 @@ mod tests { #[test] fn test_unknown() { let elem: Element = "".parse().unwrap(); - let error = rsm::parse_rsm(&elem).unwrap_err(); + let error = Set::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -166,7 +172,7 @@ mod tests { #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = rsm::parse_rsm(&elem).unwrap_err(); + let error = Set::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -177,7 +183,7 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let rsm = rsm::Set { + let rsm = Set { after: None, before: None, count: None, @@ -186,7 +192,7 @@ mod tests { last: None, max: None, }; - let elem2 = rsm::serialise(&rsm); + let elem2 = (&rsm).into(); assert_eq!(elem, elem2); } } From 4f11a067d82f8ff407c734df2a7b94fcd67d2f56 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 20:16:45 +0100 Subject: [PATCH 140/698] message_correct: Switch to Into/TryFrom. --- src/message.rs | 8 +++---- src/message_correct.rs | 54 +++++++++++++++++++++++------------------- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/message.rs b/src/message.rs index 48001452a985cf99bc5d5c95c52f8fcf09fcd61a..c513abee9aad85c47c58b273683d5267c8f73727 100644 --- a/src/message.rs +++ b/src/message.rs @@ -21,7 +21,7 @@ use chatstates; use receipts::Receipt; use delay; use attention::Attention; -use message_correct; +use message_correct::Replace; use eme; /// Lists every known payload of a ``. @@ -33,7 +33,7 @@ pub enum MessagePayload { Receipt(Receipt), Delay(delay::Delay), Attention(Attention), - MessageCorrect(message_correct::Replace), + MessageCorrect(Replace), ExplicitMessageEncryption(eme::ExplicitMessageEncryption), } @@ -123,7 +123,7 @@ pub fn parse_message(root: &Element) -> Result { Some(MessagePayload::Delay(delay)) } else if let Ok(attention) = Attention::try_from(elem) { Some(MessagePayload::Attention(attention)) - } else if let Ok(replace) = message_correct::parse_replace(elem) { + } else if let Ok(replace) = Replace::try_from(elem) { Some(MessagePayload::MessageCorrect(replace)) } else if let Ok(eme) = eme::parse_explicit_message_encryption(elem) { Some(MessagePayload::ExplicitMessageEncryption(eme)) @@ -152,7 +152,7 @@ pub fn serialise_payload(payload: &MessagePayload) -> Element { MessagePayload::ChatState(ref chatstate) => chatstates::serialise(chatstate), MessagePayload::Receipt(ref receipt) => receipt.into(), MessagePayload::Delay(ref delay) => delay::serialise(delay), - MessagePayload::MessageCorrect(ref replace) => message_correct::serialise(replace), + MessagePayload::MessageCorrect(ref replace) => replace.into(), MessagePayload::ExplicitMessageEncryption(ref eme) => eme::serialise(eme), } } diff --git a/src/message_correct.rs b/src/message_correct.rs index 27ac7e917fcad7f8186800e8c7855eac95a99d17..ecc555cc5aa23681d4e6c8600795a8aaca234d2a 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use error::Error; @@ -15,43 +17,47 @@ pub struct Replace { pub id: String, } -pub fn parse_replace(root: &Element) -> Result { - if !root.is("replace", ns::MESSAGE_CORRECT) { - return Err(Error::ParseError("This is not a replace element.")); - } - for _ in root.children() { - return Err(Error::ParseError("Unknown child in replace element.")); +impl<'a> TryFrom<&'a Element> for Replace { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if !elem.is("replace", ns::MESSAGE_CORRECT) { + return Err(Error::ParseError("This is not a replace element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in replace element.")); + } + let id = match elem.attr("id") { + Some(id) => id.to_owned(), + None => return Err(Error::ParseError("No 'id' attribute present in replace.")), + }; + Ok(Replace { id: id }) } - let id = match root.attr("id") { - Some(id) => id.to_owned(), - None => return Err(Error::ParseError("No 'id' attribute present in replace.")), - }; - Ok(Replace { id: id }) } -pub fn serialise(replace: &Replace) -> Element { - Element::builder("replace") - .ns(ns::MESSAGE_CORRECT) - .attr("id", replace.id.clone()) - .build() +impl<'a> Into for &'a Replace { + fn into(self) -> Element { + Element::builder("replace") + .ns(ns::MESSAGE_CORRECT) + .attr("id", self.id.clone()) + .build() + } } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use message_correct; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - message_correct::parse_replace(&elem).unwrap(); + Replace::try_from(&elem).unwrap(); } #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = message_correct::parse_replace(&elem).unwrap_err(); + let error = Replace::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -62,7 +68,7 @@ mod tests { #[test] fn test_invalid_id() { let elem: Element = "".parse().unwrap(); - let error = message_correct::parse_replace(&elem).unwrap_err(); + let error = Replace::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -73,8 +79,8 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let replace = message_correct::Replace { id: String::from("coucou") }; - let elem2 = message_correct::serialise(&replace); + let replace = Replace { id: String::from("coucou") }; + let elem2 = (&replace).into(); assert_eq!(elem, elem2); } } From a3a90e4edad79e1a231c761cab96953b8a4bb2e5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 20:30:52 +0100 Subject: [PATCH 141/698] jingle_ft: Switch to Into/TryFrom. --- src/jingle_ft.rs | 270 ++++++++++++++++++++++++----------------------- 1 file changed, 140 insertions(+), 130 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 7cd729e1041530bdc2b8fca0136e97780a307138..f2b981faa45d8f4a967769a243f4da01bfa43e65 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use hashes; use hashes::{Hash, parse_hash}; @@ -85,150 +87,158 @@ impl IntoElements for Received { } } -pub fn parse_jingle_ft(root: &Element) -> Result { - if !root.is("description", ns::JINGLE_FT) { - return Err(Error::ParseError("This is not a JingleFT description element.")); - } - if root.children().collect::>().len() != 1 { - return Err(Error::ParseError("JingleFT description element must have exactly one child.")); - } +impl<'a> TryFrom<&'a Element> for Description { + type Error = Error; - let mut date = None; - let mut media_type = None; - let mut name = None; - let mut desc = None; - let mut size = None; - let mut range = None; - let mut hashes = vec!(); - for description_payload in root.children() { - if !description_payload.is("file", ns::JINGLE_FT) { - return Err(Error::ParseError("Unknown element in JingleFT description.")); + fn try_from(elem: &'a Element) -> Result { + if !elem.is("description", ns::JINGLE_FT) { + return Err(Error::ParseError("This is not a JingleFT description element.")); } - for file_payload in description_payload.children() { - if file_payload.is("date", ns::JINGLE_FT) { - if date.is_some() { - return Err(Error::ParseError("File must not have more than one date.")); - } - date = Some(file_payload.text()); - } else if file_payload.is("media-type", ns::JINGLE_FT) { - if media_type.is_some() { - return Err(Error::ParseError("File must not have more than one media-type.")); - } - media_type = Some(file_payload.text()); - } else if file_payload.is("name", ns::JINGLE_FT) { - if name.is_some() { - return Err(Error::ParseError("File must not have more than one name.")); - } - name = Some(file_payload.text()); - } else if file_payload.is("desc", ns::JINGLE_FT) { - if desc.is_some() { - return Err(Error::ParseError("File must not have more than one desc.")); - } - desc = Some(file_payload.text()); - } else if file_payload.is("size", ns::JINGLE_FT) { - if size.is_some() { - return Err(Error::ParseError("File must not have more than one size.")); - } - size = Some(file_payload.text().parse()?); - } else if file_payload.is("range", ns::JINGLE_FT) { - if range.is_some() { - return Err(Error::ParseError("File must not have more than one range.")); - } - let offset = file_payload.attr("offset").unwrap_or("0").parse()?; - let length = match file_payload.attr("length") { - Some(length) => Some(length.parse()?), - None => None, - }; - let mut range_hashes = vec!(); - for hash_element in file_payload.children() { - if !hash_element.is("hash", ns::HASHES) { - return Err(Error::ParseError("Unknown element in JingleFT range.")); + if elem.children().collect::>().len() != 1 { + return Err(Error::ParseError("JingleFT description element must have exactly one child.")); + } + + let mut date = None; + let mut media_type = None; + let mut name = None; + let mut desc = None; + let mut size = None; + let mut range = None; + let mut hashes = vec!(); + for description_payload in elem.children() { + if !description_payload.is("file", ns::JINGLE_FT) { + return Err(Error::ParseError("Unknown element in JingleFT description.")); + } + for file_payload in description_payload.children() { + if file_payload.is("date", ns::JINGLE_FT) { + if date.is_some() { + return Err(Error::ParseError("File must not have more than one date.")); + } + date = Some(file_payload.text()); + } else if file_payload.is("media-type", ns::JINGLE_FT) { + if media_type.is_some() { + return Err(Error::ParseError("File must not have more than one media-type.")); + } + media_type = Some(file_payload.text()); + } else if file_payload.is("name", ns::JINGLE_FT) { + if name.is_some() { + return Err(Error::ParseError("File must not have more than one name.")); + } + name = Some(file_payload.text()); + } else if file_payload.is("desc", ns::JINGLE_FT) { + if desc.is_some() { + return Err(Error::ParseError("File must not have more than one desc.")); } - range_hashes.push(parse_hash(hash_element)?); + desc = Some(file_payload.text()); + } else if file_payload.is("size", ns::JINGLE_FT) { + if size.is_some() { + return Err(Error::ParseError("File must not have more than one size.")); + } + size = Some(file_payload.text().parse()?); + } else if file_payload.is("range", ns::JINGLE_FT) { + if range.is_some() { + return Err(Error::ParseError("File must not have more than one range.")); + } + let offset = file_payload.attr("offset").unwrap_or("0").parse()?; + let length = match file_payload.attr("length") { + Some(length) => Some(length.parse()?), + None => None, + }; + let mut range_hashes = vec!(); + for hash_element in file_payload.children() { + if !hash_element.is("hash", ns::HASHES) { + return Err(Error::ParseError("Unknown element in JingleFT range.")); + } + range_hashes.push(parse_hash(hash_element)?); + } + range = Some(Range { + offset: offset, + length: length, + hashes: range_hashes, + }); + } else if file_payload.is("hash", ns::HASHES) { + hashes.push(parse_hash(file_payload)?); + } else { + return Err(Error::ParseError("Unknown element in JingleFT file.")); } - range = Some(Range { - offset: offset, - length: length, - hashes: range_hashes, - }); - } else if file_payload.is("hash", ns::HASHES) { - hashes.push(parse_hash(file_payload)?); - } else { - return Err(Error::ParseError("Unknown element in JingleFT file.")); } } - } - Ok(Description { - file: File { - date: date, - media_type: media_type, - name: name, - desc: desc, - size: size, - range: range, - hashes: hashes, - }, - }) + Ok(Description { + file: File { + date: date, + media_type: media_type, + name: name, + desc: desc, + size: size, + range: range, + hashes: hashes, + }, + }) + } } -pub fn serialise_file(file: &File) -> Element { - let mut root = Element::builder("file") - .ns(ns::JINGLE_FT) - .build(); - if let Some(ref date) = file.date { - root.append_child(Element::builder("date") - .ns(ns::JINGLE_FT) - .append(date.clone()) - .build()); - } - if let Some(ref media_type) = file.media_type { - root.append_child(Element::builder("media-type") - .ns(ns::JINGLE_FT) - .append(media_type.clone()) - .build()); - } - if let Some(ref name) = file.name { - root.append_child(Element::builder("name") - .ns(ns::JINGLE_FT) - .append(name.clone()) - .build()); - } - if let Some(ref desc) = file.desc { - root.append_child(Element::builder("desc") - .ns(ns::JINGLE_FT) - .append(desc.clone()) - .build()); - } - if let Some(ref size) = file.size { - root.append_child(Element::builder("size") - .ns(ns::JINGLE_FT) - .append(format!("{}", size)) - .build()); - } - if let Some(ref range) = file.range { - root.append_child(Element::builder("range") - .ns(ns::JINGLE_FT) - .append(range.clone()) - .build()); - } - for hash in file.hashes.clone() { - root.append_child(hashes::serialise(&hash)); +impl<'a> Into for &'a File { + fn into(self) -> Element { + let mut root = Element::builder("file") + .ns(ns::JINGLE_FT) + .build(); + if let Some(ref date) = self.date { + root.append_child(Element::builder("date") + .ns(ns::JINGLE_FT) + .append(date.clone()) + .build()); + } + if let Some(ref media_type) = self.media_type { + root.append_child(Element::builder("media-type") + .ns(ns::JINGLE_FT) + .append(media_type.clone()) + .build()); + } + if let Some(ref name) = self.name { + root.append_child(Element::builder("name") + .ns(ns::JINGLE_FT) + .append(name.clone()) + .build()); + } + if let Some(ref desc) = self.desc { + root.append_child(Element::builder("desc") + .ns(ns::JINGLE_FT) + .append(desc.clone()) + .build()); + } + if let Some(ref size) = self.size { + root.append_child(Element::builder("size") + .ns(ns::JINGLE_FT) + .append(format!("{}", size)) + .build()); + } + if let Some(ref range) = self.range { + root.append_child(Element::builder("range") + .ns(ns::JINGLE_FT) + .append(range.clone()) + .build()); + } + for hash in self.hashes.clone() { + root.append_child(hashes::serialise(&hash)); + } + root } - root } -pub fn serialise(desc: &Description) -> Element { - Element::builder("description") - .ns(ns::JINGLE_FT) - .append(serialise_file(&desc.file)) - .build() +impl<'a> Into for &'a Description { + fn into(self) -> Element { + let file: Element = (&self.file).into(); + Element::builder("description") + .ns(ns::JINGLE_FT) + .append(file) + .build() + } } #[cfg(test)] mod tests { - use minidom::Element; - use jingle_ft; + use super::*; #[test] fn test_description() { @@ -245,7 +255,7 @@ mod tests { "#.parse().unwrap(); - let desc = jingle_ft::parse_jingle_ft(&elem).unwrap(); + let desc = Description::try_from(&elem).unwrap(); assert_eq!(desc.file.media_type, Some(String::from("text/plain"))); assert_eq!(desc.file.name, Some(String::from("test.txt"))); assert_eq!(desc.file.desc, None); @@ -267,7 +277,7 @@ mod tests { "#.parse().unwrap(); - let desc = jingle_ft::parse_jingle_ft(&elem).unwrap(); + let desc = Description::try_from(&elem).unwrap(); assert_eq!(desc.file.media_type, None); assert_eq!(desc.file.name, None); assert_eq!(desc.file.desc, None); From e45152018771ad1cdb7f83e266fe761f1e5f9e11 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 20:33:58 +0100 Subject: [PATCH 142/698] chatstates: Switch to Into/TryFrom. --- src/chatstates.rs | 75 +++++++++++++++++++++++++---------------------- src/message.rs | 8 ++--- 2 files changed, 44 insertions(+), 39 deletions(-) diff --git a/src/chatstates.rs b/src/chatstates.rs index d26791ef67968e9f0a2540a5e8eb1b0f2c1c7f4b..9ed2b10c1a2b804e2ce402ae5955fa247a6c3822 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use error::Error; @@ -19,53 +21,56 @@ pub enum ChatState { Paused, } -pub fn parse_chatstate(root: &Element) -> Result { - for _ in root.children() { - return Err(Error::ParseError("Unknown child in chatstate element.")); - } - if root.is("active", ns::CHATSTATES) { - Ok(ChatState::Active) - } else if root.is("composing", ns::CHATSTATES) { - Ok(ChatState::Composing) - } else if root.is("gone", ns::CHATSTATES) { - Ok(ChatState::Gone) - } else if root.is("inactive", ns::CHATSTATES) { - Ok(ChatState::Inactive) - } else if root.is("paused", ns::CHATSTATES) { - Ok(ChatState::Paused) - } else { - Err(Error::ParseError("This is not a chatstate element.")) +impl<'a> TryFrom<&'a Element> for ChatState { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in chatstate element.")); + } + if elem.is("active", ns::CHATSTATES) { + Ok(ChatState::Active) + } else if elem.is("composing", ns::CHATSTATES) { + Ok(ChatState::Composing) + } else if elem.is("gone", ns::CHATSTATES) { + Ok(ChatState::Gone) + } else if elem.is("inactive", ns::CHATSTATES) { + Ok(ChatState::Inactive) + } else if elem.is("paused", ns::CHATSTATES) { + Ok(ChatState::Paused) + } else { + Err(Error::ParseError("This is not a chatstate element.")) + } } } -pub fn serialise(chatstate: &ChatState) -> Element { - Element::builder(match *chatstate { - ChatState::Active => "active", - ChatState::Composing => "composing", - ChatState::Gone => "gone", - ChatState::Inactive => "inactive", - ChatState::Paused => "paused", - }).ns(ns::CHATSTATES) - .build() +impl<'a> Into for &'a ChatState { + fn into(self) -> Element { + Element::builder(match *self { + ChatState::Active => "active", + ChatState::Composing => "composing", + ChatState::Gone => "gone", + ChatState::Inactive => "inactive", + ChatState::Paused => "paused", + }).ns(ns::CHATSTATES) + .build() + } } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use chatstates; - use ns; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - chatstates::parse_chatstate(&elem).unwrap(); + ChatState::try_from(&elem).unwrap(); } #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); - let error = chatstates::parse_chatstate(&elem).unwrap_err(); + let error = ChatState::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -76,7 +81,7 @@ mod tests { #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = chatstates::parse_chatstate(&elem).unwrap_err(); + let error = ChatState::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -88,7 +93,7 @@ mod tests { #[ignore] fn test_invalid_attribute() { let elem: Element = "".parse().unwrap(); - let error = chatstates::parse_chatstate(&elem).unwrap_err(); + let error = ChatState::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -98,8 +103,8 @@ mod tests { #[test] fn test_serialise() { - let chatstate = chatstates::ChatState::Active; - let elem = chatstates::serialise(&chatstate); + let chatstate = ChatState::Active; + let elem: Element = (&chatstate).into(); assert!(elem.is("active", ns::CHATSTATES)); } } diff --git a/src/message.rs b/src/message.rs index c513abee9aad85c47c58b273683d5267c8f73727..dfc5e6eb5655f6f7d5bf4637feb1280cf311a4f0 100644 --- a/src/message.rs +++ b/src/message.rs @@ -17,7 +17,7 @@ use ns; use body; use stanza_error; -use chatstates; +use chatstates::ChatState; use receipts::Receipt; use delay; use attention::Attention; @@ -29,7 +29,7 @@ use eme; pub enum MessagePayload { Body(body::Body), StanzaError(stanza_error::StanzaError), - ChatState(chatstates::ChatState), + ChatState(ChatState), Receipt(Receipt), Delay(delay::Delay), Attention(Attention), @@ -115,7 +115,7 @@ pub fn parse_message(root: &Element) -> Result { Some(MessagePayload::Body(body)) } else if let Ok(stanza_error) = stanza_error::parse_stanza_error(elem) { Some(MessagePayload::StanzaError(stanza_error)) - } else if let Ok(chatstate) = chatstates::parse_chatstate(elem) { + } else if let Ok(chatstate) = ChatState::try_from(elem) { Some(MessagePayload::ChatState(chatstate)) } else if let Ok(receipt) = Receipt::try_from(elem) { Some(MessagePayload::Receipt(receipt)) @@ -149,7 +149,7 @@ pub fn serialise_payload(payload: &MessagePayload) -> Element { MessagePayload::Body(ref body) => body::serialise(body), MessagePayload::StanzaError(ref stanza_error) => stanza_error::serialise(stanza_error), MessagePayload::Attention(ref attention) => attention.into(), - MessagePayload::ChatState(ref chatstate) => chatstates::serialise(chatstate), + MessagePayload::ChatState(ref chatstate) => chatstate.into(), MessagePayload::Receipt(ref receipt) => receipt.into(), MessagePayload::Delay(ref delay) => delay::serialise(delay), MessagePayload::MessageCorrect(ref replace) => replace.into(), From 0f58e650b75974d3419df3161d947a64efdf50f5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 20:42:12 +0100 Subject: [PATCH 143/698] delay: Switch to Into/TryFrom. --- src/delay.rs | 82 +++++++++++++++++++++++------------------------ src/forwarding.rs | 10 +++--- src/message.rs | 8 ++--- src/presence.rs | 9 +++--- 4 files changed, 55 insertions(+), 54 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index 2b2149eb6a5526b9249ceba9b82bcaca1135ba93..a15b61bcac8340fc1320e262975fbef2b16d9383 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -4,7 +4,9 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::{Element, IntoElements, ElementEmitter}; +use std::convert::TryFrom; + +use minidom::Element; use error::Error; use jid::Jid; @@ -18,54 +20,50 @@ pub struct Delay { pub data: Option, } -pub fn parse_delay(root: &Element) -> Result { - if !root.is("delay", ns::DELAY) { - return Err(Error::ParseError("This is not a delay element.")); - } - for _ in root.children() { - return Err(Error::ParseError("Unknown child in delay element.")); - } - let from = root.attr("from").and_then(|value| value.parse().ok()); - let stamp = root.attr("stamp").ok_or(Error::ParseError("Mandatory argument 'stamp' not present in delay element."))?.to_owned(); - let data = match root.text().as_ref() { - "" => None, - text => Some(text.to_owned()), - }; - Ok(Delay { - from: from, - stamp: stamp, - data: data, - }) -} +impl<'a> TryFrom<&'a Element> for Delay { + type Error = Error; -pub fn serialise(delay: &Delay) -> Element { - Element::builder("delay") - .ns(ns::DELAY) - .attr("from", delay.from.clone().and_then(|value| Some(String::from(value)))) - .attr("stamp", delay.stamp.clone()) - .append(delay.data.clone()) - .build() + fn try_from(elem: &'a Element) -> Result { + if !elem.is("delay", ns::DELAY) { + return Err(Error::ParseError("This is not a delay element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in delay element.")); + } + let from = elem.attr("from").and_then(|value| value.parse().ok()); + let stamp = elem.attr("stamp").ok_or(Error::ParseError("Mandatory argument 'stamp' not present in delay element."))?.to_owned(); + let data = match elem.text().as_ref() { + "" => None, + text => Some(text.to_owned()), + }; + Ok(Delay { + from: from, + stamp: stamp, + data: data, + }) + } } -impl IntoElements for Delay { - fn into_elements(self, emitter: &mut ElementEmitter) { - let elem = serialise(&self); - emitter.append_child(elem) +impl<'a> Into for &'a Delay { + fn into(self) -> Element { + Element::builder("delay") + .ns(ns::DELAY) + .attr("from", self.from.clone().and_then(|value| Some(String::from(value)))) + .attr("stamp", self.stamp.clone()) + .append(self.data.clone()) + .build() } } #[cfg(test)] mod tests { use std::str::FromStr; - use minidom::Element; - use error::Error; - use jid::Jid; - use delay; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let delay = delay::parse_delay(&elem).unwrap(); + let delay = Delay::try_from(&elem).unwrap(); assert_eq!(delay.from, Some(Jid::from_str("capulet.com").unwrap())); assert_eq!(delay.stamp, "2002-09-10T23:08:25Z"); assert_eq!(delay.data, None); @@ -74,7 +72,7 @@ mod tests { #[test] fn test_unknown() { let elem: Element = "".parse().unwrap(); - let error = delay::parse_delay(&elem).unwrap_err(); + let error = Delay::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -85,7 +83,7 @@ mod tests { #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = delay::parse_delay(&elem).unwrap_err(); + let error = Delay::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -96,24 +94,24 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let delay = delay::Delay { + let delay = Delay { from: None, stamp: "2002-09-10T23:08:25Z".to_owned(), data: None, }; - let elem2 = delay::serialise(&delay); + let elem2 = (&delay).into(); assert_eq!(elem, elem2); } #[test] fn test_serialise_data() { let elem: Element = "Reason".parse().unwrap(); - let delay = delay::Delay { + let delay = Delay { from: Some(Jid::from_str("juliet@example.org").unwrap()), stamp: "2002-09-10T23:08:25Z".to_owned(), data: Some(String::from("Reason")), }; - let elem2 = delay::serialise(&delay); + let elem2 = (&delay).into(); assert_eq!(elem, elem2); } } diff --git a/src/forwarding.rs b/src/forwarding.rs index 991d508ada8a4e16e622d0360cb9e42a36b0b000..d34414578e429ad1b7dbdcb0eeb0763b1506c1b9 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -4,18 +4,20 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use error::Error; -use delay; +use delay::Delay; use message; use ns; #[derive(Debug, Clone)] pub struct Forwarded { - pub delay: Option, + pub delay: Option, // XXX: really? Option? pub stanza: Option, } @@ -28,7 +30,7 @@ pub fn parse_forwarded(root: &Element) -> Result { let mut stanza = None; for child in root.children() { if child.is("delay", ns::DELAY) { - delay = Some(delay::parse_delay(child)?); + delay = Some(Delay::try_from(child)?); } else if child.is("message", ns::JABBER_CLIENT) { stanza = Some(message::parse_message(child)?); // TODO: also handle the five other possibilities. @@ -45,7 +47,7 @@ pub fn parse_forwarded(root: &Element) -> Result { pub fn serialise(forwarded: &Forwarded) -> Element { Element::builder("forwarded") .ns(ns::FORWARD) - .append(forwarded.delay.clone()) + .append(match forwarded.delay { Some(ref delay) => { let elem: Element = delay.into(); Some(elem) }, None => None }) .append(forwarded.stanza.clone()) .build() } diff --git a/src/message.rs b/src/message.rs index dfc5e6eb5655f6f7d5bf4637feb1280cf311a4f0..476920a04adf54b360969d2766c879d6034c9be7 100644 --- a/src/message.rs +++ b/src/message.rs @@ -19,7 +19,7 @@ use body; use stanza_error; use chatstates::ChatState; use receipts::Receipt; -use delay; +use delay::Delay; use attention::Attention; use message_correct::Replace; use eme; @@ -31,7 +31,7 @@ pub enum MessagePayload { StanzaError(stanza_error::StanzaError), ChatState(ChatState), Receipt(Receipt), - Delay(delay::Delay), + Delay(Delay), Attention(Attention), MessageCorrect(Replace), ExplicitMessageEncryption(eme::ExplicitMessageEncryption), @@ -119,7 +119,7 @@ pub fn parse_message(root: &Element) -> Result { Some(MessagePayload::ChatState(chatstate)) } else if let Ok(receipt) = Receipt::try_from(elem) { Some(MessagePayload::Receipt(receipt)) - } else if let Ok(delay) = delay::parse_delay(elem) { + } else if let Ok(delay) = Delay::try_from(elem) { Some(MessagePayload::Delay(delay)) } else if let Ok(attention) = Attention::try_from(elem) { Some(MessagePayload::Attention(attention)) @@ -151,7 +151,7 @@ pub fn serialise_payload(payload: &MessagePayload) -> Element { MessagePayload::Attention(ref attention) => attention.into(), MessagePayload::ChatState(ref chatstate) => chatstate.into(), MessagePayload::Receipt(ref receipt) => receipt.into(), - MessagePayload::Delay(ref delay) => delay::serialise(delay), + MessagePayload::Delay(ref delay) => delay.into(), MessagePayload::MessageCorrect(ref replace) => replace.into(), MessagePayload::ExplicitMessageEncryption(ref eme) => eme::serialise(eme), } diff --git a/src/presence.rs b/src/presence.rs index bf7b57dfc495c53d1722e030a126df451cbe3e15..de5944c2e7c654443abe2954e0b8bfc9245a900a 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -4,6 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; use std::str::FromStr; use std::collections::BTreeMap; @@ -16,7 +17,7 @@ use error::Error; use ns; use stanza_error; -use delay; +use delay::Delay; use ecaps2; #[derive(Debug, Clone, PartialEq)] @@ -51,7 +52,7 @@ pub enum PresencePayload { Status(Status), Priority(Priority), StanzaError(stanza_error::StanzaError), - Delay(delay::Delay), + Delay(Delay), ECaps2(ecaps2::ECaps2), } @@ -180,7 +181,7 @@ pub fn parse_presence(root: &Element) -> Result { } else { let payload = if let Ok(stanza_error) = stanza_error::parse_stanza_error(elem) { Some(PresencePayload::StanzaError(stanza_error)) - } else if let Ok(delay) = delay::parse_delay(elem) { + } else if let Ok(delay) = Delay::try_from(elem) { Some(PresencePayload::Delay(delay)) } else if let Ok(ecaps2) = ecaps2::parse_ecaps2(elem) { Some(PresencePayload::ECaps2(ecaps2)) @@ -226,7 +227,7 @@ pub fn serialise_payload(payload: &PresencePayload) -> Element { .build() }, PresencePayload::StanzaError(ref stanza_error) => stanza_error::serialise(stanza_error), - PresencePayload::Delay(ref delay) => delay::serialise(delay), + PresencePayload::Delay(ref delay) => delay.into(), PresencePayload::ECaps2(ref ecaps2) => ecaps2::serialise(ecaps2), } } From 1ec38066299661bf0ba61b0039353576beb3933d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 20:46:11 +0100 Subject: [PATCH 144/698] hashes: Switch to Into/TryFrom. --- src/ecaps2.rs | 9 ++++---- src/hashes.rs | 60 ++++++++++++++++++++++++++---------------------- src/jingle_ft.rs | 11 ++++----- 3 files changed, 43 insertions(+), 37 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index aa2cde7e10ca423e351ecb7b71baae6effe1c894..62242db7e2ef20e336c261fdc58621009935d125 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -4,10 +4,11 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use disco::{Feature, Identity, Disco}; use data_forms::DataForm; -use hashes; -use hashes::{Hash, parse_hash}; +use hashes::Hash; use minidom::Element; use error::Error; @@ -31,7 +32,7 @@ pub fn parse_ecaps2(root: &Element) -> Result { let mut hashes = vec!(); for child in root.children() { if child.is("hash", ns::HASHES) { - let hash = parse_hash(child)?; + let hash = Hash::try_from(child)?; hashes.push(hash); } else { return Err(Error::ParseError("Unknown child in ecaps2 element.")); @@ -47,7 +48,7 @@ pub fn serialise(ecaps2: &ECaps2) -> Element { .ns(ns::ECAPS2) .build(); for hash in ecaps2.hashes.clone() { - let hash_elem = hashes::serialise(&hash); + let hash_elem = (&hash).into(); c.append_child(hash_elem); } c diff --git a/src/hashes.rs b/src/hashes.rs index c489a32fcde701f203f219409bd372a3d6d76a2b..6e7bcb2db2c9510b32bc1957e57ffc1666b7672a 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use error::Error; @@ -16,42 +18,46 @@ pub struct Hash { pub hash: String, } -pub fn parse_hash(root: &Element) -> Result { - if !root.is("hash", ns::HASHES) { - return Err(Error::ParseError("This is not a hash element.")); - } - for _ in root.children() { - return Err(Error::ParseError("Unknown child in hash element.")); +impl<'a> TryFrom<&'a Element> for Hash { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if !elem.is("hash", ns::HASHES) { + return Err(Error::ParseError("This is not a hash element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in hash element.")); + } + let algo = elem.attr("algo").ok_or(Error::ParseError("Mandatory argument 'algo' not present in hash element."))?.to_owned(); + let hash = match elem.text().as_ref() { + "" => return Err(Error::ParseError("Hash element shouldn’t be empty.")), + text => text.to_owned(), + }; + Ok(Hash { + algo: algo, + hash: hash, + }) } - let algo = root.attr("algo").ok_or(Error::ParseError("Mandatory argument 'algo' not present in hash element."))?.to_owned(); - let hash = match root.text().as_ref() { - "" => return Err(Error::ParseError("Hash element shouldn’t be empty.")), - text => text.to_owned(), - }; - Ok(Hash { - algo: algo, - hash: hash, - }) } -pub fn serialise(hash: &Hash) -> Element { - Element::builder("hash") - .ns(ns::HASHES) - .attr("algo", hash.algo.clone()) - .append(hash.hash.clone()) - .build() +impl<'a> Into for &'a Hash { + fn into(self) -> Element { + Element::builder("hash") + .ns(ns::HASHES) + .attr("algo", self.algo.clone()) + .append(self.hash.clone()) + .build() + } } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use hashes; + use super::*; #[test] fn test_simple() { let elem: Element = "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=".parse().unwrap(); - let hash = hashes::parse_hash(&elem).unwrap(); + let hash = Hash::try_from(&elem).unwrap(); assert_eq!(hash.algo, "sha-256"); assert_eq!(hash.hash, "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU="); } @@ -59,7 +65,7 @@ mod tests { #[test] fn test_unknown() { let elem: Element = "".parse().unwrap(); - let error = hashes::parse_hash(&elem).unwrap_err(); + let error = Hash::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -70,7 +76,7 @@ mod tests { #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = hashes::parse_hash(&elem).unwrap_err(); + let error = Hash::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index f2b981faa45d8f4a967769a243f4da01bfa43e65..efbe9c9d8b355a8e3d43b8e3fb3d15c051f0c79d 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -6,8 +6,7 @@ use std::convert::TryFrom; -use hashes; -use hashes::{Hash, parse_hash}; +use hashes::Hash; use minidom::{Element, IntoElements, ElementEmitter}; @@ -32,7 +31,7 @@ impl IntoElements for Range { }) .build(); for hash in self.hashes { - elem.append_child(hashes::serialise(&hash)); + elem.append_child((&hash).into()); } emitter.append_child(elem); } @@ -149,7 +148,7 @@ impl<'a> TryFrom<&'a Element> for Description { if !hash_element.is("hash", ns::HASHES) { return Err(Error::ParseError("Unknown element in JingleFT range.")); } - range_hashes.push(parse_hash(hash_element)?); + range_hashes.push(Hash::try_from(hash_element)?); } range = Some(Range { offset: offset, @@ -157,7 +156,7 @@ impl<'a> TryFrom<&'a Element> for Description { hashes: range_hashes, }); } else if file_payload.is("hash", ns::HASHES) { - hashes.push(parse_hash(file_payload)?); + hashes.push(Hash::try_from(file_payload)?); } else { return Err(Error::ParseError("Unknown element in JingleFT file.")); } @@ -220,7 +219,7 @@ impl<'a> Into for &'a File { .build()); } for hash in self.hashes.clone() { - root.append_child(hashes::serialise(&hash)); + root.append_child((&hash).into()); } root } From 0dd0b444b390bef071644ecea38e46e80a1102e2 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 20:48:41 +0100 Subject: [PATCH 145/698] media_element: Switch to Into/TryFrom. --- src/data_forms.rs | 5 ++-- src/media_element.rs | 66 +++++++++++++++++++++++--------------------- 2 files changed, 38 insertions(+), 33 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index 3b3757efea29cd00abeb4ccac66b6b9d0afe8b2a..94dae1cac83d690e3b00a7eefdaaa3c7653edbe6 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -4,6 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; use std::str::FromStr; use minidom::Element; @@ -11,7 +12,7 @@ use minidom::Element; use error::Error; use ns; -use media_element::{MediaElement, parse_media_element}; +use media_element::MediaElement; #[derive(Debug, Clone)] pub struct Field { @@ -74,7 +75,7 @@ pub fn parse_data_form(root: &Element) -> Result { if element.is("value", ns::DATA_FORMS) { values.push(element.text()); } else if element.is("media", ns::MEDIA_ELEMENT) { - match parse_media_element(element) { + match MediaElement::try_from(element) { Ok(media_element) => media.push(media_element), Err(_) => (), // TODO: is it really nice to swallow this error? } diff --git a/src/media_element.rs b/src/media_element.rs index a3c360e576d91cffb4bb9aa8b9a2995fdefe45eb..be2adda105eb6b31ee96a8626a920293748c9d1c 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use error::Error; @@ -23,40 +25,42 @@ pub struct MediaElement { pub uris: Vec, } -pub fn parse_media_element(root: &Element) -> Result { - if !root.is("media", ns::MEDIA_ELEMENT) { - return Err(Error::ParseError("This is not a media element.")); - } +impl<'a> TryFrom<&'a Element> for MediaElement { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if !elem.is("media", ns::MEDIA_ELEMENT) { + return Err(Error::ParseError("This is not a media element.")); + } - let width = root.attr("width").and_then(|width| width.parse().ok()); - let height = root.attr("height").and_then(|height| height.parse().ok()); - let mut uris = vec!(); - for uri in root.children() { - if uri.is("uri", ns::MEDIA_ELEMENT) { - let type_ = uri.attr("type").ok_or(Error::ParseError("Attribute type on uri is mandatory."))?; - let text = uri.text().trim().to_owned(); - if text == "" { - return Err(Error::ParseError("URI missing in uri.")); + let width = elem.attr("width").and_then(|width| width.parse().ok()); + let height = elem.attr("height").and_then(|height| height.parse().ok()); + let mut uris = vec!(); + for uri in elem.children() { + if uri.is("uri", ns::MEDIA_ELEMENT) { + let type_ = uri.attr("type").ok_or(Error::ParseError("Attribute type on uri is mandatory."))?; + let text = uri.text().trim().to_owned(); + if text == "" { + return Err(Error::ParseError("URI missing in uri.")); + } + uris.push(URI { type_: type_.to_owned(), uri: text }); + } else { + return Err(Error::ParseError("Unknown child in media element.")); } - uris.push(URI { type_: type_.to_owned(), uri: text }); - } else { - return Err(Error::ParseError("Unknown child in media element.")); } + Ok(MediaElement { width: width, height: height, uris: uris }) } - Ok(MediaElement { width: width, height: height, uris: uris }) } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use media_element; + use super::*; use data_forms; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let media = media_element::parse_media_element(&elem).unwrap(); + let media = MediaElement::try_from(&elem).unwrap(); assert!(media.width.is_none()); assert!(media.height.is_none()); assert!(media.uris.is_empty()); @@ -65,7 +69,7 @@ mod tests { #[test] fn test_width_height() { let elem: Element = "".parse().unwrap(); - let media = media_element::parse_media_element(&elem).unwrap(); + let media = MediaElement::try_from(&elem).unwrap(); assert_eq!(media.width.unwrap(), 32); assert_eq!(media.height.unwrap(), 32); } @@ -73,7 +77,7 @@ mod tests { #[test] fn test_uri() { let elem: Element = "https://example.org/".parse().unwrap(); - let media = media_element::parse_media_element(&elem).unwrap(); + let media = MediaElement::try_from(&elem).unwrap(); assert_eq!(media.uris.len(), 1); assert_eq!(media.uris[0].type_, "text/html"); assert_eq!(media.uris[0].uri, "https://example.org/"); @@ -82,26 +86,26 @@ mod tests { #[test] fn test_invalid_width_height() { let elem: Element = "".parse().unwrap(); - let media = media_element::parse_media_element(&elem).unwrap(); + let media = MediaElement::try_from(&elem).unwrap(); assert!(media.width.is_none()); let elem: Element = "".parse().unwrap(); - let media = media_element::parse_media_element(&elem).unwrap(); + let media = MediaElement::try_from(&elem).unwrap(); assert!(media.width.is_none()); let elem: Element = "".parse().unwrap(); - let media = media_element::parse_media_element(&elem).unwrap(); + let media = MediaElement::try_from(&elem).unwrap(); assert!(media.height.is_none()); let elem: Element = "".parse().unwrap(); - let media = media_element::parse_media_element(&elem).unwrap(); + let media = MediaElement::try_from(&elem).unwrap(); assert!(media.height.is_none()); } #[test] fn test_unknown_child() { let elem: Element = "".parse().unwrap(); - let error = media_element::parse_media_element(&elem).unwrap_err(); + let error = MediaElement::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -112,7 +116,7 @@ mod tests { #[test] fn test_bad_uri() { let elem: Element = "https://example.org/".parse().unwrap(); - let error = media_element::parse_media_element(&elem).unwrap_err(); + let error = MediaElement::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -120,7 +124,7 @@ mod tests { assert_eq!(message, "Attribute type on uri is mandatory."); let elem: Element = "".parse().unwrap(); - let error = media_element::parse_media_element(&elem).unwrap_err(); + let error = MediaElement::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -142,7 +146,7 @@ mod tests { http://victim.example.com/challenges/speech.mp3?F3A6292C "#.parse().unwrap(); - let media = media_element::parse_media_element(&elem).unwrap(); + let media = MediaElement::try_from(&elem).unwrap(); assert!(media.width.is_none()); assert!(media.height.is_none()); assert_eq!(media.uris.len(), 3); From 8673b8f90e2eabb4e1c50057fcfbd4bbb3eab807 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 20:51:39 +0100 Subject: [PATCH 146/698] data_forms: Switch to Into/TryFrom. --- src/data_forms.rs | 106 ++++++++++++++++++++++--------------------- src/disco.rs | 6 ++- src/mam.rs | 5 +- src/media_element.rs | 4 +- 4 files changed, 62 insertions(+), 59 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index 94dae1cac83d690e3b00a7eefdaaa3c7653edbe6..a83ebb824b608dc507989048630f8bd4d06e2a9a 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -53,70 +53,72 @@ pub struct DataForm { pub fields: Vec, } -pub fn parse_data_form(root: &Element) -> Result { - if !root.is("x", ns::DATA_FORMS) { - return Err(Error::ParseError("This is not a data form element.")); - } +impl<'a> TryFrom<&'a Element> for DataForm { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if !elem.is("x", ns::DATA_FORMS) { + return Err(Error::ParseError("This is not a data form element.")); + } - let type_: DataFormType = match root.attr("type") { - Some(type_) => type_.parse()?, - None => return Err(Error::ParseError("Type attribute on data form is mandatory.")), - }; - let mut fields = vec!(); - let mut form_type = None; - for field in root.children() { - if field.is("field", ns::DATA_FORMS) { - let var = field.attr("var").ok_or(Error::ParseError("Field must have a 'var' attribute."))?; - let field_type = field.attr("type").unwrap_or("text-single"); - let label = field.attr("label").and_then(|label| label.parse().ok()); - let mut values = vec!(); - let mut media = vec!(); - for element in field.children() { - if element.is("value", ns::DATA_FORMS) { - values.push(element.text()); - } else if element.is("media", ns::MEDIA_ELEMENT) { - match MediaElement::try_from(element) { - Ok(media_element) => media.push(media_element), - Err(_) => (), // TODO: is it really nice to swallow this error? + let type_: DataFormType = match elem.attr("type") { + Some(type_) => type_.parse()?, + None => return Err(Error::ParseError("Type attribute on data form is mandatory.")), + }; + let mut fields = vec!(); + let mut form_type = None; + for field in elem.children() { + if field.is("field", ns::DATA_FORMS) { + let var = field.attr("var").ok_or(Error::ParseError("Field must have a 'var' attribute."))?; + let field_type = field.attr("type").unwrap_or("text-single"); + let label = field.attr("label").and_then(|label| label.parse().ok()); + let mut values = vec!(); + let mut media = vec!(); + for element in field.children() { + if element.is("value", ns::DATA_FORMS) { + values.push(element.text()); + } else if element.is("media", ns::MEDIA_ELEMENT) { + match MediaElement::try_from(element) { + Ok(media_element) => media.push(media_element), + Err(_) => (), // TODO: is it really nice to swallow this error? + } + } else { + return Err(Error::ParseError("Field child isn’t a value or media element.")); } - } else { - return Err(Error::ParseError("Field child isn’t a value or media element.")); } - } - if var == "FORM_TYPE" && field_type == "hidden" { - if form_type != None { - return Err(Error::ParseError("More than one FORM_TYPE in a data form.")); - } - if values.len() != 1 { - return Err(Error::ParseError("Wrong number of values in FORM_TYPE.")); + if var == "FORM_TYPE" && field_type == "hidden" { + if form_type != None { + return Err(Error::ParseError("More than one FORM_TYPE in a data form.")); + } + if values.len() != 1 { + return Err(Error::ParseError("Wrong number of values in FORM_TYPE.")); + } + form_type = Some(values[0].clone()); } - form_type = Some(values[0].clone()); + fields.push(Field { + var: var.to_owned(), + type_: field_type.to_owned(), + label: label, + values: values, + media: media, + }); + } else { + return Err(Error::ParseError("Unknown field type in data form.")); } - fields.push(Field { - var: var.to_owned(), - type_: field_type.to_owned(), - label: label, - values: values, - media: media, - }); - } else { - return Err(Error::ParseError("Unknown field type in data form.")); } + Ok(DataForm { type_: type_, form_type: form_type, fields: fields }) } - Ok(DataForm { type_: type_, form_type: form_type, fields: fields }) } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use data_forms; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let form = data_forms::parse_data_form(&elem).unwrap(); - assert_eq!(form.type_, data_forms::DataFormType::Result_); + let form = DataForm::try_from(&elem).unwrap(); + assert_eq!(form.type_, DataFormType::Result_); assert!(form.form_type.is_none()); assert!(form.fields.is_empty()); } @@ -124,7 +126,7 @@ mod tests { #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); - let error = data_forms::parse_data_form(&elem).unwrap_err(); + let error = DataForm::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -132,7 +134,7 @@ mod tests { assert_eq!(message, "Type attribute on data form is mandatory."); let elem: Element = "".parse().unwrap(); - let error = data_forms::parse_data_form(&elem).unwrap_err(); + let error = DataForm::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -143,7 +145,7 @@ mod tests { #[test] fn test_wrong_child() { let elem: Element = "".parse().unwrap(); - let error = data_forms::parse_data_form(&elem).unwrap_err(); + let error = DataForm::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/disco.rs b/src/disco.rs index bc448091b830ee903356d444b05c1d046f1bd975..9fb529060cd915f7c24ccd1a06809874776045b9 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -4,12 +4,14 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use error::Error; use ns; -use data_forms::{DataForm, DataFormType, parse_data_form}; +use data_forms::{DataForm, DataFormType}; #[derive(Debug, Clone, PartialEq)] pub struct Feature { @@ -74,7 +76,7 @@ pub fn parse_disco(root: &Element) -> Result { name: name, }); } else if child.is("x", ns::DATA_FORMS) { - let data_form = parse_data_form(child)?; + let data_form = DataForm::try_from(child)?; match data_form.type_ { DataFormType::Result_ => (), _ => return Err(Error::ParseError("Data form must have a 'result' type in disco#info.")), diff --git a/src/mam.rs b/src/mam.rs index 77a3b6cf0caf988d0ee8294e2f7ca1519bbafcd3..308cc1c0a38a83cf56f8d594d2fe0897c7677691 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -11,7 +11,6 @@ use jid::Jid; use error::Error; -use data_forms; use data_forms::DataForm; use rsm::Set; use forwarding; @@ -62,7 +61,7 @@ pub fn parse_query(root: &Element) -> Result { let mut set = None; for child in root.children() { if child.is("x", ns::DATA_FORMS) { - form = Some(data_forms::parse_data_form(child)?); + form = Some(DataForm::try_from(child)?); } else if child.is("set", ns::RSM) { set = Some(Set::try_from(child)?); } else { @@ -177,7 +176,7 @@ pub fn serialise_query(query: &Query) -> Element { .attr("node", query.node.clone()) .build(); //if let Some(form) = query.form { - // elem.append_child(data_forms::serialise(&form)); + // elem.append_child((&form).into()); //} if let Some(ref set) = query.set { elem.append_child(set.into()); diff --git a/src/media_element.rs b/src/media_element.rs index be2adda105eb6b31ee96a8626a920293748c9d1c..d4d31985caad71b54afa050ddd0594a30a9650f3 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -55,7 +55,7 @@ impl<'a> TryFrom<&'a Element> for MediaElement { #[cfg(test)] mod tests { use super::*; - use data_forms; + use data_forms::DataForm; #[test] fn test_simple() { @@ -177,7 +177,7 @@ mod tests { [ ... ] "#.parse().unwrap(); - let form = data_forms::parse_data_form(&elem).unwrap(); + let form = DataForm::try_from(&elem).unwrap(); assert_eq!(form.fields.len(), 1); assert_eq!(form.fields[0].var, "ocr"); assert_eq!(form.fields[0].media[0].width, Some(290)); From 2f05d02d239ad8cf412d8411c2a03a93d61f8528 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 20:53:43 +0100 Subject: [PATCH 147/698] forwarding: Switch to Into/TryFrom. --- src/forwarding.rs | 66 +++++++++++++++++++++++++---------------------- src/mam.rs | 5 ++-- 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/src/forwarding.rs b/src/forwarding.rs index d34414578e429ad1b7dbdcb0eeb0763b1506c1b9..75b7fbcc8a307946346e9d07298e744c215ce5de 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -22,52 +22,56 @@ pub struct Forwarded { pub stanza: Option, } -pub fn parse_forwarded(root: &Element) -> Result { - if !root.is("forwarded", ns::FORWARD) { - return Err(Error::ParseError("This is not a forwarded element.")); - } - let mut delay = None; - let mut stanza = None; - for child in root.children() { - if child.is("delay", ns::DELAY) { - delay = Some(Delay::try_from(child)?); - } else if child.is("message", ns::JABBER_CLIENT) { - stanza = Some(message::parse_message(child)?); - // TODO: also handle the five other possibilities. - } else { - return Err(Error::ParseError("Unknown child in forwarded element.")); +impl<'a> TryFrom<&'a Element> for Forwarded { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if !elem.is("forwarded", ns::FORWARD) { + return Err(Error::ParseError("This is not a forwarded element.")); } + let mut delay = None; + let mut stanza = None; + for child in elem.children() { + if child.is("delay", ns::DELAY) { + delay = Some(Delay::try_from(child)?); + } else if child.is("message", ns::JABBER_CLIENT) { + stanza = Some(message::parse_message(child)?); + // TODO: also handle the five other possibilities. + } else { + return Err(Error::ParseError("Unknown child in forwarded element.")); + } + } + Ok(Forwarded { + delay: delay, + stanza: stanza, + }) } - Ok(Forwarded { - delay: delay, - stanza: stanza, - }) } -pub fn serialise(forwarded: &Forwarded) -> Element { - Element::builder("forwarded") - .ns(ns::FORWARD) - .append(match forwarded.delay { Some(ref delay) => { let elem: Element = delay.into(); Some(elem) }, None => None }) - .append(forwarded.stanza.clone()) - .build() +impl<'a> Into for &'a Forwarded { + fn into(self) -> Element { + Element::builder("forwarded") + .ns(ns::FORWARD) + .append(match self.delay { Some(ref delay) => { let elem: Element = delay.into(); Some(elem) }, None => None }) + .append(self.stanza.clone()) + .build() + } } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use forwarding; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - forwarding::parse_forwarded(&elem).unwrap(); + Forwarded::try_from(&elem).unwrap(); } #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = forwarding::parse_forwarded(&elem).unwrap_err(); + let error = Forwarded::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -78,8 +82,8 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let forwarded = forwarding::Forwarded { delay: None, stanza: None }; - let elem2 = forwarding::serialise(&forwarded); + let forwarded = Forwarded { delay: None, stanza: None }; + let elem2 = (&forwarded).into(); assert_eq!(elem, elem2); } } diff --git a/src/mam.rs b/src/mam.rs index 308cc1c0a38a83cf56f8d594d2fe0897c7677691..a826decc45e5e2b1111c8cb25ab7db2ed0ca12fc 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -13,7 +13,6 @@ use error::Error; use data_forms::DataForm; use rsm::Set; -use forwarding; use forwarding::Forwarded; use ns; @@ -86,7 +85,7 @@ pub fn parse_result(root: &Element) -> Result { let mut forwarded = None; for child in root.children() { if child.is("forwarded", ns::FORWARD) { - forwarded = Some(forwarding::parse_forwarded(child)?); + forwarded = Some(Forwarded::try_from(child)?); } else { return Err(Error::ParseError("Unknown child in result element.")); } @@ -190,7 +189,7 @@ pub fn serialise_result(result: &Result_) -> Element { .attr("queryid", result.queryid.clone()) .attr("id", result.id.clone()) .build(); - elem.append_child(forwarding::serialise(&result.forwarded)); + elem.append_child((&result.forwarded).into()); elem } From f963715e77c2899a35e3d521609b9696c3da28f8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 20:58:22 +0100 Subject: [PATCH 148/698] ecaps2: Switch to Into/TryFrom. --- src/ecaps2.rs | 55 ++++++++++++++++++++++++++----------------------- src/presence.rs | 8 +++---- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 62242db7e2ef20e336c261fdc58621009935d125..fdddfdaad507f73f945d250e8779ee72c717789d 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -25,33 +25,37 @@ pub struct ECaps2 { hashes: Vec, } -pub fn parse_ecaps2(root: &Element) -> Result { - if !root.is("c", ns::ECAPS2) { - return Err(Error::ParseError("This is not an ecaps2 element.")); - } - let mut hashes = vec!(); - for child in root.children() { - if child.is("hash", ns::HASHES) { - let hash = Hash::try_from(child)?; - hashes.push(hash); - } else { - return Err(Error::ParseError("Unknown child in ecaps2 element.")); +impl<'a> TryFrom<&'a Element> for ECaps2 { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if !elem.is("c", ns::ECAPS2) { + return Err(Error::ParseError("This is not an ecaps2 element.")); + } + let mut hashes = vec!(); + for child in elem.children() { + if child.is("hash", ns::HASHES) { + let hash = Hash::try_from(child)?; + hashes.push(hash); + } else { + return Err(Error::ParseError("Unknown child in ecaps2 element.")); + } } + Ok(ECaps2 { + hashes: hashes, + }) } - Ok(ECaps2 { - hashes: hashes, - }) } -pub fn serialise(ecaps2: &ECaps2) -> Element { - let mut c = Element::builder("c") - .ns(ns::ECAPS2) - .build(); - for hash in ecaps2.hashes.clone() { - let hash_elem = (&hash).into(); - c.append_child(hash_elem); +impl<'a> Into for &'a ECaps2 { + fn into(self) -> Element { + Element::builder("c") + .ns(ns::ECAPS2) + .append(self.hashes.iter() + .map(|hash| hash.into()) + .collect::>()) + .build() } - c } fn compute_item(field: &str) -> Vec { @@ -161,8 +165,7 @@ pub fn hash_ecaps2(data: &[u8], algo: &str) -> String { #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; + use super::*; use disco; use ecaps2; use base64; @@ -170,7 +173,7 @@ mod tests { #[test] fn test_parse() { let elem: Element = "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=".parse().unwrap(); - let ecaps2 = ecaps2::parse_ecaps2(&elem).unwrap(); + let ecaps2 = ECaps2::try_from(&elem).unwrap(); assert_eq!(ecaps2.hashes.len(), 2); assert_eq!(ecaps2.hashes[0].algo, "sha-256"); assert_eq!(ecaps2.hashes[0].hash, "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4="); @@ -181,7 +184,7 @@ mod tests { #[test] fn test_invalid_child() { let elem: Element = "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=".parse().unwrap(); - let error = ecaps2::parse_ecaps2(&elem).unwrap_err(); + let error = ECaps2::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/presence.rs b/src/presence.rs index de5944c2e7c654443abe2954e0b8bfc9245a900a..6ae97ecb9ae2371c5dbda2460080d79c7b552047 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -18,7 +18,7 @@ use ns; use stanza_error; use delay::Delay; -use ecaps2; +use ecaps2::ECaps2; #[derive(Debug, Clone, PartialEq)] pub enum Show { @@ -53,7 +53,7 @@ pub enum PresencePayload { Priority(Priority), StanzaError(stanza_error::StanzaError), Delay(Delay), - ECaps2(ecaps2::ECaps2), + ECaps2(ECaps2), } #[derive(Debug, Clone, PartialEq)] @@ -183,7 +183,7 @@ pub fn parse_presence(root: &Element) -> Result { Some(PresencePayload::StanzaError(stanza_error)) } else if let Ok(delay) = Delay::try_from(elem) { Some(PresencePayload::Delay(delay)) - } else if let Ok(ecaps2) = ecaps2::parse_ecaps2(elem) { + } else if let Ok(ecaps2) = ECaps2::try_from(elem) { Some(PresencePayload::ECaps2(ecaps2)) } else { None @@ -228,7 +228,7 @@ pub fn serialise_payload(payload: &PresencePayload) -> Element { }, PresencePayload::StanzaError(ref stanza_error) => stanza_error::serialise(stanza_error), PresencePayload::Delay(ref delay) => delay.into(), - PresencePayload::ECaps2(ref ecaps2) => ecaps2::serialise(ecaps2), + PresencePayload::ECaps2(ref ecaps2) => ecaps2.into(), } } From 2b49d8aa539113474707eafd62a72f27f1c5f522 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 21:01:15 +0100 Subject: [PATCH 149/698] disco: Switch to Into/TryFrom. --- src/disco.rs | 218 +++++++++++++++++++++++++------------------------- src/ecaps2.rs | 7 +- src/iq.rs | 8 +- 3 files changed, 118 insertions(+), 115 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index 9fb529060cd915f7c24ccd1a06809874776045b9..48f4a2d0db93b5e426a877af91a2c06b56dd0031 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -34,121 +34,125 @@ pub struct Disco { pub extensions: Vec, } -pub fn parse_disco(root: &Element) -> Result { - if !root.is("query", ns::DISCO_INFO) { - return Err(Error::ParseError("This is not a disco#info element.")); - } - - let mut identities: Vec = vec!(); - let mut features: Vec = vec!(); - let mut extensions: Vec = vec!(); - - let node = root.attr("node") - .and_then(|node| node.parse().ok()); - - for child in root.children() { - if child.is("feature", ns::DISCO_INFO) { - let feature = child.attr("var") - .ok_or(Error::ParseError("Feature must have a 'var' attribute."))?; - features.push(Feature { - var: feature.to_owned(), - }); - } else if child.is("identity", ns::DISCO_INFO) { - let category = child.attr("category") - .ok_or(Error::ParseError("Identity must have a 'category' attribute."))?; - if category == "" { - return Err(Error::ParseError("Identity must have a non-empty 'category' attribute.")) - } +impl<'a> TryFrom<&'a Element> for Disco { + type Error = Error; - let type_ = child.attr("type") - .ok_or(Error::ParseError("Identity must have a 'type' attribute."))?; - if type_ == "" { - return Err(Error::ParseError("Identity must have a non-empty 'type' attribute.")) - } + fn try_from(elem: &'a Element) -> Result { + if !elem.is("query", ns::DISCO_INFO) { + return Err(Error::ParseError("This is not a disco#info element.")); + } - let xml_lang = child.attr("xml:lang").unwrap_or(""); - let name = child.attr("name") - .and_then(|name| name.parse().ok()); - identities.push(Identity { - category: category.to_owned(), - type_: type_.to_owned(), - xml_lang: xml_lang.to_owned(), - name: name, - }); - } else if child.is("x", ns::DATA_FORMS) { - let data_form = DataForm::try_from(child)?; - match data_form.type_ { - DataFormType::Result_ => (), - _ => return Err(Error::ParseError("Data form must have a 'result' type in disco#info.")), + let mut identities: Vec = vec!(); + let mut features: Vec = vec!(); + let mut extensions: Vec = vec!(); + + let node = elem.attr("node") + .and_then(|node| node.parse().ok()); + + for child in elem.children() { + if child.is("feature", ns::DISCO_INFO) { + let feature = child.attr("var") + .ok_or(Error::ParseError("Feature must have a 'var' attribute."))?; + features.push(Feature { + var: feature.to_owned(), + }); + } else if child.is("identity", ns::DISCO_INFO) { + let category = child.attr("category") + .ok_or(Error::ParseError("Identity must have a 'category' attribute."))?; + if category == "" { + return Err(Error::ParseError("Identity must have a non-empty 'category' attribute.")) + } + + let type_ = child.attr("type") + .ok_or(Error::ParseError("Identity must have a 'type' attribute."))?; + if type_ == "" { + return Err(Error::ParseError("Identity must have a non-empty 'type' attribute.")) + } + + let xml_lang = child.attr("xml:lang").unwrap_or(""); + let name = child.attr("name") + .and_then(|name| name.parse().ok()); + identities.push(Identity { + category: category.to_owned(), + type_: type_.to_owned(), + xml_lang: xml_lang.to_owned(), + name: name, + }); + } else if child.is("x", ns::DATA_FORMS) { + let data_form = DataForm::try_from(child)?; + match data_form.type_ { + DataFormType::Result_ => (), + _ => return Err(Error::ParseError("Data form must have a 'result' type in disco#info.")), + } + match data_form.form_type { + Some(_) => extensions.push(data_form), + None => return Err(Error::ParseError("Data form found without a FORM_TYPE.")), + } + } else { + return Err(Error::ParseError("Unknown element in disco#info.")); } - match data_form.form_type { - Some(_) => extensions.push(data_form), - None => return Err(Error::ParseError("Data form found without a FORM_TYPE.")), - } - } else { - return Err(Error::ParseError("Unknown element in disco#info.")); } - } - /* - // TODO: encode these restrictions only for result disco#info, not get ones. - if identities.is_empty() { - return Err(Error::ParseError("There must be at least one identity in disco#info.")); - } - if features.is_empty() { - return Err(Error::ParseError("There must be at least one feature in disco#info.")); - } - if !features.contains(&Feature { var: ns::DISCO_INFO.to_owned() }) { - return Err(Error::ParseError("disco#info feature not present in disco#info.")); + /* + // TODO: encode these restrictions only for result disco#info, not get ones. + if identities.is_empty() { + return Err(Error::ParseError("There must be at least one identity in disco#info.")); + } + if features.is_empty() { + return Err(Error::ParseError("There must be at least one feature in disco#info.")); + } + if !features.contains(&Feature { var: ns::DISCO_INFO.to_owned() }) { + return Err(Error::ParseError("disco#info feature not present in disco#info.")); + } + */ + + Ok(Disco { + node: node, + identities: identities, + features: features, + extensions: extensions + }) } - */ - - Ok(Disco { - node: node, - identities: identities, - features: features, - extensions: extensions - }) } -pub fn serialise_disco(disco: &Disco) -> Element { - let mut root = Element::builder("query") - .ns(ns::DISCO_INFO) - .attr("node", disco.node.clone()) - .build(); - for identity in &disco.identities { - let identity_element = Element::builder("identity") - .ns(ns::DISCO_INFO) - .attr("category", identity.category.clone()) - .attr("type", identity.type_.clone()) - .attr("xml:lang", identity.xml_lang.clone()) - .attr("name", identity.name.clone()) - .build(); - root.append_child(identity_element); - } - for feature in &disco.features { - let feature_element = Element::builder("feature") - .ns(ns::DISCO_INFO) - .attr("var", feature.var.clone()) - .build(); - root.append_child(feature_element); - } - for _ in &disco.extensions { - panic!("Not yet implemented!"); +impl<'a> Into for &'a Disco { + fn into(self) -> Element { + let mut root = Element::builder("query") + .ns(ns::DISCO_INFO) + .attr("node", self.node.clone()) + .build(); + for identity in &self.identities { + let identity_element = Element::builder("identity") + .ns(ns::DISCO_INFO) + .attr("category", identity.category.clone()) + .attr("type", identity.type_.clone()) + .attr("xml:lang", identity.xml_lang.clone()) + .attr("name", identity.name.clone()) + .build(); + root.append_child(identity_element); + } + for feature in &self.features { + let feature_element = Element::builder("feature") + .ns(ns::DISCO_INFO) + .attr("var", feature.var.clone()) + .build(); + root.append_child(feature_element); + } + for _ in &self.extensions { + panic!("Not yet implemented!"); + } + root } - root } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use disco; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let query = disco::parse_disco(&elem).unwrap(); + let query = Disco::try_from(&elem).unwrap(); assert!(query.node.is_none()); assert_eq!(query.identities.len(), 1); assert_eq!(query.features.len(), 1); @@ -158,7 +162,7 @@ mod tests { #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); - let error = disco::parse_disco(&elem).unwrap_err(); + let error = Disco::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -169,7 +173,7 @@ mod tests { #[test] fn test_invalid_identity() { let elem: Element = "".parse().unwrap(); - let error = disco::parse_disco(&elem).unwrap_err(); + let error = Disco::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -177,7 +181,7 @@ mod tests { assert_eq!(message, "Identity must have a 'category' attribute."); let elem: Element = "".parse().unwrap(); - let error = disco::parse_disco(&elem).unwrap_err(); + let error = Disco::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -185,7 +189,7 @@ mod tests { assert_eq!(message, "Identity must have a non-empty 'category' attribute."); let elem: Element = "".parse().unwrap(); - let error = disco::parse_disco(&elem).unwrap_err(); + let error = Disco::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -193,7 +197,7 @@ mod tests { assert_eq!(message, "Identity must have a 'type' attribute."); let elem: Element = "".parse().unwrap(); - let error = disco::parse_disco(&elem).unwrap_err(); + let error = Disco::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -204,7 +208,7 @@ mod tests { #[test] fn test_invalid_feature() { let elem: Element = "".parse().unwrap(); - let error = disco::parse_disco(&elem).unwrap_err(); + let error = Disco::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -216,7 +220,7 @@ mod tests { #[ignore] fn test_invalid_result() { let elem: Element = "".parse().unwrap(); - let error = disco::parse_disco(&elem).unwrap_err(); + let error = Disco::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -224,7 +228,7 @@ mod tests { assert_eq!(message, "There must be at least one identity in disco#info."); let elem: Element = "".parse().unwrap(); - let error = disco::parse_disco(&elem).unwrap_err(); + let error = Disco::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -232,7 +236,7 @@ mod tests { assert_eq!(message, "There must be at least one feature in disco#info."); let elem: Element = "".parse().unwrap(); - let error = disco::parse_disco(&elem).unwrap_err(); + let error = Disco::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/ecaps2.rs b/src/ecaps2.rs index fdddfdaad507f73f945d250e8779ee72c717789d..3d7f2c949438b6d0c2f586dcf1f4d6dbaa6b604e 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -166,7 +166,6 @@ pub fn hash_ecaps2(data: &[u8], algo: &str) -> String { #[cfg(test)] mod tests { use super::*; - use disco; use ecaps2; use base64; @@ -195,7 +194,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let disco = disco::parse_disco(&elem).unwrap(); + let disco = Disco::try_from(&elem).unwrap(); let ecaps2 = ecaps2::compute_disco(&disco); assert_eq!(ecaps2.len(), 54); } @@ -258,7 +257,7 @@ mod tests { 105, 109, 101, 31, 28, 99, 108, 105, 101, 110, 116, 31, 109, 111, 98, 105, 108, 101, 31, 31, 66, 111, 109, 98, 117, 115, 77, 111, 100, 31, 30, 28, 28]; - let disco = disco::parse_disco(&elem).unwrap(); + let disco = Disco::try_from(&elem).unwrap(); let ecaps2 = ecaps2::compute_disco(&disco); assert_eq!(ecaps2.len(), 0x1d9); assert_eq!(ecaps2, expected); @@ -430,7 +429,7 @@ mod tests { 111, 110, 31, 48, 46, 49, 49, 46, 49, 45, 115, 118, 110, 45, 50, 48, 49, 49, 49, 50, 49, 54, 45, 109, 111, 100, 32, 40, 84, 99, 108, 47, 84, 107, 32, 56, 46,54, 98, 50, 41, 31, 30, 29, 28]; - let disco = disco::parse_disco(&elem).unwrap(); + let disco = Disco::try_from(&elem).unwrap(); let ecaps2 = ecaps2::compute_disco(&disco); assert_eq!(ecaps2.len(), 0x543); assert_eq!(ecaps2, expected); diff --git a/src/iq.rs b/src/iq.rs index 65b49cdbce0148609c63fc745dcba0a764f76594..f48026a5670f7d4ab5ea65118ebd554b72765a61 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -17,7 +17,7 @@ use error::Error; use ns; use stanza_error; -use disco; +use disco::Disco; use ibb::IBB; use jingle::Jingle; use ping::Ping; @@ -25,7 +25,7 @@ use ping::Ping; /// Lists every known payload of a ``. #[derive(Debug, Clone)] pub enum IqPayload { - Disco(disco::Disco), + Disco(Disco), IBB(IBB), Jingle(Jingle), Ping(Ping), @@ -95,7 +95,7 @@ pub fn parse_iq(root: &Element) -> Result { return Err(Error::ParseError("Wrong number of children in iq element.")); } } else { - let parsed_payload = if let Ok(disco) = disco::parse_disco(elem) { + let parsed_payload = if let Ok(disco) = Disco::try_from(elem) { Some(IqPayload::Disco(disco)) } else if let Ok(ibb) = IBB::try_from(elem) { Some(IqPayload::IBB(ibb)) @@ -152,7 +152,7 @@ pub fn parse_iq(root: &Element) -> Result { pub fn serialise_payload(payload: &IqPayload) -> Element { match *payload { - IqPayload::Disco(ref disco) => disco::serialise_disco(disco), + IqPayload::Disco(ref disco) => disco.into(), IqPayload::IBB(ref ibb) => ibb.into(), IqPayload::Jingle(ref jingle) => jingle.into(), IqPayload::Ping(ref ping) => ping.into(), From 2b96751e5fd59fece078cca80282b28a9f3dd71f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 21:03:42 +0100 Subject: [PATCH 150/698] eme: Switch to Into/TryFrom. --- src/eme.rs | 60 +++++++++++++++++++++++++++----------------------- src/message.rs | 8 +++---- 2 files changed, 37 insertions(+), 31 deletions(-) diff --git a/src/eme.rs b/src/eme.rs index 879ddda0c6684f4f20992c8c21b6a0de3c59d813..ad28afc705c643fd61da43f32e181771b13cdad0 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use error::Error; @@ -16,44 +18,48 @@ pub struct ExplicitMessageEncryption { pub name: Option, } -pub fn parse_explicit_message_encryption(root: &Element) -> Result { - if !root.is("encryption", ns::EME) { - return Err(Error::ParseError("This is not an encryption element.")); - } - for _ in root.children() { - return Err(Error::ParseError("Unknown child in encryption element.")); +impl<'a> TryFrom<&'a Element> for ExplicitMessageEncryption { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if !elem.is("encryption", ns::EME) { + return Err(Error::ParseError("This is not an encryption element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in encryption element.")); + } + let namespace = elem.attr("namespace").ok_or(Error::ParseError("Mandatory argument 'namespace' not present in encryption element."))?.to_owned(); + let name = elem.attr("name").and_then(|value| value.parse().ok()); + Ok(ExplicitMessageEncryption { + namespace: namespace, + name: name, + }) } - let namespace = root.attr("namespace").ok_or(Error::ParseError("Mandatory argument 'namespace' not present in encryption element."))?.to_owned(); - let name = root.attr("name").and_then(|value| value.parse().ok()); - Ok(ExplicitMessageEncryption { - namespace: namespace, - name: name, - }) } -pub fn serialise(eme: &ExplicitMessageEncryption) -> Element { - Element::builder("encryption") - .ns(ns::EME) - .attr("namespace", eme.namespace.clone()) - .attr("name", eme.name.clone()) - .build() +impl<'a> Into for &'a ExplicitMessageEncryption { + fn into(self) -> Element { + Element::builder("encryption") + .ns(ns::EME) + .attr("namespace", self.namespace.clone()) + .attr("name", self.name.clone()) + .build() + } } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use eme; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let encryption = eme::parse_explicit_message_encryption(&elem).unwrap(); + let encryption = ExplicitMessageEncryption::try_from(&elem).unwrap(); assert_eq!(encryption.namespace, "urn:xmpp:otr:0"); assert_eq!(encryption.name, None); let elem: Element = "".parse().unwrap(); - let encryption = eme::parse_explicit_message_encryption(&elem).unwrap(); + let encryption = ExplicitMessageEncryption::try_from(&elem).unwrap(); assert_eq!(encryption.namespace, "some.unknown.mechanism"); assert_eq!(encryption.name, Some(String::from("SuperMechanism"))); } @@ -61,7 +67,7 @@ mod tests { #[test] fn test_unknown() { let elem: Element = "".parse().unwrap(); - let error = eme::parse_explicit_message_encryption(&elem).unwrap_err(); + let error = ExplicitMessageEncryption::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -72,7 +78,7 @@ mod tests { #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = eme::parse_explicit_message_encryption(&elem).unwrap_err(); + let error = ExplicitMessageEncryption::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -83,8 +89,8 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let eme = eme::ExplicitMessageEncryption { namespace: String::from("coucou"), name: None }; - let elem2 = eme::serialise(&eme); + let eme = ExplicitMessageEncryption { namespace: String::from("coucou"), name: None }; + let elem2 = (&eme).into(); assert_eq!(elem, elem2); } } diff --git a/src/message.rs b/src/message.rs index 476920a04adf54b360969d2766c879d6034c9be7..ce6622aa9fae19f067deadf83ca9e2584809af4a 100644 --- a/src/message.rs +++ b/src/message.rs @@ -22,7 +22,7 @@ use receipts::Receipt; use delay::Delay; use attention::Attention; use message_correct::Replace; -use eme; +use eme::ExplicitMessageEncryption; /// Lists every known payload of a ``. #[derive(Debug, Clone)] @@ -34,7 +34,7 @@ pub enum MessagePayload { Delay(Delay), Attention(Attention), MessageCorrect(Replace), - ExplicitMessageEncryption(eme::ExplicitMessageEncryption), + ExplicitMessageEncryption(ExplicitMessageEncryption), } #[derive(Debug, Clone, PartialEq)] @@ -125,7 +125,7 @@ pub fn parse_message(root: &Element) -> Result { Some(MessagePayload::Attention(attention)) } else if let Ok(replace) = Replace::try_from(elem) { Some(MessagePayload::MessageCorrect(replace)) - } else if let Ok(eme) = eme::parse_explicit_message_encryption(elem) { + } else if let Ok(eme) = ExplicitMessageEncryption::try_from(elem) { Some(MessagePayload::ExplicitMessageEncryption(eme)) } else { None @@ -153,7 +153,7 @@ pub fn serialise_payload(payload: &MessagePayload) -> Element { MessagePayload::Receipt(ref receipt) => receipt.into(), MessagePayload::Delay(ref delay) => delay.into(), MessagePayload::MessageCorrect(ref replace) => replace.into(), - MessagePayload::ExplicitMessageEncryption(ref eme) => eme::serialise(eme), + MessagePayload::ExplicitMessageEncryption(ref eme) => eme.into(), } } From 418956c7209cabc352fa8638fe3c3e2b332b820f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 21:08:44 +0100 Subject: [PATCH 151/698] mam: Switch to Into/TryFrom. --- src/mam.rs | 368 ++++++++++++++++++++++++++++------------------------- 1 file changed, 195 insertions(+), 173 deletions(-) diff --git a/src/mam.rs b/src/mam.rs index a826decc45e5e2b1111c8cb25ab7db2ed0ca12fc..c831a0993d237fd9441101ad02a6fed5f9c7e8c6 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -52,206 +52,228 @@ pub struct Prefs { pub never: Vec, } -pub fn parse_query(root: &Element) -> Result { - if !root.is("query", ns::MAM) { - return Err(Error::ParseError("This is not a query element.")); - } - let mut form = None; - let mut set = None; - for child in root.children() { - if child.is("x", ns::DATA_FORMS) { - form = Some(DataForm::try_from(child)?); - } else if child.is("set", ns::RSM) { - set = Some(Set::try_from(child)?); - } else { - return Err(Error::ParseError("Unknown child in query element.")); +impl<'a> TryFrom<&'a Element> for Query { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if !elem.is("query", ns::MAM) { + return Err(Error::ParseError("This is not a query element.")); } + let mut form = None; + let mut set = None; + for child in elem.children() { + if child.is("x", ns::DATA_FORMS) { + form = Some(DataForm::try_from(child)?); + } else if child.is("set", ns::RSM) { + set = Some(Set::try_from(child)?); + } else { + return Err(Error::ParseError("Unknown child in query element.")); + } + } + let queryid = match elem.attr("queryid") { + Some(queryid) => Some(queryid.to_owned()), + None => None, + }; + let node = match elem.attr("node") { + Some(node) => Some(node.to_owned()), + None => None, + }; + Ok(Query { queryid, node, form, set }) } - let queryid = match root.attr("queryid") { - Some(queryid) => Some(queryid.to_owned()), - None => None, - }; - let node = match root.attr("node") { - Some(node) => Some(node.to_owned()), - None => None, - }; - Ok(Query { queryid, node, form, set }) } -pub fn parse_result(root: &Element) -> Result { - if !root.is("result", ns::MAM) { - return Err(Error::ParseError("This is not a result element.")); - } - let mut forwarded = None; - for child in root.children() { - if child.is("forwarded", ns::FORWARD) { - forwarded = Some(Forwarded::try_from(child)?); - } else { - return Err(Error::ParseError("Unknown child in result element.")); +impl<'a> TryFrom<&'a Element> for Result_ { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if !elem.is("result", ns::MAM) { + return Err(Error::ParseError("This is not a result element.")); } + let mut forwarded = None; + for child in elem.children() { + if child.is("forwarded", ns::FORWARD) { + forwarded = Some(Forwarded::try_from(child)?); + } else { + return Err(Error::ParseError("Unknown child in result element.")); + } + } + let queryid = match elem.attr("queryid") { + Some(queryid) => queryid.to_owned(), + None => return Err(Error::ParseError("No 'queryid' attribute present in result.")), + }; + let id = match elem.attr("id") { + Some(id) => id.to_owned(), + None => return Err(Error::ParseError("No 'id' attribute present in result.")), + }; + if forwarded.is_none() { + return Err(Error::ParseError("Mandatory forwarded element missing in result.")); + } + let forwarded = forwarded.unwrap(); + Ok(Result_ { + queryid, + id, + forwarded, + }) } - let queryid = match root.attr("queryid") { - Some(queryid) => queryid.to_owned(), - None => return Err(Error::ParseError("No 'queryid' attribute present in result.")), - }; - let id = match root.attr("id") { - Some(id) => id.to_owned(), - None => return Err(Error::ParseError("No 'id' attribute present in result.")), - }; - if forwarded.is_none() { - return Err(Error::ParseError("Mandatory forwarded element missing in result.")); - } - let forwarded = forwarded.unwrap(); - Ok(Result_ { - queryid, - id, - forwarded, - }) } -pub fn parse_fin(root: &Element) -> Result { - if !root.is("fin", ns::MAM) { - return Err(Error::ParseError("This is not a fin element.")); - } - let mut set = None; - for child in root.children() { - if child.is("set", ns::RSM) { - set = Some(Set::try_from(child)?); - } else { - return Err(Error::ParseError("Unknown child in fin element.")); +impl<'a> TryFrom<&'a Element> for Fin { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if !elem.is("fin", ns::MAM) { + return Err(Error::ParseError("This is not a fin element.")); } + let mut set = None; + for child in elem.children() { + if child.is("set", ns::RSM) { + set = Some(Set::try_from(child)?); + } else { + return Err(Error::ParseError("Unknown child in fin element.")); + } + } + let complete = match elem.attr("complete") { + Some(complete) => complete == "true", + None => false, + }; + if set.is_none() { + return Err(Error::ParseError("Mandatory set element missing in fin.")); + } + let set = set.unwrap(); + Ok(Fin { complete, set }) } - let complete = match root.attr("complete") { - Some(complete) => complete == "true", - None => false, - }; - if set.is_none() { - return Err(Error::ParseError("Mandatory set element missing in fin.")); - } - let set = set.unwrap(); - Ok(Fin { complete, set }) } -pub fn parse_prefs(root: &Element) -> Result { - if !root.is("prefs", ns::MAM) { - return Err(Error::ParseError("This is not a prefs element.")); - } - let mut always = vec!(); - let mut never = vec!(); - for child in root.children() { - if child.is("always", ns::MAM) { - for jid_elem in child.children() { - if !jid_elem.is("jid", ns::MAM) { - return Err(Error::ParseError("Invalid jid element in always.")); +impl<'a> TryFrom<&'a Element> for Prefs { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if !elem.is("prefs", ns::MAM) { + return Err(Error::ParseError("This is not a prefs element.")); + } + let mut always = vec!(); + let mut never = vec!(); + for child in elem.children() { + if child.is("always", ns::MAM) { + for jid_elem in child.children() { + if !jid_elem.is("jid", ns::MAM) { + return Err(Error::ParseError("Invalid jid element in always.")); + } + always.push(jid_elem.text().parse()?); } - always.push(jid_elem.text().parse()?); - } - } else if child.is("never", ns::MAM) { - for jid_elem in child.children() { - if !jid_elem.is("jid", ns::MAM) { - return Err(Error::ParseError("Invalid jid element in never.")); + } else if child.is("never", ns::MAM) { + for jid_elem in child.children() { + if !jid_elem.is("jid", ns::MAM) { + return Err(Error::ParseError("Invalid jid element in never.")); + } + never.push(jid_elem.text().parse()?); } - never.push(jid_elem.text().parse()?); + } else { + return Err(Error::ParseError("Unknown child in prefs element.")); } - } else { - return Err(Error::ParseError("Unknown child in prefs element.")); } + let default_ = match elem.attr("default") { + Some("always") => Some(DefaultPrefs::Always), + Some("never") => Some(DefaultPrefs::Never), + Some("roster") => Some(DefaultPrefs::Roster), + None => None, + + _ => return Err(Error::ParseError("Invalid 'default' attribute present in prefs.")), + }; + Ok(Prefs { default_, always, never }) } - let default_ = match root.attr("default") { - Some("always") => Some(DefaultPrefs::Always), - Some("never") => Some(DefaultPrefs::Never), - Some("roster") => Some(DefaultPrefs::Roster), - None => None, - - _ => return Err(Error::ParseError("Invalid 'default' attribute present in prefs.")), - }; - Ok(Prefs { default_, always, never }) } -pub fn serialise_query(query: &Query) -> Element { - let mut elem = Element::builder("query") - .ns(ns::MAM) - .attr("queryid", query.queryid.clone()) - .attr("node", query.node.clone()) - .build(); - //if let Some(form) = query.form { - // elem.append_child((&form).into()); - //} - if let Some(ref set) = query.set { - elem.append_child(set.into()); +impl<'a> Into for &'a Query { + fn into(self) -> Element { + let mut elem = Element::builder("query") + .ns(ns::MAM) + .attr("queryid", self.queryid.clone()) + .attr("node", self.node.clone()) + .build(); + //if let Some(form) = self.form { + // elem.append_child((&form).into()); + //} + if let Some(ref set) = self.set { + elem.append_child(set.into()); + } + elem } - elem } -pub fn serialise_result(result: &Result_) -> Element { - let mut elem = Element::builder("result") - .ns(ns::MAM) - .attr("queryid", result.queryid.clone()) - .attr("id", result.id.clone()) - .build(); - elem.append_child((&result.forwarded).into()); - elem +impl<'a> Into for &'a Result_ { + fn into(self) -> Element { + let mut elem = Element::builder("result") + .ns(ns::MAM) + .attr("queryid", self.queryid.clone()) + .attr("id", self.id.clone()) + .build(); + elem.append_child((&self.forwarded).into()); + elem + } } -pub fn serialise_fin(fin: &Fin) -> Element { - let mut elem = Element::builder("fin") - .ns(ns::MAM) - .attr("complete", match fin.complete { - true => Some("true"), - false => None, - }) - .build(); - elem.append_child((&fin.set).into()); - elem +impl<'a> Into for &'a Fin { + fn into(self) -> Element { + let mut elem = Element::builder("fin") + .ns(ns::MAM) + .attr("complete", match self.complete { + true => Some("true"), + false => None, + }) + .build(); + elem.append_child((&self.set).into()); + elem + } } -pub fn serialise_prefs(prefs: &Prefs) -> Element { - let mut elem = Element::builder("prefs") - .ns(ns::MAM) - .attr("default", match prefs.default_ { - Some(DefaultPrefs::Always) => Some("always"), - Some(DefaultPrefs::Never) => Some("never"), - Some(DefaultPrefs::Roster) => Some("roster"), - None => None, - }) - .build(); - if !prefs.always.is_empty() { - let mut always = Element::builder("always") - .ns(ns::RSM) - .build(); - for jid in prefs.always.clone() { - always.append_child(Element::builder("jid") - .ns(ns::RSM) - .append(String::from(jid)) - .build()); +impl<'a> Into for &'a Prefs { + fn into(self) -> Element { + let mut elem = Element::builder("prefs") + .ns(ns::MAM) + .attr("default", match self.default_ { + Some(DefaultPrefs::Always) => Some("always"), + Some(DefaultPrefs::Never) => Some("never"), + Some(DefaultPrefs::Roster) => Some("roster"), + None => None, + }) + .build(); + if !self.always.is_empty() { + let mut always = Element::builder("always") + .ns(ns::RSM) + .build(); + for jid in self.always.clone() { + always.append_child(Element::builder("jid") + .ns(ns::RSM) + .append(String::from(jid)) + .build()); + } + elem.append_child(always); } - elem.append_child(always); - } - if !prefs.never.is_empty() { - let mut never = Element::builder("never") - .ns(ns::RSM) - .build(); - for jid in prefs.never.clone() { - never.append_child(Element::builder("jid") - .ns(ns::RSM) - .append(String::from(jid)) - .build()); + if !self.never.is_empty() { + let mut never = Element::builder("never") + .ns(ns::RSM) + .build(); + for jid in self.never.clone() { + never.append_child(Element::builder("jid") + .ns(ns::RSM) + .append(String::from(jid)) + .build()); + } + elem.append_child(never); } - elem.append_child(never); + elem } - elem } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use mam; + use super::*; #[test] fn test_query() { let elem: Element = "".parse().unwrap(); - mam::parse_query(&elem).unwrap(); + Query::try_from(&elem).unwrap(); } #[test] @@ -266,7 +288,7 @@ mod tests { "#.parse().unwrap(); - mam::parse_result(&elem).unwrap(); + Result_::try_from(&elem).unwrap(); } #[test] @@ -279,7 +301,7 @@ mod tests { "#.parse().unwrap(); - mam::parse_fin(&elem).unwrap(); + Fin::try_from(&elem).unwrap(); } #[test] @@ -296,7 +318,7 @@ mod tests { "#.parse().unwrap(); - mam::parse_query(&elem).unwrap(); + Query::try_from(&elem).unwrap(); } #[test] @@ -316,13 +338,13 @@ mod tests { "#.parse().unwrap(); - mam::parse_query(&elem).unwrap(); + Query::try_from(&elem).unwrap(); } #[test] fn test_prefs_get() { let elem: Element = "".parse().unwrap(); - mam::parse_prefs(&elem).unwrap(); + Prefs::try_from(&elem).unwrap(); let elem: Element = r#" @@ -330,7 +352,7 @@ mod tests { "#.parse().unwrap(); - mam::parse_prefs(&elem).unwrap(); + Prefs::try_from(&elem).unwrap(); } #[test] @@ -345,13 +367,13 @@ mod tests { "#.parse().unwrap(); - mam::parse_prefs(&elem).unwrap(); + Prefs::try_from(&elem).unwrap(); } #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = mam::parse_query(&elem).unwrap_err(); + let error = Query::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -362,8 +384,8 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let replace = mam::Query { queryid: None, node: None, form: None, set: None }; - let elem2 = mam::serialise_query(&replace); + let replace = Query { queryid: None, node: None, form: None, set: None }; + let elem2 = (&replace).into(); assert_eq!(elem, elem2); } } From 04d90f22ee306cf8c251e77812b55ea69044dc1f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 21:13:53 +0100 Subject: [PATCH 152/698] stanza_error: Switch to Into/TryFrom. --- src/iq.rs | 20 +++--- src/message.rs | 8 +-- src/presence.rs | 8 +-- src/stanza_error.rs | 161 +++++++++++++++++++++++--------------------- 4 files changed, 100 insertions(+), 97 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index f48026a5670f7d4ab5ea65118ebd554b72765a61..0e0aca7fc76e5b2ae9a49dd7e1d4d8f408a05efa 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -16,7 +16,7 @@ use error::Error; use ns; -use stanza_error; +use stanza_error::StanzaError; use disco::Disco; use ibb::IBB; use jingle::Jingle; @@ -42,7 +42,7 @@ pub enum IqType { Get(IqPayloadType), Set(IqPayloadType), Result(Option), - Error(stanza_error::StanzaError), + Error(StanzaError), } impl IntoAttributeValue for IqType { @@ -90,7 +90,7 @@ pub fn parse_iq(root: &Element) -> Result { if error_payload.is_some() { return Err(Error::ParseError("Wrong number of children in iq element.")); } - error_payload = Some(stanza_error::parse_stanza_error(elem)?); + error_payload = Some(StanzaError::try_from(elem)?); } else if root.children().collect::>().len() != 2 { return Err(Error::ParseError("Wrong number of children in iq element.")); } @@ -171,7 +171,7 @@ pub fn serialise(iq: &Iq) -> Element { IqType::Get(IqPayloadType::XML(elem)) | IqType::Set(IqPayloadType::XML(elem)) | IqType::Result(Some(IqPayloadType::XML(elem))) => elem, - IqType::Error(error) => stanza_error::serialise(&error), + IqType::Error(error) => (&error).into(), IqType::Get(IqPayloadType::Parsed(payload)) | IqType::Set(IqPayloadType::Parsed(payload)) | IqType::Result(Some(IqPayloadType::Parsed(payload))) => serialise_payload(&payload), @@ -183,11 +183,9 @@ pub fn serialise(iq: &Iq) -> Element { #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; + use super::*; use iq; - use stanza_error; - use disco; + use stanza_error::{ErrorType, DefinedCondition}; #[test] fn test_require_type() { @@ -275,9 +273,9 @@ mod tests { assert_eq!(iq.id, None); match iq.payload { iq::IqType::Error(error) => { - assert_eq!(error.type_, stanza_error::ErrorType::Cancel); + assert_eq!(error.type_, ErrorType::Cancel); assert_eq!(error.by, None); - assert_eq!(error.defined_condition, stanza_error::DefinedCondition::ServiceUnavailable); + assert_eq!(error.defined_condition, DefinedCondition::ServiceUnavailable); assert_eq!(error.texts.len(), 0); assert_eq!(error.other, None); }, @@ -314,7 +312,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let iq = iq::parse_iq(&elem).unwrap(); assert!(match iq.payload { - iq::IqType::Get(iq::IqPayloadType::Parsed(iq::IqPayload::Disco(disco::Disco { .. }))) => true, + IqType::Get(IqPayloadType::Parsed(IqPayload::Disco(Disco { .. }))) => true, _ => false, }); } diff --git a/src/message.rs b/src/message.rs index ce6622aa9fae19f067deadf83ca9e2584809af4a..ee2c64477ce141cf8b33766d1e8655b23ca501e6 100644 --- a/src/message.rs +++ b/src/message.rs @@ -16,7 +16,7 @@ use error::Error; use ns; use body; -use stanza_error; +use stanza_error::StanzaError; use chatstates::ChatState; use receipts::Receipt; use delay::Delay; @@ -28,7 +28,7 @@ use eme::ExplicitMessageEncryption; #[derive(Debug, Clone)] pub enum MessagePayload { Body(body::Body), - StanzaError(stanza_error::StanzaError), + StanzaError(StanzaError), ChatState(ChatState), Receipt(Receipt), Delay(Delay), @@ -113,7 +113,7 @@ pub fn parse_message(root: &Element) -> Result { for elem in root.children() { let payload = if let Ok(body) = body::parse_body(elem) { Some(MessagePayload::Body(body)) - } else if let Ok(stanza_error) = stanza_error::parse_stanza_error(elem) { + } else if let Ok(stanza_error) = StanzaError::try_from(elem) { Some(MessagePayload::StanzaError(stanza_error)) } else if let Ok(chatstate) = ChatState::try_from(elem) { Some(MessagePayload::ChatState(chatstate)) @@ -147,7 +147,7 @@ pub fn parse_message(root: &Element) -> Result { pub fn serialise_payload(payload: &MessagePayload) -> Element { match *payload { MessagePayload::Body(ref body) => body::serialise(body), - MessagePayload::StanzaError(ref stanza_error) => stanza_error::serialise(stanza_error), + MessagePayload::StanzaError(ref stanza_error) => stanza_error.into(), MessagePayload::Attention(ref attention) => attention.into(), MessagePayload::ChatState(ref chatstate) => chatstate.into(), MessagePayload::Receipt(ref receipt) => receipt.into(), diff --git a/src/presence.rs b/src/presence.rs index 6ae97ecb9ae2371c5dbda2460080d79c7b552047..4ec697cbbe1c6cf56da436f72e0778fdac190499 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -16,7 +16,7 @@ use error::Error; use ns; -use stanza_error; +use stanza_error::StanzaError; use delay::Delay; use ecaps2::ECaps2; @@ -51,7 +51,7 @@ pub enum PresencePayload { Show(Show), Status(Status), Priority(Priority), - StanzaError(stanza_error::StanzaError), + StanzaError(StanzaError), Delay(Delay), ECaps2(ECaps2), } @@ -179,7 +179,7 @@ pub fn parse_presence(root: &Element) -> Result { } priority = Some(Priority::from_str(elem.text().as_ref())?); } else { - let payload = if let Ok(stanza_error) = stanza_error::parse_stanza_error(elem) { + let payload = if let Ok(stanza_error) = StanzaError::try_from(elem) { Some(PresencePayload::StanzaError(stanza_error)) } else if let Ok(delay) = Delay::try_from(elem) { Some(PresencePayload::Delay(delay)) @@ -226,7 +226,7 @@ pub fn serialise_payload(payload: &PresencePayload) -> Element { .append(format!("{}", priority)) .build() }, - PresencePayload::StanzaError(ref stanza_error) => stanza_error::serialise(stanza_error), + PresencePayload::StanzaError(ref stanza_error) => stanza_error.into(), PresencePayload::Delay(ref delay) => delay.into(), PresencePayload::ECaps2(ref ecaps2) => ecaps2.into(), } diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 0a1eb42566a042ab0198ed8ead421cfb10b8b1d3..9a0bfa03dc71eb316580db86cc8f6a7bccb3152e 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -4,6 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; use std::str::FromStr; use std::collections::BTreeMap; @@ -149,104 +150,108 @@ pub struct StanzaError { pub other: Option, } -pub fn parse_stanza_error(root: &Element) -> Result { - if !root.is("error", ns::JABBER_CLIENT) { - return Err(Error::ParseError("This is not an error element.")); - } +impl<'a> TryFrom<&'a Element> for StanzaError { + type Error = Error; - let type_ = root.attr("type") - .ok_or(Error::ParseError("Error must have a 'type' attribute."))? - .parse()?; - let by = root.attr("by") - .and_then(|by| by.parse().ok()); - let mut defined_condition = None; - let mut texts = BTreeMap::new(); - let mut other = None; + fn try_from(elem: &'a Element) -> Result { + if !elem.is("error", ns::JABBER_CLIENT) { + return Err(Error::ParseError("This is not an error element.")); + } - for child in root.children() { - if child.is("text", ns::XMPP_STANZAS) { - for _ in child.children() { - return Err(Error::ParseError("Unknown element in error text.")); - } - let lang = child.attr("xml:lang").unwrap_or("").to_owned(); - if let Some(_) = texts.insert(lang, child.text()) { - return Err(Error::ParseError("Text element present twice for the same xml:lang.")); - } - } else if child.ns() == Some(ns::XMPP_STANZAS) { - if defined_condition.is_some() { - return Err(Error::ParseError("Error must not have more than one defined-condition.")); - } - for _ in child.children() { - return Err(Error::ParseError("Unknown element in defined-condition.")); - } - let condition = DefinedCondition::from_str(child.name())?; - defined_condition = Some(condition); - } else { - if other.is_some() { - return Err(Error::ParseError("Error must not have more than one other element.")); + let type_ = elem.attr("type") + .ok_or(Error::ParseError("Error must have a 'type' attribute."))? + .parse()?; + let by = elem.attr("by") + .and_then(|by| by.parse().ok()); + let mut defined_condition = None; + let mut texts = BTreeMap::new(); + let mut other = None; + + for child in elem.children() { + if child.is("text", ns::XMPP_STANZAS) { + for _ in child.children() { + return Err(Error::ParseError("Unknown element in error text.")); + } + let lang = child.attr("xml:lang").unwrap_or("").to_owned(); + if let Some(_) = texts.insert(lang, child.text()) { + return Err(Error::ParseError("Text element present twice for the same xml:lang.")); + } + } else if child.ns() == Some(ns::XMPP_STANZAS) { + if defined_condition.is_some() { + return Err(Error::ParseError("Error must not have more than one defined-condition.")); + } + for _ in child.children() { + return Err(Error::ParseError("Unknown element in defined-condition.")); + } + let condition = DefinedCondition::from_str(child.name())?; + defined_condition = Some(condition); + } else { + if other.is_some() { + return Err(Error::ParseError("Error must not have more than one other element.")); + } + other = Some(child.clone()); } - other = Some(child.clone()); } - } - if defined_condition.is_none() { - return Err(Error::ParseError("Error must have a defined-condition.")); - } - let defined_condition = defined_condition.unwrap(); + if defined_condition.is_none() { + return Err(Error::ParseError("Error must have a defined-condition.")); + } + let defined_condition = defined_condition.unwrap(); - Ok(StanzaError { - type_: type_, - by: by, - defined_condition: defined_condition, - texts: texts, - other: other, - }) + Ok(StanzaError { + type_: type_, + by: by, + defined_condition: defined_condition, + texts: texts, + other: other, + }) + } } -pub fn serialise(error: &StanzaError) -> Element { - let mut root = Element::builder("error") - .ns(ns::JABBER_CLIENT) - .attr("type", String::from(error.type_.clone())) - .attr("by", match error.by { - Some(ref by) => Some(String::from(by.clone())), - None => None, - }) - .append(Element::builder(error.defined_condition.clone()) - .ns(ns::XMPP_STANZAS) - .build()) - .build(); - for (lang, text) in error.texts.clone() { - let elem = Element::builder("text") - .ns(ns::XMPP_STANZAS) - .attr("xml:lang", lang) - .append(text) - .build(); - root.append_child(elem); - } - if let Some(ref other) = error.other { - root.append_child(other.clone()); +impl<'a> Into for &'a StanzaError { + fn into(self) -> Element { + let mut root = Element::builder("error") + .ns(ns::JABBER_CLIENT) + .attr("type", String::from(self.type_.clone())) + .attr("by", match self.by { + Some(ref by) => Some(String::from(by.clone())), + None => None, + }) + .append(Element::builder(self.defined_condition.clone()) + .ns(ns::XMPP_STANZAS) + .build()) + .build(); + for (lang, text) in self.texts.clone() { + let elem = Element::builder("text") + .ns(ns::XMPP_STANZAS) + .attr("xml:lang", lang) + .append(text) + .build(); + root.append_child(elem); + } + if let Some(ref other) = self.other { + root.append_child(other.clone()); + } + root } - root } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use stanza_error; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let error = stanza_error::parse_stanza_error(&elem).unwrap(); - assert_eq!(error.type_, stanza_error::ErrorType::Cancel); - assert_eq!(error.defined_condition, stanza_error::DefinedCondition::UndefinedCondition); + let error = StanzaError::try_from(&elem).unwrap(); + assert_eq!(error.type_, ErrorType::Cancel); + assert_eq!(error.defined_condition, DefinedCondition::UndefinedCondition); } #[test] fn test_invalid_type() { let elem: Element = "".parse().unwrap(); - let error = stanza_error::parse_stanza_error(&elem).unwrap_err(); + let error = StanzaError::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -254,7 +259,7 @@ mod tests { assert_eq!(message, "Error must have a 'type' attribute."); let elem: Element = "".parse().unwrap(); - let error = stanza_error::parse_stanza_error(&elem).unwrap_err(); + let error = StanzaError::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -265,7 +270,7 @@ mod tests { #[test] fn test_invalid_condition() { let elem: Element = "".parse().unwrap(); - let error = stanza_error::parse_stanza_error(&elem).unwrap_err(); + let error = StanzaError::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), From fffaca316f460121330635b95ac912b74e6cc2a4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 21:16:56 +0100 Subject: [PATCH 153/698] iq: Switch to Into/TryFrom. --- src/iq.rs | 241 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 124 insertions(+), 117 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index 0e0aca7fc76e5b2ae9a49dd7e1d4d8f408a05efa..4f61260d8184481c836fbb5165c7683ba5d41437 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -64,133 +64,140 @@ pub struct Iq { pub payload: IqType, } -pub fn parse_iq(root: &Element) -> Result { - if !root.is("iq", ns::JABBER_CLIENT) { - return Err(Error::ParseError("This is not an iq element.")); - } - let from = root.attr("from") - .and_then(|value| value.parse().ok()); - let to = root.attr("to") - .and_then(|value| value.parse().ok()); - let id = root.attr("id") - .and_then(|value| value.parse().ok()); - let type_ = match root.attr("type") { - Some(type_) => type_, - None => return Err(Error::ParseError("Iq element requires a 'type' attribute.")), - }; +impl<'a> TryFrom<&'a Element> for Iq { + type Error = Error; - let mut payload = None; - let mut error_payload = None; - for elem in root.children() { - if payload.is_some() { - return Err(Error::ParseError("Wrong number of children in iq element.")); + fn try_from(root: &'a Element) -> Result { + if !root.is("iq", ns::JABBER_CLIENT) { + return Err(Error::ParseError("This is not an iq element.")); } - if type_ == "error" { - if elem.is("error", ns::JABBER_CLIENT) { - if error_payload.is_some() { - return Err(Error::ParseError("Wrong number of children in iq element.")); - } - error_payload = Some(StanzaError::try_from(elem)?); - } else if root.children().collect::>().len() != 2 { + let from = root.attr("from") + .and_then(|value| value.parse().ok()); + let to = root.attr("to") + .and_then(|value| value.parse().ok()); + let id = root.attr("id") + .and_then(|value| value.parse().ok()); + let type_ = match root.attr("type") { + Some(type_) => type_, + None => return Err(Error::ParseError("Iq element requires a 'type' attribute.")), + }; + + let mut payload = None; + let mut error_payload = None; + for elem in root.children() { + if payload.is_some() { return Err(Error::ParseError("Wrong number of children in iq element.")); } - } else { - let parsed_payload = if let Ok(disco) = Disco::try_from(elem) { - Some(IqPayload::Disco(disco)) - } else if let Ok(ibb) = IBB::try_from(elem) { - Some(IqPayload::IBB(ibb)) - } else if let Ok(jingle) = Jingle::try_from(elem) { - Some(IqPayload::Jingle(jingle)) - } else if let Ok(ping) = Ping::try_from(elem) { - Some(IqPayload::Ping(ping)) + if type_ == "error" { + if elem.is("error", ns::JABBER_CLIENT) { + if error_payload.is_some() { + return Err(Error::ParseError("Wrong number of children in iq element.")); + } + error_payload = Some(StanzaError::try_from(elem)?); + } else if root.children().collect::>().len() != 2 { + return Err(Error::ParseError("Wrong number of children in iq element.")); + } } else { - None - }; + let parsed_payload = if let Ok(disco) = Disco::try_from(elem) { + Some(IqPayload::Disco(disco)) + } else if let Ok(ibb) = IBB::try_from(elem) { + Some(IqPayload::IBB(ibb)) + } else if let Ok(jingle) = Jingle::try_from(elem) { + Some(IqPayload::Jingle(jingle)) + } else if let Ok(ping) = Ping::try_from(elem) { + Some(IqPayload::Ping(ping)) + } else { + None + }; - payload = match parsed_payload { - Some(payload) => Some(IqPayloadType::Parsed(payload)), - None => Some(IqPayloadType::XML(elem.clone())), - }; + payload = match parsed_payload { + Some(payload) => Some(IqPayloadType::Parsed(payload)), + None => Some(IqPayloadType::XML(elem.clone())), + }; + } } - } - let type_ = if type_ == "get" { - if let Some(payload) = payload.clone() { - IqType::Get(payload.clone()) - } else { - return Err(Error::ParseError("Wrong number of children in iq element.")); - } - } else if type_ == "set" { - if let Some(payload) = payload.clone() { - IqType::Set(payload.clone()) - } else { - return Err(Error::ParseError("Wrong number of children in iq element.")); - } - } else if type_ == "result" { - if let Some(payload) = payload.clone() { - IqType::Result(Some(payload.clone())) - } else { - IqType::Result(None) - } - } else if type_ == "error" { - if let Some(payload) = error_payload.clone() { - IqType::Error(payload.clone()) + let type_ = if type_ == "get" { + if let Some(payload) = payload.clone() { + IqType::Get(payload.clone()) + } else { + return Err(Error::ParseError("Wrong number of children in iq element.")); + } + } else if type_ == "set" { + if let Some(payload) = payload.clone() { + IqType::Set(payload.clone()) + } else { + return Err(Error::ParseError("Wrong number of children in iq element.")); + } + } else if type_ == "result" { + if let Some(payload) = payload.clone() { + IqType::Result(Some(payload.clone())) + } else { + IqType::Result(None) + } + } else if type_ == "error" { + if let Some(payload) = error_payload.clone() { + IqType::Error(payload.clone()) + } else { + return Err(Error::ParseError("Wrong number of children in iq element.")); + } } else { - return Err(Error::ParseError("Wrong number of children in iq element.")); - } - } else { - panic!() - }; + panic!() + }; - Ok(Iq { - from: from, - to: to, - id: id, - payload: type_, - }) + Ok(Iq { + from: from, + to: to, + id: id, + payload: type_, + }) + } } -pub fn serialise_payload(payload: &IqPayload) -> Element { - match *payload { - IqPayload::Disco(ref disco) => disco.into(), - IqPayload::IBB(ref ibb) => ibb.into(), - IqPayload::Jingle(ref jingle) => jingle.into(), - IqPayload::Ping(ref ping) => ping.into(), +impl<'a> Into for &'a IqPayload { + fn into(self) -> Element { + match *self { + IqPayload::Disco(ref disco) => disco.into(), + IqPayload::IBB(ref ibb) => ibb.into(), + IqPayload::Jingle(ref jingle) => jingle.into(), + IqPayload::Ping(ref ping) => ping.into(), + } } } -pub fn serialise(iq: &Iq) -> Element { - let mut stanza = Element::builder("iq") - .ns(ns::JABBER_CLIENT) - .attr("from", iq.from.clone().and_then(|value| Some(String::from(value)))) - .attr("to", iq.to.clone().and_then(|value| Some(String::from(value)))) - .attr("id", iq.id.clone()) - .attr("type", iq.payload.clone()) - .build(); - let elem = match iq.payload.clone() { - IqType::Get(IqPayloadType::XML(elem)) - | IqType::Set(IqPayloadType::XML(elem)) - | IqType::Result(Some(IqPayloadType::XML(elem))) => elem, - IqType::Error(error) => (&error).into(), - IqType::Get(IqPayloadType::Parsed(payload)) - | IqType::Set(IqPayloadType::Parsed(payload)) - | IqType::Result(Some(IqPayloadType::Parsed(payload))) => serialise_payload(&payload), - IqType::Result(None) => return stanza, - }; - stanza.append_child(elem); - stanza +impl<'a> Into for &'a Iq { + fn into(self) -> Element { + let mut stanza = Element::builder("iq") + .ns(ns::JABBER_CLIENT) + .attr("from", self.from.clone().and_then(|value| Some(String::from(value)))) + .attr("to", self.to.clone().and_then(|value| Some(String::from(value)))) + .attr("id", self.id.clone()) + .attr("type", self.payload.clone()) + .build(); + let elem = match self.payload.clone() { + IqType::Get(IqPayloadType::XML(elem)) + | IqType::Set(IqPayloadType::XML(elem)) + | IqType::Result(Some(IqPayloadType::XML(elem))) => elem, + IqType::Error(error) => (&error).into(), + IqType::Get(IqPayloadType::Parsed(payload)) + | IqType::Set(IqPayloadType::Parsed(payload)) + | IqType::Result(Some(IqPayloadType::Parsed(payload))) => (&payload).into(), + IqType::Result(None) => return stanza, + }; + stanza.append_child(elem); + stanza + } } #[cfg(test)] mod tests { use super::*; - use iq; use stanza_error::{ErrorType, DefinedCondition}; #[test] fn test_require_type() { let elem: Element = "".parse().unwrap(); - let error = iq::parse_iq(&elem).unwrap_err(); + let error = Iq::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -203,13 +210,13 @@ mod tests { let elem: Element = " ".parse().unwrap(); - let iq = iq::parse_iq(&elem).unwrap(); + let iq = Iq::try_from(&elem).unwrap(); let query: Element = "".parse().unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); assert_eq!(iq.id, None); assert!(match iq.payload { - iq::IqType::Get(iq::IqPayloadType::XML(element)) => element == query, + IqType::Get(IqPayloadType::XML(element)) => element == query, _ => false }); } @@ -219,13 +226,13 @@ mod tests { let elem: Element = " ".parse().unwrap(); - let iq = iq::parse_iq(&elem).unwrap(); + let iq = Iq::try_from(&elem).unwrap(); let vcard: Element = "".parse().unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); assert_eq!(iq.id, None); assert!(match iq.payload { - iq::IqType::Set(iq::IqPayloadType::XML(element)) => element == vcard, + IqType::Set(IqPayloadType::XML(element)) => element == vcard, _ => false }); } @@ -233,12 +240,12 @@ mod tests { #[test] fn test_result_empty() { let elem: Element = "".parse().unwrap(); - let iq = iq::parse_iq(&elem).unwrap(); + let iq = Iq::try_from(&elem).unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); assert_eq!(iq.id, None); assert!(match iq.payload { - iq::IqType::Result(None) => true, + IqType::Result(None) => true, _ => false, }); } @@ -248,13 +255,13 @@ mod tests { let elem: Element = " ".parse().unwrap(); - let iq = iq::parse_iq(&elem).unwrap(); + let iq = Iq::try_from(&elem).unwrap(); let query: Element = "".parse().unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); assert_eq!(iq.id, None); assert!(match iq.payload { - iq::IqType::Result(Some(iq::IqPayloadType::XML(element))) => element == query, + IqType::Result(Some(IqPayloadType::XML(element))) => element == query, _ => false, }); } @@ -267,12 +274,12 @@ mod tests { ".parse().unwrap(); - let iq = iq::parse_iq(&elem).unwrap(); + let iq = Iq::try_from(&elem).unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); assert_eq!(iq.id, None); match iq.payload { - iq::IqType::Error(error) => { + IqType::Error(error) => { assert_eq!(error.type_, ErrorType::Cancel); assert_eq!(error.by, None); assert_eq!(error.defined_condition, DefinedCondition::ServiceUnavailable); @@ -286,7 +293,7 @@ mod tests { #[test] fn test_children_invalid() { let elem: Element = "".parse().unwrap(); - let error = iq::parse_iq(&elem).unwrap_err(); + let error = Iq::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -297,20 +304,20 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let iq2 = iq::Iq { + let iq2 = Iq { from: None, to: None, id: None, - payload: iq::IqType::Result(None), + payload: IqType::Result(None), }; - let elem2 = iq::serialise(&iq2); + let elem2 = (&iq2).into(); assert_eq!(elem, elem2); } #[test] fn test_disco() { let elem: Element = "".parse().unwrap(); - let iq = iq::parse_iq(&elem).unwrap(); + let iq = Iq::try_from(&elem).unwrap(); assert!(match iq.payload { IqType::Get(IqPayloadType::Parsed(IqPayload::Disco(Disco { .. }))) => true, _ => false, From f971cbd5c9f541b2857e437ad8887daad3aeadf2 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 21:21:34 +0100 Subject: [PATCH 154/698] message: Switch to Into/TryFrom. --- src/forwarding.rs | 8 +- src/message.rs | 186 +++++++++++++++++++++++----------------------- 2 files changed, 96 insertions(+), 98 deletions(-) diff --git a/src/forwarding.rs b/src/forwarding.rs index 75b7fbcc8a307946346e9d07298e744c215ce5de..a2162e06b110209c8b9e8001d0da0518c90255ad 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -11,7 +11,7 @@ use minidom::Element; use error::Error; use delay::Delay; -use message; +use message::Message; use ns; @@ -19,7 +19,7 @@ use ns; pub struct Forwarded { pub delay: Option, // XXX: really? Option? - pub stanza: Option, + pub stanza: Option, } impl<'a> TryFrom<&'a Element> for Forwarded { @@ -35,7 +35,7 @@ impl<'a> TryFrom<&'a Element> for Forwarded { if child.is("delay", ns::DELAY) { delay = Some(Delay::try_from(child)?); } else if child.is("message", ns::JABBER_CLIENT) { - stanza = Some(message::parse_message(child)?); + stanza = Some(Message::try_from(child)?); // TODO: also handle the five other possibilities. } else { return Err(Error::ParseError("Unknown child in forwarded element.")); @@ -53,7 +53,7 @@ impl<'a> Into for &'a Forwarded { Element::builder("forwarded") .ns(ns::FORWARD) .append(match self.delay { Some(ref delay) => { let elem: Element = delay.into(); Some(elem) }, None => None }) - .append(self.stanza.clone()) + .append(match self.stanza { Some(ref stanza) => { let elem: Element = stanza.into(); Some(elem) }, None => None }) .build() } } diff --git a/src/message.rs b/src/message.rs index ee2c64477ce141cf8b33766d1e8655b23ca501e6..51baf9b9ffb8a0cd4d2dbaec8d67816a6ed83afa 100644 --- a/src/message.rs +++ b/src/message.rs @@ -7,7 +7,7 @@ use std::convert::TryFrom; use std::str::FromStr; -use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; +use minidom::{Element, IntoAttributeValue}; use jid::Jid; @@ -95,152 +95,150 @@ pub struct Message { pub payloads: Vec, } -pub fn parse_message(root: &Element) -> Result { - if !root.is("message", ns::JABBER_CLIENT) { - return Err(Error::ParseError("This is not a message element.")); - } - let from = root.attr("from") - .and_then(|value| value.parse().ok()); - let to = root.attr("to") - .and_then(|value| value.parse().ok()); - let id = root.attr("id") - .and_then(|value| value.parse().ok()); - let type_ = match root.attr("type") { - Some(type_) => type_.parse()?, - None => Default::default(), - }; - let mut payloads = vec!(); - for elem in root.children() { - let payload = if let Ok(body) = body::parse_body(elem) { - Some(MessagePayload::Body(body)) - } else if let Ok(stanza_error) = StanzaError::try_from(elem) { - Some(MessagePayload::StanzaError(stanza_error)) - } else if let Ok(chatstate) = ChatState::try_from(elem) { - Some(MessagePayload::ChatState(chatstate)) - } else if let Ok(receipt) = Receipt::try_from(elem) { - Some(MessagePayload::Receipt(receipt)) - } else if let Ok(delay) = Delay::try_from(elem) { - Some(MessagePayload::Delay(delay)) - } else if let Ok(attention) = Attention::try_from(elem) { - Some(MessagePayload::Attention(attention)) - } else if let Ok(replace) = Replace::try_from(elem) { - Some(MessagePayload::MessageCorrect(replace)) - } else if let Ok(eme) = ExplicitMessageEncryption::try_from(elem) { - Some(MessagePayload::ExplicitMessageEncryption(eme)) - } else { - None +impl<'a> TryFrom<&'a Element> for Message { + type Error = Error; + + fn try_from(root: &'a Element) -> Result { + if !root.is("message", ns::JABBER_CLIENT) { + return Err(Error::ParseError("This is not a message element.")); + } + let from = root.attr("from") + .and_then(|value| value.parse().ok()); + let to = root.attr("to") + .and_then(|value| value.parse().ok()); + let id = root.attr("id") + .and_then(|value| value.parse().ok()); + let type_ = match root.attr("type") { + Some(type_) => type_.parse()?, + None => Default::default(), }; - payloads.push(match payload { - Some(payload) => MessagePayloadType::Parsed(payload), - None => MessagePayloadType::XML(elem.clone()), - }); - } - Ok(Message { - from: from, - to: to, - id: id, - type_: type_, - payloads: payloads, - }) -} - -pub fn serialise_payload(payload: &MessagePayload) -> Element { - match *payload { - MessagePayload::Body(ref body) => body::serialise(body), - MessagePayload::StanzaError(ref stanza_error) => stanza_error.into(), - MessagePayload::Attention(ref attention) => attention.into(), - MessagePayload::ChatState(ref chatstate) => chatstate.into(), - MessagePayload::Receipt(ref receipt) => receipt.into(), - MessagePayload::Delay(ref delay) => delay.into(), - MessagePayload::MessageCorrect(ref replace) => replace.into(), - MessagePayload::ExplicitMessageEncryption(ref eme) => eme.into(), + let mut payloads = vec!(); + for elem in root.children() { + let payload = if let Ok(body) = body::parse_body(elem) { + Some(MessagePayload::Body(body)) + } else if let Ok(stanza_error) = StanzaError::try_from(elem) { + Some(MessagePayload::StanzaError(stanza_error)) + } else if let Ok(chatstate) = ChatState::try_from(elem) { + Some(MessagePayload::ChatState(chatstate)) + } else if let Ok(receipt) = Receipt::try_from(elem) { + Some(MessagePayload::Receipt(receipt)) + } else if let Ok(delay) = Delay::try_from(elem) { + Some(MessagePayload::Delay(delay)) + } else if let Ok(attention) = Attention::try_from(elem) { + Some(MessagePayload::Attention(attention)) + } else if let Ok(replace) = Replace::try_from(elem) { + Some(MessagePayload::MessageCorrect(replace)) + } else if let Ok(eme) = ExplicitMessageEncryption::try_from(elem) { + Some(MessagePayload::ExplicitMessageEncryption(eme)) + } else { + None + }; + payloads.push(match payload { + Some(payload) => MessagePayloadType::Parsed(payload), + None => MessagePayloadType::XML(elem.clone()), + }); + } + Ok(Message { + from: from, + to: to, + id: id, + type_: type_, + payloads: payloads, + }) } } -pub fn serialise(message: &Message) -> Element { - let mut stanza = Element::builder("message") - .ns(ns::JABBER_CLIENT) - .attr("from", message.from.clone().and_then(|value| Some(String::from(value)))) - .attr("to", message.to.clone().and_then(|value| Some(String::from(value)))) - .attr("id", message.id.clone()) - .attr("type", message.type_.clone()) - .build(); - for child in message.payloads.clone() { - let elem = match child { - MessagePayloadType::XML(elem) => elem, - MessagePayloadType::Parsed(payload) => serialise_payload(&payload), - }; - stanza.append_child(elem); +impl<'a> Into for &'a MessagePayload { + fn into(self) -> Element { + match *self { + MessagePayload::Body(ref body) => body::serialise(body), + MessagePayload::StanzaError(ref stanza_error) => stanza_error.into(), + MessagePayload::Attention(ref attention) => attention.into(), + MessagePayload::ChatState(ref chatstate) => chatstate.into(), + MessagePayload::Receipt(ref receipt) => receipt.into(), + MessagePayload::Delay(ref delay) => delay.into(), + MessagePayload::MessageCorrect(ref replace) => replace.into(), + MessagePayload::ExplicitMessageEncryption(ref eme) => eme.into(), + } } - stanza } -impl IntoElements for Message { - fn into_elements(self, emitter: &mut ElementEmitter) { - let elem = serialise(&self); - emitter.append_child(elem); +impl<'a> Into for &'a Message { + fn into(self) -> Element { + let mut stanza = Element::builder("message") + .ns(ns::JABBER_CLIENT) + .attr("from", self.from.clone().and_then(|value| Some(String::from(value)))) + .attr("to", self.to.clone().and_then(|value| Some(String::from(value)))) + .attr("id", self.id.clone()) + .attr("type", self.type_.clone()) + .build(); + for child in self.payloads.clone() { + let elem = match child { + MessagePayloadType::XML(elem) => elem, + MessagePayloadType::Parsed(payload) => (&payload).into(), + }; + stanza.append_child(elem); + } + stanza } } #[cfg(test)] mod tests { - use std::str::FromStr; - use minidom::Element; - use jid::Jid; - use message; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let message = message::parse_message(&elem).unwrap(); + let message = Message::try_from(&elem).unwrap(); assert_eq!(message.from, None); assert_eq!(message.to, None); assert_eq!(message.id, None); - assert_eq!(message.type_, message::MessageType::Normal); + assert_eq!(message.type_, MessageType::Normal); assert!(message.payloads.is_empty()); } #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let message = message::Message { + let message = Message { from: None, to: None, id: None, - type_: message::MessageType::Normal, + type_: MessageType::Normal, payloads: vec!(), }; - let elem2 = message::serialise(&message); + let elem2 = (&message).into(); assert_eq!(elem, elem2); } #[test] fn test_body() { let elem: Element = "Hello world!".parse().unwrap(); - message::parse_message(&elem).unwrap(); + Message::try_from(&elem).unwrap(); } #[test] fn test_serialise_body() { let elem: Element = "Hello world!".parse().unwrap(); - let message = message::Message { + let message = Message { from: None, to: Some(Jid::from_str("coucou@example.org").unwrap()), id: None, - type_: message::MessageType::Chat, + type_: MessageType::Chat, payloads: vec!( - message::MessagePayloadType::Parsed(message::MessagePayload::Body("Hello world!".to_owned())), + MessagePayloadType::Parsed(MessagePayload::Body("Hello world!".to_owned())), ), }; - let elem2 = message::serialise(&message); + let elem2 = (&message).into(); assert_eq!(elem, elem2); } #[test] fn test_attention() { let elem: Element = "".parse().unwrap(); - let message = message::parse_message(&elem).unwrap(); - let elem2 = message::serialise(&message); + let message = Message::try_from(&elem).unwrap(); + let elem2 = (&message).into(); assert_eq!(elem, elem2); } } From 69cfb14c77fe93fe0a8c92aeaf114581867ea715 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 21:24:17 +0100 Subject: [PATCH 155/698] presence: Switch to Into/TryFrom. --- src/presence.rs | 282 ++++++++++++++++++++++++------------------------ 1 file changed, 144 insertions(+), 138 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index 4ec697cbbe1c6cf56da436f72e0778fdac190499..cfaf5e71ab90caf5d6acf00fe377f7789f2265f4 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -128,177 +128,183 @@ pub struct Presence { pub payloads: Vec, } -pub fn parse_presence(root: &Element) -> Result { - if !root.is("presence", ns::JABBER_CLIENT) { - return Err(Error::ParseError("This is not a presence element.")); - } - let from = root.attr("from") - .and_then(|value| value.parse().ok()); - let to = root.attr("to") - .and_then(|value| value.parse().ok()); - let id = root.attr("id") - .and_then(|value| value.parse().ok()); - let type_ = match root.attr("type") { - Some(type_) => type_.parse()?, - None => Default::default(), - }; - let mut show = None; - let mut statuses = BTreeMap::new(); - let mut priority = None; - let mut payloads = vec!(); - for elem in root.children() { - if elem.is("show", ns::JABBER_CLIENT) { - if show.is_some() { - return Err(Error::ParseError("More than one show element in a presence.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in show element.")); - } - show = Some(match elem.text().as_ref() { - "away" => Show::Away, - "chat" => Show::Chat, - "dnd" => Show::Dnd, - "xa" => Show::Xa, - - _ => return Err(Error::ParseError("Invalid value for show.")), - }); - } else if elem.is("status", ns::JABBER_CLIENT) { - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in status element.")); - } - let lang = elem.attr("xml:lang").unwrap_or("").to_owned(); - if let Some(_) = statuses.insert(lang, elem.text()) { - return Err(Error::ParseError("Status element present twice for the same xml:lang.")); - } - } else if elem.is("priority", ns::JABBER_CLIENT) { - if priority.is_some() { - return Err(Error::ParseError("More than one priority element in a presence.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in priority element.")); - } - priority = Some(Priority::from_str(elem.text().as_ref())?); - } else { - let payload = if let Ok(stanza_error) = StanzaError::try_from(elem) { - Some(PresencePayload::StanzaError(stanza_error)) - } else if let Ok(delay) = Delay::try_from(elem) { - Some(PresencePayload::Delay(delay)) - } else if let Ok(ecaps2) = ECaps2::try_from(elem) { - Some(PresencePayload::ECaps2(ecaps2)) +impl<'a> TryFrom<&'a Element> for Presence { + type Error = Error; + + fn try_from(root: &'a Element) -> Result { + if !root.is("presence", ns::JABBER_CLIENT) { + return Err(Error::ParseError("This is not a presence element.")); + } + let from = root.attr("from") + .and_then(|value| value.parse().ok()); + let to = root.attr("to") + .and_then(|value| value.parse().ok()); + let id = root.attr("id") + .and_then(|value| value.parse().ok()); + let type_ = match root.attr("type") { + Some(type_) => type_.parse()?, + None => Default::default(), + }; + let mut show = None; + let mut statuses = BTreeMap::new(); + let mut priority = None; + let mut payloads = vec!(); + for elem in root.children() { + if elem.is("show", ns::JABBER_CLIENT) { + if show.is_some() { + return Err(Error::ParseError("More than one show element in a presence.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in show element.")); + } + show = Some(match elem.text().as_ref() { + "away" => Show::Away, + "chat" => Show::Chat, + "dnd" => Show::Dnd, + "xa" => Show::Xa, + + _ => return Err(Error::ParseError("Invalid value for show.")), + }); + } else if elem.is("status", ns::JABBER_CLIENT) { + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in status element.")); + } + let lang = elem.attr("xml:lang").unwrap_or("").to_owned(); + if let Some(_) = statuses.insert(lang, elem.text()) { + return Err(Error::ParseError("Status element present twice for the same xml:lang.")); + } + } else if elem.is("priority", ns::JABBER_CLIENT) { + if priority.is_some() { + return Err(Error::ParseError("More than one priority element in a presence.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in priority element.")); + } + priority = Some(Priority::from_str(elem.text().as_ref())?); } else { - None - }; - payloads.push(match payload { - Some(payload) => PresencePayloadType::Parsed(payload), - None => PresencePayloadType::XML(elem.clone()), - }); + let payload = if let Ok(stanza_error) = StanzaError::try_from(elem) { + Some(PresencePayload::StanzaError(stanza_error)) + } else if let Ok(delay) = Delay::try_from(elem) { + Some(PresencePayload::Delay(delay)) + } else if let Ok(ecaps2) = ECaps2::try_from(elem) { + Some(PresencePayload::ECaps2(ecaps2)) + } else { + None + }; + payloads.push(match payload { + Some(payload) => PresencePayloadType::Parsed(payload), + None => PresencePayloadType::XML(elem.clone()), + }); + } } + Ok(Presence { + from: from, + to: to, + id: id, + type_: type_, + show: show, + statuses: statuses, + priority: priority.unwrap_or(0i8), + payloads: payloads, + }) } - Ok(Presence { - from: from, - to: to, - id: id, - type_: type_, - show: show, - statuses: statuses, - priority: priority.unwrap_or(0i8), - payloads: payloads, - }) } -pub fn serialise_payload(payload: &PresencePayload) -> Element { - match *payload { - PresencePayload::Show(ref show) => { - Element::builder("status") - .ns(ns::JABBER_CLIENT) - .append(show.to_owned()) - .build() - }, - PresencePayload::Status(ref status) => { - Element::builder("status") - .ns(ns::JABBER_CLIENT) - .append(status.to_owned()) - .build() - }, - PresencePayload::Priority(ref priority) => { - Element::builder("status") - .ns(ns::JABBER_CLIENT) - .append(format!("{}", priority)) - .build() - }, - PresencePayload::StanzaError(ref stanza_error) => stanza_error.into(), - PresencePayload::Delay(ref delay) => delay.into(), - PresencePayload::ECaps2(ref ecaps2) => ecaps2.into(), +impl<'a> Into for &'a PresencePayload { + fn into(self) -> Element { + match *self { + PresencePayload::Show(ref show) => { + Element::builder("status") + .ns(ns::JABBER_CLIENT) + .append(show.to_owned()) + .build() + }, + PresencePayload::Status(ref status) => { + Element::builder("status") + .ns(ns::JABBER_CLIENT) + .append(status.to_owned()) + .build() + }, + PresencePayload::Priority(ref priority) => { + Element::builder("status") + .ns(ns::JABBER_CLIENT) + .append(format!("{}", priority)) + .build() + }, + PresencePayload::StanzaError(ref stanza_error) => stanza_error.into(), + PresencePayload::Delay(ref delay) => delay.into(), + PresencePayload::ECaps2(ref ecaps2) => ecaps2.into(), + } } } -pub fn serialise(presence: &Presence) -> Element { - let mut stanza = Element::builder("presence") - .ns(ns::JABBER_CLIENT) - .attr("from", presence.from.clone().and_then(|value| Some(String::from(value)))) - .attr("to", presence.to.clone().and_then(|value| Some(String::from(value)))) - .attr("id", presence.id.clone()) - .attr("type", presence.type_.clone()) - .build(); - for child in presence.payloads.clone() { - let elem = match child { - PresencePayloadType::XML(elem) => elem, - PresencePayloadType::Parsed(payload) => serialise_payload(&payload), - }; - stanza.append_child(elem); +impl<'a> Into for &'a Presence { + fn into(self) -> Element { + let mut stanza = Element::builder("presence") + .ns(ns::JABBER_CLIENT) + .attr("from", self.from.clone().and_then(|value| Some(String::from(value)))) + .attr("to", self.to.clone().and_then(|value| Some(String::from(value)))) + .attr("id", self.id.clone()) + .attr("type", self.type_.clone()) + .build(); + for child in self.payloads.clone() { + let elem = match child { + PresencePayloadType::XML(elem) => elem, + PresencePayloadType::Parsed(payload) => (&payload).into(), + }; + stanza.append_child(elem); + } + stanza } - stanza } #[cfg(test)] mod tests { use std::collections::BTreeMap; - use minidom::Element; - use error::Error; - use presence; + use super::*; use ns; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let presence = presence::parse_presence(&elem).unwrap(); + let presence = Presence::try_from(&elem).unwrap(); assert_eq!(presence.from, None); assert_eq!(presence.to, None); assert_eq!(presence.id, None); - assert_eq!(presence.type_, presence::PresenceType::Available); + assert_eq!(presence.type_, PresenceType::Available); assert!(presence.payloads.is_empty()); } #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let presence = presence::Presence { + let presence = Presence { from: None, to: None, id: None, - type_: presence::PresenceType::Unavailable, + type_: PresenceType::Unavailable, show: None, statuses: BTreeMap::new(), priority: 0i8, payloads: vec!(), }; - let elem2 = presence::serialise(&presence); + let elem2 = (&presence).into(); assert_eq!(elem, elem2); } #[test] fn test_show() { let elem: Element = "chat".parse().unwrap(); - let presence = presence::parse_presence(&elem).unwrap(); + let presence = Presence::try_from(&elem).unwrap(); assert_eq!(presence.payloads.len(), 0); - assert_eq!(presence.show, Some(presence::Show::Chat)); + assert_eq!(presence.show, Some(Show::Chat)); } #[test] fn test_missing_show_value() { // "online" used to be a pretty common mistake. let elem: Element = "".parse().unwrap(); - let error = presence::parse_presence(&elem).unwrap_err(); + let error = Presence::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -310,7 +316,7 @@ mod tests { fn test_invalid_show() { // "online" used to be a pretty common mistake. let elem: Element = "online".parse().unwrap(); - let error = presence::parse_presence(&elem).unwrap_err(); + let error = Presence::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -321,7 +327,7 @@ mod tests { #[test] fn test_empty_status() { let elem: Element = "".parse().unwrap(); - let presence = presence::parse_presence(&elem).unwrap(); + let presence = Presence::try_from(&elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.statuses.len(), 1); assert_eq!(presence.statuses[""], ""); @@ -330,7 +336,7 @@ mod tests { #[test] fn test_status() { let elem: Element = "Here!".parse().unwrap(); - let presence = presence::parse_presence(&elem).unwrap(); + let presence = Presence::try_from(&elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.statuses.len(), 1); assert_eq!(presence.statuses[""], "Here!"); @@ -339,7 +345,7 @@ mod tests { #[test] fn test_multiple_statuses() { let elem: Element = "Here!Là!".parse().unwrap(); - let presence = presence::parse_presence(&elem).unwrap(); + let presence = Presence::try_from(&elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.statuses.len(), 2); assert_eq!(presence.statuses[""], "Here!"); @@ -349,7 +355,7 @@ mod tests { #[test] fn test_invalid_multiple_statuses() { let elem: Element = "Here!Là!".parse().unwrap(); - let error = presence::parse_presence(&elem).unwrap_err(); + let error = Presence::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -360,7 +366,7 @@ mod tests { #[test] fn test_priority() { let elem: Element = "-1".parse().unwrap(); - let presence = presence::parse_presence(&elem).unwrap(); + let presence = Presence::try_from(&elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.priority, -1i8); } @@ -368,7 +374,7 @@ mod tests { #[test] fn test_invalid_priority() { let elem: Element = "128".parse().unwrap(); - let error = presence::parse_presence(&elem).unwrap_err(); + let error = Presence::try_from(&elem).unwrap_err(); match error { Error::ParseIntError(_) => (), _ => panic!(), @@ -378,8 +384,8 @@ mod tests { #[test] fn test_unknown_child() { let elem: Element = "".parse().unwrap(); - let presence = presence::parse_presence(&elem).unwrap(); - if let presence::PresencePayloadType::XML(ref payload) = presence.payloads[0] { + let presence = Presence::try_from(&elem).unwrap(); + if let PresencePayloadType::XML(ref payload) = presence.payloads[0] { assert!(payload.is("test", "invalid")); } else { panic!("Did successfully parse an invalid element."); @@ -390,7 +396,7 @@ mod tests { #[ignore] fn test_invalid_status_child() { let elem: Element = "".parse().unwrap(); - let error = presence::parse_presence(&elem).unwrap_err(); + let error = Presence::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -402,7 +408,7 @@ mod tests { #[ignore] fn test_invalid_attribute() { let elem: Element = "".parse().unwrap(); - let error = presence::parse_presence(&elem).unwrap_err(); + let error = Presence::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -412,19 +418,19 @@ mod tests { #[test] fn test_serialise_status() { - let status = presence::Status::from("Hello world!"); - let payloads = vec!(presence::PresencePayloadType::Parsed(presence::PresencePayload::Status(status))); - let presence = presence::Presence { + let status = Status::from("Hello world!"); + let payloads = vec!(PresencePayloadType::Parsed(PresencePayload::Status(status))); + let presence = Presence { from: None, to: None, id: None, - type_: presence::PresenceType::Unavailable, + type_: PresenceType::Unavailable, show: None, statuses: BTreeMap::new(), priority: 0i8, payloads: payloads, }; - let elem = presence::serialise(&presence); + let elem: Element = (&presence).into(); assert!(elem.is("presence", ns::JABBER_CLIENT)); assert!(elem.children().collect::>()[0].is("status", ns::JABBER_CLIENT)); } From 414210796507c03c71ded2dfda77a8a60da25a48 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 21:38:23 +0100 Subject: [PATCH 156/698] message: Merge body in this module, and make it support xml:lang. --- src/body.rs | 86 -------------------------------------------------- src/lib.rs | 3 -- src/message.rs | 81 +++++++++++++++++++++++++++++++---------------- 3 files changed, 53 insertions(+), 117 deletions(-) delete mode 100644 src/body.rs diff --git a/src/body.rs b/src/body.rs deleted file mode 100644 index 8f23b56927dad6d04827aad39bc06e2ff92d403f..0000000000000000000000000000000000000000 --- a/src/body.rs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) 2017 Emmanuel Gil Peyrot -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -use minidom::Element; - -use error::Error; - -use ns; - -pub type Body = String; - -pub fn parse_body(root: &Element) -> Result { - // TODO: also support components and servers. - if !root.is("body", ns::JABBER_CLIENT) { - return Err(Error::ParseError("This is not a body element.")); - } - for _ in root.children() { - return Err(Error::ParseError("Unknown child in body element.")); - } - Ok(root.text()) -} - -pub fn serialise(body: &Body) -> Element { - Element::builder("body") - .ns(ns::JABBER_CLIENT) - .append(body.to_owned()) - .build() -} - -#[cfg(test)] -mod tests { - use minidom::Element; - use error::Error; - use body; - use ns; - - #[test] - fn test_simple() { - let elem: Element = "".parse().unwrap(); - body::parse_body(&elem).unwrap(); - } - - #[test] - fn test_invalid() { - let elem: Element = "".parse().unwrap(); - let error = body::parse_body(&elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "This is not a body element."); - } - - #[test] - fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); - let error = body::parse_body(&elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown child in body element."); - } - - #[test] - #[ignore] - fn test_invalid_attribute() { - let elem: Element = "".parse().unwrap(); - let error = body::parse_body(&elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown attribute in body element."); - } - - #[test] - fn test_serialise() { - let body = body::Body::from("Hello world!"); - let elem = body::serialise(&body); - assert!(elem.is("body", ns::JABBER_CLIENT)); - } -} diff --git a/src/lib.rs b/src/lib.rs index e3fb5356424a05396f7d8c6d3a21b7fd8c4fdd6d..67c06296c339d8f0fcab94fe1d279e254088c428 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,9 +37,6 @@ pub mod iq; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod stanza_error; -/// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence -pub mod body; - /// XEP-0004: Data Forms pub mod data_forms; diff --git a/src/message.rs b/src/message.rs index 51baf9b9ffb8a0cd4d2dbaec8d67816a6ed83afa..c54b0ab4d00b57891af31b6a9eed95a08211cc3f 100644 --- a/src/message.rs +++ b/src/message.rs @@ -6,6 +6,7 @@ use std::convert::TryFrom; use std::str::FromStr; +use std::collections::BTreeMap; use minidom::{Element, IntoAttributeValue}; @@ -15,7 +16,6 @@ use error::Error; use ns; -use body; use stanza_error::StanzaError; use chatstates::ChatState; use receipts::Receipt; @@ -27,7 +27,6 @@ use eme::ExplicitMessageEncryption; /// Lists every known payload of a ``. #[derive(Debug, Clone)] pub enum MessagePayload { - Body(body::Body), StanzaError(StanzaError), ChatState(ChatState), Receipt(Receipt), @@ -86,12 +85,16 @@ pub enum MessagePayloadType { Parsed(MessagePayload), } +type Lang = String; +type Body = String; + #[derive(Debug, Clone)] pub struct Message { pub from: Option, pub to: Option, pub id: Option, pub type_: MessageType, + pub bodies: BTreeMap, pub payloads: Vec, } @@ -112,37 +115,47 @@ impl<'a> TryFrom<&'a Element> for Message { Some(type_) => type_.parse()?, None => Default::default(), }; + let mut bodies = BTreeMap::new(); let mut payloads = vec!(); for elem in root.children() { - let payload = if let Ok(body) = body::parse_body(elem) { - Some(MessagePayload::Body(body)) - } else if let Ok(stanza_error) = StanzaError::try_from(elem) { - Some(MessagePayload::StanzaError(stanza_error)) - } else if let Ok(chatstate) = ChatState::try_from(elem) { - Some(MessagePayload::ChatState(chatstate)) - } else if let Ok(receipt) = Receipt::try_from(elem) { - Some(MessagePayload::Receipt(receipt)) - } else if let Ok(delay) = Delay::try_from(elem) { - Some(MessagePayload::Delay(delay)) - } else if let Ok(attention) = Attention::try_from(elem) { - Some(MessagePayload::Attention(attention)) - } else if let Ok(replace) = Replace::try_from(elem) { - Some(MessagePayload::MessageCorrect(replace)) - } else if let Ok(eme) = ExplicitMessageEncryption::try_from(elem) { - Some(MessagePayload::ExplicitMessageEncryption(eme)) + if elem.is("body", ns::JABBER_CLIENT) { + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in body element.")); + } + let lang = elem.attr("xml:lang").unwrap_or("").to_owned(); + if let Some(_) = bodies.insert(lang, elem.text()) { + return Err(Error::ParseError("Body element present twice for the same xml:lang.")); + } } else { - None - }; - payloads.push(match payload { - Some(payload) => MessagePayloadType::Parsed(payload), - None => MessagePayloadType::XML(elem.clone()), - }); + let payload = if let Ok(stanza_error) = StanzaError::try_from(elem) { + Some(MessagePayload::StanzaError(stanza_error)) + } else if let Ok(chatstate) = ChatState::try_from(elem) { + Some(MessagePayload::ChatState(chatstate)) + } else if let Ok(receipt) = Receipt::try_from(elem) { + Some(MessagePayload::Receipt(receipt)) + } else if let Ok(delay) = Delay::try_from(elem) { + Some(MessagePayload::Delay(delay)) + } else if let Ok(attention) = Attention::try_from(elem) { + Some(MessagePayload::Attention(attention)) + } else if let Ok(replace) = Replace::try_from(elem) { + Some(MessagePayload::MessageCorrect(replace)) + } else if let Ok(eme) = ExplicitMessageEncryption::try_from(elem) { + Some(MessagePayload::ExplicitMessageEncryption(eme)) + } else { + None + }; + payloads.push(match payload { + Some(payload) => MessagePayloadType::Parsed(payload), + None => MessagePayloadType::XML(elem.clone()), + }); + } } Ok(Message { from: from, to: to, id: id, type_: type_, + bodies: BTreeMap::new(), payloads: payloads, }) } @@ -151,7 +164,6 @@ impl<'a> TryFrom<&'a Element> for Message { impl<'a> Into for &'a MessagePayload { fn into(self) -> Element { match *self { - MessagePayload::Body(ref body) => body::serialise(body), MessagePayload::StanzaError(ref stanza_error) => stanza_error.into(), MessagePayload::Attention(ref attention) => attention.into(), MessagePayload::ChatState(ref chatstate) => chatstate.into(), @@ -171,6 +183,17 @@ impl<'a> Into for &'a Message { .attr("to", self.to.clone().and_then(|value| Some(String::from(value)))) .attr("id", self.id.clone()) .attr("type", self.type_.clone()) + .append(self.bodies.iter() + .map(|(lang, body)| { + Element::builder("body") + .ns(ns::JABBER_CLIENT) + .attr("xml:lang", match lang.as_ref() { + "" => None, + lang => Some(lang), + }) + .append(body.clone()) + .build() }) + .collect::>()) .build(); for child in self.payloads.clone() { let elem = match child { @@ -206,6 +229,7 @@ mod tests { to: None, id: None, type_: MessageType::Normal, + bodies: BTreeMap::new(), payloads: vec!(), }; let elem2 = (&message).into(); @@ -221,14 +245,15 @@ mod tests { #[test] fn test_serialise_body() { let elem: Element = "Hello world!".parse().unwrap(); + let mut bodies = BTreeMap::new(); + bodies.insert(String::from(""), String::from("Hello world!")); let message = Message { from: None, to: Some(Jid::from_str("coucou@example.org").unwrap()), id: None, type_: MessageType::Chat, - payloads: vec!( - MessagePayloadType::Parsed(MessagePayload::Body("Hello world!".to_owned())), - ), + bodies: bodies, + payloads: vec!(), }; let elem2 = (&message).into(); assert_eq!(elem, elem2); From 3dfb05aab3e6835c5572de015203e27a045085ed Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 21:41:33 +0100 Subject: [PATCH 157/698] lib: Fix documentation. --- src/lib.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 67c06296c339d8f0fcab94fe1d279e254088c428..7faf06c6c8f0e672d8a9715668d1ffe96e0ba0bf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,10 @@ //! A crate parsing common XMPP elements into Rust structures. //! -//! Each module implements a `parse` function, which takes a minidom -//! `Element` reference and returns `Some(MessagePayload)` if the parsing -//! succeeded, None otherwise. +//! Each module implements the `TryFrom<&minidom::Element>` trait, which takes +//! a minidom `Element` reference and returns a `Result`. //! -//! Parsed structs can then be manipulated internally, and serialised back -//! before being sent over the wire. +//! Parsed structs can then be manipulated manually, and must be serialised +//! back before being sent over the wire. // Copyright (c) 2017 Emmanuel Gil Peyrot // From 8f85c95a5255b8ef6039fc9e0bebaf8038d6bbf6 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 21:54:12 +0100 Subject: [PATCH 158/698] Release version 0.2.0! --- Cargo.toml | 4 ++-- ChangeLog | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ea7eecef2b81b19673c0ad916bafc0032d0341c3..85e089c9c0e3a6c605c68ba018efda29001f7204 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "xmpp-parsers" -version = "0.1.0" +version = "0.2.0" authors = ["Emmanuel Gil Peyrot "] description = "Collection of parsers and serialisers for XMPP extensions" homepage = "https://hg.linkmauve.fr/xmpp-parsers" repository = "https://hg.linkmauve.fr/xmpp-parsers" -keywords = ["xmpp"] +keywords = ["xmpp", "xml"] categories = ["parsing", "network-programming"] license = "MPL-2.0" diff --git a/ChangeLog b/ChangeLog index 83fad57c8c820b87e4c3b31269e9ffbaf8d55ca6..d8e85e11df28f7d4fe9955a72dde69553b21afc8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,12 +1,22 @@ -Version XXX: -2017-0X-XX Emmanuel Gil Peyrot +Version 0.2.0: +2017-05-06 Emmanuel Gil Peyrot * New parsers/serialisers: - Stanza error, as per RFC 6120 §8.3. + - Jingle SOCKS5 Transport, XEP-0260. * Incompatible changes: + - Parsers and serialisers now all implement TryFrom + and Into, instead of the old parse_* and serialise_* + functions. - Presence has got an overhaul, it now hosts show, statuses and priority in its struct. The status module has also been dropped. + - Message now supports multiple bodies, each in a different + language. The body module has also been dropped. - Iq now gets a proper StanzaError when the type is error. + - Fix bogus Jingle payload, which was requiring both + description and transport. + * Crate updates: + - minidom 0.3.0 Version 0.1.0: 2017-04-29 Emmanuel Gil Peyrot From 4278c8ce2b7de2acdf40265bf0906b91b833862b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 7 May 2017 15:10:04 +0100 Subject: [PATCH 160/698] message: Add support for the element. --- src/message.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/message.rs b/src/message.rs index c54b0ab4d00b57891af31b6a9eed95a08211cc3f..2c8959b8bb6efcc7d1d85a567849c24a3395b376 100644 --- a/src/message.rs +++ b/src/message.rs @@ -87,6 +87,7 @@ pub enum MessagePayloadType { type Lang = String; type Body = String; +type Subject = String; #[derive(Debug, Clone)] pub struct Message { @@ -95,6 +96,7 @@ pub struct Message { pub id: Option, pub type_: MessageType, pub bodies: BTreeMap, + pub subjects: BTreeMap, pub payloads: Vec, } @@ -116,6 +118,7 @@ impl<'a> TryFrom<&'a Element> for Message { None => Default::default(), }; let mut bodies = BTreeMap::new(); + let mut subjects = BTreeMap::new(); let mut payloads = vec!(); for elem in root.children() { if elem.is("body", ns::JABBER_CLIENT) { @@ -126,6 +129,14 @@ impl<'a> TryFrom<&'a Element> for Message { if let Some(_) = bodies.insert(lang, elem.text()) { return Err(Error::ParseError("Body element present twice for the same xml:lang.")); } + } else if elem.is("subject", ns::JABBER_CLIENT) { + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in subject element.")); + } + let lang = elem.attr("xml:lang").unwrap_or("").to_owned(); + if let Some(_) = subjects.insert(lang, elem.text()) { + return Err(Error::ParseError("Subject element present twice for the same xml:lang.")); + } } else { let payload = if let Ok(stanza_error) = StanzaError::try_from(elem) { Some(MessagePayload::StanzaError(stanza_error)) @@ -156,6 +167,7 @@ impl<'a> TryFrom<&'a Element> for Message { id: id, type_: type_, bodies: BTreeMap::new(), + subjects: subjects, payloads: payloads, }) } @@ -183,6 +195,17 @@ impl<'a> Into for &'a Message { .attr("to", self.to.clone().and_then(|value| Some(String::from(value)))) .attr("id", self.id.clone()) .attr("type", self.type_.clone()) + .append(self.subjects.iter() + .map(|(lang, subject)| { + Element::builder("subject") + .ns(ns::JABBER_CLIENT) + .attr("xml:lang", match lang.as_ref() { + "" => None, + lang => Some(lang), + }) + .append(subject.clone()) + .build() }) + .collect::>()) .append(self.bodies.iter() .map(|(lang, body)| { Element::builder("body") @@ -230,6 +253,7 @@ mod tests { id: None, type_: MessageType::Normal, bodies: BTreeMap::new(), + subjects: BTreeMap::new(), payloads: vec!(), }; let elem2 = (&message).into(); @@ -253,12 +277,23 @@ mod tests { id: None, type_: MessageType::Chat, bodies: bodies, + subjects: BTreeMap::new(), payloads: vec!(), }; let elem2 = (&message).into(); assert_eq!(elem, elem2); } + #[test] + fn test_subject() { + let elem: Element = "Hello world!".parse().unwrap(); + let message = Message::try_from(&elem).unwrap(); + assert_eq!(message.subjects[""], "Hello world!"); + + let elem2 = (&message).into(); + assert_eq!(elem, elem2); + } + #[test] fn test_attention() { let elem: Element = "".parse().unwrap(); From da31e7235718ee7eaf1f9fcb276e9b3ae78bd369 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 7 May 2017 15:06:11 +0100 Subject: [PATCH 161/698] message: Fix wrong parsing of the element, and add a test for it. --- src/message.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/message.rs b/src/message.rs index 2c8959b8bb6efcc7d1d85a567849c24a3395b376..3248451c2651a8beb5d4ac9bedae296d4d2885da 100644 --- a/src/message.rs +++ b/src/message.rs @@ -166,7 +166,7 @@ impl<'a> TryFrom<&'a Element> for Message { to: to, id: id, type_: type_, - bodies: BTreeMap::new(), + bodies: bodies, subjects: subjects, payloads: payloads, }) @@ -263,7 +263,11 @@ mod tests { #[test] fn test_body() { let elem: Element = "Hello world!".parse().unwrap(); - Message::try_from(&elem).unwrap(); + let message = Message::try_from(&elem).unwrap(); + assert_eq!(message.bodies[""], "Hello world!"); + + let elem2 = (&message).into(); + assert_eq!(elem, elem2); } #[test] From 2b29748e6b2dfff871b9bb1e14be53fc9493c4e3 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 7 May 2017 15:09:18 +0100 Subject: [PATCH 162/698] message: Add support for the element. --- src/message.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/message.rs b/src/message.rs index 3248451c2651a8beb5d4ac9bedae296d4d2885da..e7972c3405c423eb30e3a93a7694b70c1340b5d7 100644 --- a/src/message.rs +++ b/src/message.rs @@ -88,6 +88,7 @@ pub enum MessagePayloadType { type Lang = String; type Body = String; type Subject = String; +type Thread = String; #[derive(Debug, Clone)] pub struct Message { @@ -97,6 +98,7 @@ pub struct Message { pub type_: MessageType, pub bodies: BTreeMap, pub subjects: BTreeMap, + pub thread: Option, pub payloads: Vec, } @@ -119,6 +121,7 @@ impl<'a> TryFrom<&'a Element> for Message { }; let mut bodies = BTreeMap::new(); let mut subjects = BTreeMap::new(); + let mut thread = None; let mut payloads = vec!(); for elem in root.children() { if elem.is("body", ns::JABBER_CLIENT) { @@ -137,6 +140,14 @@ impl<'a> TryFrom<&'a Element> for Message { if let Some(_) = subjects.insert(lang, elem.text()) { return Err(Error::ParseError("Subject element present twice for the same xml:lang.")); } + } else if elem.is("thread", ns::JABBER_CLIENT) { + if thread.is_some() { + return Err(Error::ParseError("Thread element present twice.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in thread element.")); + } + thread = Some(elem.text()); } else { let payload = if let Ok(stanza_error) = StanzaError::try_from(elem) { Some(MessagePayload::StanzaError(stanza_error)) @@ -168,6 +179,7 @@ impl<'a> TryFrom<&'a Element> for Message { type_: type_, bodies: bodies, subjects: subjects, + thread: thread, payloads: payloads, }) } @@ -254,6 +266,7 @@ mod tests { type_: MessageType::Normal, bodies: BTreeMap::new(), subjects: BTreeMap::new(), + thread: None, payloads: vec!(), }; let elem2 = (&message).into(); @@ -282,6 +295,7 @@ mod tests { type_: MessageType::Chat, bodies: bodies, subjects: BTreeMap::new(), + thread: None, payloads: vec!(), }; let elem2 = (&message).into(); From 4ec92b16acff01004a7b6945234c36dc6e1b95e9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 7 May 2017 15:23:06 +0100 Subject: [PATCH 163/698] Fix most clippy warnings. --- src/jingle_s5b.rs | 8 ++++---- src/mam.rs | 5 +---- src/message.rs | 4 ++-- src/presence.rs | 2 +- src/stanza_error.rs | 2 +- 5 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index b0d7266f42e16e5ad1c531ef08a7e32307407bfb..4c55d76583f9effa538dba86b11b9b405d90f028 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -182,7 +182,7 @@ impl<'a> TryFrom<&'a Element> for Transport { }); TransportPayload::Candidates(candidates) } else if child.is("activated", ns::JINGLE_S5B) { - if let Some(_) = payload { + if payload.is_some() { return Err(Error::ParseError("Non-activated child already present in JingleS5B transport element.")); } let cid = child.attr("cid") @@ -190,12 +190,12 @@ impl<'a> TryFrom<&'a Element> for Transport { .parse()?; TransportPayload::Activated(cid) } else if child.is("candidate-error", ns::JINGLE_S5B) { - if let Some(_) = payload { + if payload.is_some() { return Err(Error::ParseError("Non-candidate-error child already present in JingleS5B transport element.")); } TransportPayload::CandidateError } else if child.is("candidate-used", ns::JINGLE_S5B) { - if let Some(_) = payload { + if payload.is_some() { return Err(Error::ParseError("Non-candidate-used child already present in JingleS5B transport element.")); } let cid = child.attr("cid") @@ -203,7 +203,7 @@ impl<'a> TryFrom<&'a Element> for Transport { .parse()?; TransportPayload::CandidateUsed(cid) } else if child.is("proxy-error", ns::JINGLE_S5B) { - if let Some(_) = payload { + if payload.is_some() { return Err(Error::ParseError("Non-proxy-error child already present in JingleS5B transport element.")); } TransportPayload::ProxyError diff --git a/src/mam.rs b/src/mam.rs index c831a0993d237fd9441101ad02a6fed5f9c7e8c6..640a3c042ae1dc61b675d3c5bb901b65a77fe5ca 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -217,10 +217,7 @@ impl<'a> Into for &'a Fin { fn into(self) -> Element { let mut elem = Element::builder("fin") .ns(ns::MAM) - .attr("complete", match self.complete { - true => Some("true"), - false => None, - }) + .attr("complete", if self.complete { Some("true") } else { None }) .build(); elem.append_child((&self.set).into()); elem diff --git a/src/message.rs b/src/message.rs index e7972c3405c423eb30e3a93a7694b70c1340b5d7..bb8e8b46abe1f646265cbec0adf9dfc4155cad03 100644 --- a/src/message.rs +++ b/src/message.rs @@ -129,7 +129,7 @@ impl<'a> TryFrom<&'a Element> for Message { return Err(Error::ParseError("Unknown child in body element.")); } let lang = elem.attr("xml:lang").unwrap_or("").to_owned(); - if let Some(_) = bodies.insert(lang, elem.text()) { + if bodies.insert(lang, elem.text()).is_some() { return Err(Error::ParseError("Body element present twice for the same xml:lang.")); } } else if elem.is("subject", ns::JABBER_CLIENT) { @@ -137,7 +137,7 @@ impl<'a> TryFrom<&'a Element> for Message { return Err(Error::ParseError("Unknown child in subject element.")); } let lang = elem.attr("xml:lang").unwrap_or("").to_owned(); - if let Some(_) = subjects.insert(lang, elem.text()) { + if subjects.insert(lang, elem.text()).is_some() { return Err(Error::ParseError("Subject element present twice for the same xml:lang.")); } } else if elem.is("thread", ns::JABBER_CLIENT) { diff --git a/src/presence.rs b/src/presence.rs index cfaf5e71ab90caf5d6acf00fe377f7789f2265f4..f354d3101e4ced006933811e5feefcb12b765d45 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -170,7 +170,7 @@ impl<'a> TryFrom<&'a Element> for Presence { return Err(Error::ParseError("Unknown child in status element.")); } let lang = elem.attr("xml:lang").unwrap_or("").to_owned(); - if let Some(_) = statuses.insert(lang, elem.text()) { + if statuses.insert(lang, elem.text()).is_some() { return Err(Error::ParseError("Status element present twice for the same xml:lang.")); } } else if elem.is("priority", ns::JABBER_CLIENT) { diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 9a0bfa03dc71eb316580db86cc8f6a7bccb3152e..2f509ee21f6edb84cb1252104cafca57a5f8271f 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -173,7 +173,7 @@ impl<'a> TryFrom<&'a Element> for StanzaError { return Err(Error::ParseError("Unknown element in error text.")); } let lang = child.attr("xml:lang").unwrap_or("").to_owned(); - if let Some(_) = texts.insert(lang, child.text()) { + if texts.insert(lang, child.text()).is_some() { return Err(Error::ParseError("Text element present twice for the same xml:lang.")); } } else if child.ns() == Some(ns::XMPP_STANZAS) { From b22acff15ec8a9eddd5e7a37e65d18992d06610b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 18 May 2017 23:09:29 +0100 Subject: [PATCH 164/698] hashes, ecaps2, jingle_ft: Make the algorithm a proper enum. --- src/ecaps2.rs | 113 ++++++++++++++++++++++++----------------------- src/hashes.rs | 59 +++++++++++++++++++++++-- src/jingle_ft.rs | 5 ++- 3 files changed, 116 insertions(+), 61 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 3d7f2c949438b6d0c2f586dcf1f4d6dbaa6b604e..e397a4558dc29d32c80b8abdf645e692bf8d7abb 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -8,7 +8,7 @@ use std::convert::TryFrom; use disco::{Feature, Identity, Disco}; use data_forms::DataForm; -use hashes::Hash; +use hashes::{Hash, Algo}; use minidom::Element; use error::Error; @@ -118,49 +118,52 @@ pub fn compute_disco(disco: &Disco) -> Vec { final_string } -// TODO: make algo into an enum. -pub fn hash_ecaps2(data: &[u8], algo: &str) -> String { - match algo { - "sha-256" => { - let mut hasher = Sha256::default(); - hasher.input(data); - let hash = hasher.result(); - base64::encode(&hash.as_slice()) +pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { + Ok(Hash { + algo: algo.clone(), + hash: match algo { + Algo::Sha_256 => { + let mut hasher = Sha256::default(); + hasher.input(data); + let hash = hasher.result(); + base64::encode(&hash.as_slice()) + }, + Algo::Sha_512 => { + let mut hasher = Sha512::default(); + hasher.input(data); + let hash = hasher.result(); + base64::encode(&hash.as_slice()) + }, + Algo::Sha3_256 => { + let mut hasher = Sha3_256::default(); + hasher.input(data); + let hash = hasher.result(); + base64::encode(&hash.as_slice()) + }, + Algo::Sha3_512 => { + let mut hasher = Sha3_512::default(); + hasher.input(data); + let hash = hasher.result(); + base64::encode(&hash.as_slice()) + }, + Algo::Blake2b_256 => { + let mut hasher = Blake2b::default(); + hasher.input(data); + let mut buf: [u8; 32] = [0; 32]; + let hash = hasher.variable_result(&mut buf).unwrap(); + base64::encode(hash) + }, + Algo::Blake2b_512 => { + let mut hasher = Blake2b::default(); + hasher.input(data); + let mut buf: [u8; 64] = [0; 64]; + let hash = hasher.variable_result(&mut buf).unwrap(); + base64::encode(hash) + }, + Algo::Sha_1 => return Err(String::from("Disabled algorithm sha-1: unsafe.")), + Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)), }, - "sha-512" => { - let mut hasher = Sha512::default(); - hasher.input(data); - let hash = hasher.result(); - base64::encode(&hash.as_slice()) - }, - "sha3-256" => { - let mut hasher = Sha3_256::default(); - hasher.input(data); - let hash = hasher.result(); - base64::encode(&hash.as_slice()) - }, - "sha3-512" => { - let mut hasher = Sha3_512::default(); - hasher.input(data); - let hash = hasher.result(); - base64::encode(&hash.as_slice()) - }, - "blake2b-256" => { - let mut hasher = Blake2b::default(); - hasher.input(data); - let mut buf: [u8; 32] = [0; 32]; - let hash = hasher.variable_result(&mut buf).unwrap(); - base64::encode(hash) - }, - "blake2b-512" => { - let mut hasher = Blake2b::default(); - hasher.input(data); - let mut buf: [u8; 64] = [0; 64]; - let hash = hasher.variable_result(&mut buf).unwrap(); - base64::encode(hash) - }, - _ => panic!(), - } + }) } #[cfg(test)] @@ -174,9 +177,9 @@ mod tests { let elem: Element = "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=".parse().unwrap(); let ecaps2 = ECaps2::try_from(&elem).unwrap(); assert_eq!(ecaps2.hashes.len(), 2); - assert_eq!(ecaps2.hashes[0].algo, "sha-256"); + assert_eq!(ecaps2.hashes[0].algo, Algo::Sha_256); assert_eq!(ecaps2.hashes[0].hash, "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4="); - assert_eq!(ecaps2.hashes[1].algo, "sha3-256"); + assert_eq!(ecaps2.hashes[1].algo, Algo::Sha3_256); assert_eq!(ecaps2.hashes[1].hash, "+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8="); } @@ -262,10 +265,10 @@ mod tests { assert_eq!(ecaps2.len(), 0x1d9); assert_eq!(ecaps2, expected); - let sha_256 = ecaps2::hash_ecaps2(&ecaps2, "sha-256"); - assert_eq!(sha_256, "kzBZbkqJ3ADrj7v08reD1qcWUwNGHaidNUgD7nHpiw8="); - let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, "sha3-256"); - assert_eq!(sha3_256, "79mdYAfU9rEdTOcWDO7UEAt6E56SUzk/g6TnqUeuD9Q="); + let sha_256 = ecaps2::hash_ecaps2(&ecaps2, Algo::Sha_256).unwrap(); + assert_eq!(sha_256.hash, "kzBZbkqJ3ADrj7v08reD1qcWUwNGHaidNUgD7nHpiw8="); + let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, Algo::Sha3_256).unwrap(); + assert_eq!(sha3_256.hash, "79mdYAfU9rEdTOcWDO7UEAt6E56SUzk/g6TnqUeuD9Q="); } #[test] @@ -434,15 +437,15 @@ mod tests { assert_eq!(ecaps2.len(), 0x543); assert_eq!(ecaps2, expected); - let sha_256 = ecaps2::hash_ecaps2(&ecaps2, "sha-256"); - assert_eq!(sha_256, "u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY="); - let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, "sha3-256"); - assert_eq!(sha3_256, "XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg="); + let sha_256 = ecaps2::hash_ecaps2(&ecaps2, Algo::Sha_256).unwrap(); + assert_eq!(sha_256.hash, "u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY="); + let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, Algo::Sha3_256).unwrap(); + assert_eq!(sha3_256.hash, "XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg="); } #[test] fn test_blake2b_512() { - let hash = ecaps2::hash_ecaps2("abc".as_bytes(), "blake2b-512"); + let hash = ecaps2::hash_ecaps2("abc".as_bytes(), Algo::Blake2b_512).unwrap(); let known_hash: Vec = vec!( 0xBA, 0x80, 0xA5, 0x3F, 0x98, 0x1C, 0x4D, 0x0D, 0x6A, 0x27, 0x97, 0xB6, 0x9F, 0x12, 0xF6, 0xE9, 0x4C, 0x21, 0x2F, 0x14, 0x68, 0x5A, 0xC4, 0xB7, 0x4B, 0x12, 0xBB, 0x6F, 0xDB, 0xFF, 0xA2, 0xD1, @@ -450,6 +453,6 @@ mod tests { 0x18, 0xD3, 0x8A, 0xA8, 0xDB, 0xF1, 0x92, 0x5A, 0xB9, 0x23, 0x86, 0xED, 0xD4, 0x00, 0x99, 0x23, ); let known_hash = base64::encode(&known_hash); - assert_eq!(hash, known_hash); + assert_eq!(hash.hash, known_hash); } } diff --git a/src/hashes.rs b/src/hashes.rs index 6e7bcb2db2c9510b32bc1957e57ffc1666b7672a..403cb0c90eca8989d8ad678a72ddf3b1d0479e28 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -5,16 +5,64 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use std::convert::TryFrom; +use std::str::FromStr; -use minidom::Element; +use minidom::{Element, IntoAttributeValue}; use error::Error; use ns; +#[allow(non_camel_case_types)] +#[derive(Debug, Clone, PartialEq)] +pub enum Algo { + Sha_1, + Sha_256, + Sha_512, + Sha3_256, + Sha3_512, + Blake2b_256, + Blake2b_512, + Unknown(String), +} + +impl FromStr for Algo { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "" => return Err(Error::ParseError("'algo' argument can’t be empty.")), + + "sha-1" => Algo::Sha_1, + "sha-256" => Algo::Sha_256, + "sha-512" => Algo::Sha_512, + "sha3-256" => Algo::Sha3_256, + "sha3-512" => Algo::Sha3_512, + "blake2b-256" => Algo::Blake2b_256, + "blake2b-512" => Algo::Blake2b_512, + value => Algo::Unknown(value.to_owned()), + }) + } +} + +impl IntoAttributeValue for Algo { + fn into_attribute_value(self) -> Option { + Some(String::from(match self { + Algo::Sha_1 => "sha-1", + Algo::Sha_256 => "sha-256", + Algo::Sha_512 => "sha-512", + Algo::Sha3_256 => "sha3-256", + Algo::Sha3_512 => "sha3-512", + Algo::Blake2b_256 => "blake2b-256", + Algo::Blake2b_512 => "blake2b-512", + Algo::Unknown(text) => return Some(text), + })) + } +} + #[derive(Debug, Clone, PartialEq)] pub struct Hash { - pub algo: String, + pub algo: Algo, pub hash: String, } @@ -28,7 +76,10 @@ impl<'a> TryFrom<&'a Element> for Hash { for _ in elem.children() { return Err(Error::ParseError("Unknown child in hash element.")); } - let algo = elem.attr("algo").ok_or(Error::ParseError("Mandatory argument 'algo' not present in hash element."))?.to_owned(); + let algo = match elem.attr("algo") { + None => Err(Error::ParseError("Mandatory argument 'algo' not present in hash element.")), + Some(text) => Algo::from_str(text), + }?; let hash = match elem.text().as_ref() { "" => return Err(Error::ParseError("Hash element shouldn’t be empty.")), text => text.to_owned(), @@ -58,7 +109,7 @@ mod tests { fn test_simple() { let elem: Element = "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=".parse().unwrap(); let hash = Hash::try_from(&elem).unwrap(); - assert_eq!(hash.algo, "sha-256"); + assert_eq!(hash.algo, Algo::Sha_256); assert_eq!(hash.hash, "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU="); } diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index efbe9c9d8b355a8e3d43b8e3fb3d15c051f0c79d..9dd69df1026826ab7a2dcc685b4b5f311476fcde 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -238,6 +238,7 @@ impl<'a> Into for &'a Description { #[cfg(test)] mod tests { use super::*; + use hashes::Algo; #[test] fn test_description() { @@ -261,7 +262,7 @@ mod tests { assert_eq!(desc.file.date, Some(String::from("2015-07-26T21:46:00"))); assert_eq!(desc.file.size, Some(6144u64)); assert_eq!(desc.file.range, None); - assert_eq!(desc.file.hashes[0].algo, "sha-1"); + assert_eq!(desc.file.hashes[0].algo, Algo::Sha_1); assert_eq!(desc.file.hashes[0].hash, "w0mcJylzCn+AfvuGdqkty2+KP48="); } @@ -283,7 +284,7 @@ mod tests { assert_eq!(desc.file.date, None); assert_eq!(desc.file.size, None); assert_eq!(desc.file.range, None); - assert_eq!(desc.file.hashes[0].algo, "sha-1"); + assert_eq!(desc.file.hashes[0].algo, Algo::Sha_1); assert_eq!(desc.file.hashes[0].hash, "w0mcJylzCn+AfvuGdqkty2+KP48="); } } From 36b0bead49d9fe5a180fd61e1ac378d27183677e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 18 May 2017 23:09:30 +0100 Subject: [PATCH 165/698] message: Add stanza-id as a direct payload. --- src/message.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/message.rs b/src/message.rs index bb8e8b46abe1f646265cbec0adf9dfc4155cad03..136f558570fb1cde51b653252ad3eac2ddc95cf4 100644 --- a/src/message.rs +++ b/src/message.rs @@ -23,6 +23,7 @@ use delay::Delay; use attention::Attention; use message_correct::Replace; use eme::ExplicitMessageEncryption; +use stanza_id::StanzaId; /// Lists every known payload of a ``. #[derive(Debug, Clone)] @@ -34,6 +35,7 @@ pub enum MessagePayload { Attention(Attention), MessageCorrect(Replace), ExplicitMessageEncryption(ExplicitMessageEncryption), + StanzaId(StanzaId), } #[derive(Debug, Clone, PartialEq)] @@ -163,6 +165,8 @@ impl<'a> TryFrom<&'a Element> for Message { Some(MessagePayload::MessageCorrect(replace)) } else if let Ok(eme) = ExplicitMessageEncryption::try_from(elem) { Some(MessagePayload::ExplicitMessageEncryption(eme)) + } else if let Ok(stanza_id) = StanzaId::try_from(elem) { + Some(MessagePayload::StanzaId(stanza_id)) } else { None }; @@ -195,6 +199,7 @@ impl<'a> Into for &'a MessagePayload { MessagePayload::Delay(ref delay) => delay.into(), MessagePayload::MessageCorrect(ref replace) => replace.into(), MessagePayload::ExplicitMessageEncryption(ref eme) => eme.into(), + MessagePayload::StanzaId(ref stanza_id) => stanza_id.into(), } } } From bbdf38d58b6c3367c6e26176cb60937fcab4eaf3 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 18 May 2017 23:06:22 +0100 Subject: [PATCH 166/698] message: Stop parsing the payloads automatically. --- src/message.rs | 106 ++++++++++++++++++++++++++----------------------- 1 file changed, 56 insertions(+), 50 deletions(-) diff --git a/src/message.rs b/src/message.rs index 136f558570fb1cde51b653252ad3eac2ddc95cf4..f76824ed95cc77539112e21f05a9ecdaea22037f 100644 --- a/src/message.rs +++ b/src/message.rs @@ -38,6 +38,59 @@ pub enum MessagePayload { StanzaId(StanzaId), } +impl<'a> TryFrom<&'a Element> for MessagePayload { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { + ("error", ns::JABBER_CLIENT) => MessagePayload::StanzaError(StanzaError::try_from(elem)?), + + // XEP-0085 + ("active", ns::CHATSTATES) => MessagePayload::ChatState(ChatState::try_from(elem)?), + ("inactive", ns::CHATSTATES) => MessagePayload::ChatState(ChatState::try_from(elem)?), + ("composing", ns::CHATSTATES) => MessagePayload::ChatState(ChatState::try_from(elem)?), + ("paused", ns::CHATSTATES) => MessagePayload::ChatState(ChatState::try_from(elem)?), + ("gone", ns::CHATSTATES) => MessagePayload::ChatState(ChatState::try_from(elem)?), + + // XEP-0184 + ("request", ns::RECEIPTS) => MessagePayload::Receipt(Receipt::try_from(elem)?), + ("received", ns::RECEIPTS) => MessagePayload::Receipt(Receipt::try_from(elem)?), + + // XEP-0203 + ("delay", ns::DELAY) => MessagePayload::Delay(Delay::try_from(elem)?), + + // XEP-0224 + ("attention", ns::ATTENTION) => MessagePayload::Attention(Attention::try_from(elem)?), + + // XEP-0308 + ("replace", ns::MESSAGE_CORRECT) => MessagePayload::MessageCorrect(Replace::try_from(elem)?), + + // XEP-0359 + ("stanza-id", ns::SID) => MessagePayload::StanzaId(StanzaId::try_from(elem)?), + + // XEP-0380 + ("encryption", ns::EME) => MessagePayload::ExplicitMessageEncryption(ExplicitMessageEncryption::try_from(elem)?), + + _ => return Err(Error::ParseError("Unknown message payload.")) + }) + } +} + +impl<'a> Into for &'a MessagePayload { + fn into(self) -> Element { + match *self { + MessagePayload::StanzaError(ref stanza_error) => stanza_error.into(), + MessagePayload::Attention(ref attention) => attention.into(), + MessagePayload::ChatState(ref chatstate) => chatstate.into(), + MessagePayload::Receipt(ref receipt) => receipt.into(), + MessagePayload::Delay(ref delay) => delay.into(), + MessagePayload::MessageCorrect(ref replace) => replace.into(), + MessagePayload::ExplicitMessageEncryption(ref eme) => eme.into(), + MessagePayload::StanzaId(ref stanza_id) => stanza_id.into(), + } + } +} + #[derive(Debug, Clone, PartialEq)] pub enum MessageType { Chat, @@ -81,12 +134,6 @@ impl IntoAttributeValue for MessageType { } } -#[derive(Debug, Clone)] -pub enum MessagePayloadType { - XML(Element), - Parsed(MessagePayload), -} - type Lang = String; type Body = String; type Subject = String; @@ -101,7 +148,7 @@ pub struct Message { pub bodies: BTreeMap, pub subjects: BTreeMap, pub thread: Option, - pub payloads: Vec, + pub payloads: Vec, } impl<'a> TryFrom<&'a Element> for Message { @@ -151,29 +198,7 @@ impl<'a> TryFrom<&'a Element> for Message { } thread = Some(elem.text()); } else { - let payload = if let Ok(stanza_error) = StanzaError::try_from(elem) { - Some(MessagePayload::StanzaError(stanza_error)) - } else if let Ok(chatstate) = ChatState::try_from(elem) { - Some(MessagePayload::ChatState(chatstate)) - } else if let Ok(receipt) = Receipt::try_from(elem) { - Some(MessagePayload::Receipt(receipt)) - } else if let Ok(delay) = Delay::try_from(elem) { - Some(MessagePayload::Delay(delay)) - } else if let Ok(attention) = Attention::try_from(elem) { - Some(MessagePayload::Attention(attention)) - } else if let Ok(replace) = Replace::try_from(elem) { - Some(MessagePayload::MessageCorrect(replace)) - } else if let Ok(eme) = ExplicitMessageEncryption::try_from(elem) { - Some(MessagePayload::ExplicitMessageEncryption(eme)) - } else if let Ok(stanza_id) = StanzaId::try_from(elem) { - Some(MessagePayload::StanzaId(stanza_id)) - } else { - None - }; - payloads.push(match payload { - Some(payload) => MessagePayloadType::Parsed(payload), - None => MessagePayloadType::XML(elem.clone()), - }); + payloads.push(elem.clone()) } } Ok(Message { @@ -189,21 +214,6 @@ impl<'a> TryFrom<&'a Element> for Message { } } -impl<'a> Into for &'a MessagePayload { - fn into(self) -> Element { - match *self { - MessagePayload::StanzaError(ref stanza_error) => stanza_error.into(), - MessagePayload::Attention(ref attention) => attention.into(), - MessagePayload::ChatState(ref chatstate) => chatstate.into(), - MessagePayload::Receipt(ref receipt) => receipt.into(), - MessagePayload::Delay(ref delay) => delay.into(), - MessagePayload::MessageCorrect(ref replace) => replace.into(), - MessagePayload::ExplicitMessageEncryption(ref eme) => eme.into(), - MessagePayload::StanzaId(ref stanza_id) => stanza_id.into(), - } - } -} - impl<'a> Into for &'a Message { fn into(self) -> Element { let mut stanza = Element::builder("message") @@ -236,11 +246,7 @@ impl<'a> Into for &'a Message { .collect::>()) .build(); for child in self.payloads.clone() { - let elem = match child { - MessagePayloadType::XML(elem) => elem, - MessagePayloadType::Parsed(payload) => (&payload).into(), - }; - stanza.append_child(elem); + stanza.append_child(child); } stanza } From f18043231c450c91cda41c37e38dbead8e228410 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 18 May 2017 23:12:45 +0100 Subject: [PATCH 167/698] ecaps2: Avoid a useless clone. --- src/ecaps2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index e397a4558dc29d32c80b8abdf645e692bf8d7abb..b69a6449d5efb0eab126dfd5d1e6bcd567a6df42 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -120,7 +120,6 @@ pub fn compute_disco(disco: &Disco) -> Vec { pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { Ok(Hash { - algo: algo.clone(), hash: match algo { Algo::Sha_256 => { let mut hasher = Sha256::default(); @@ -163,6 +162,7 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { Algo::Sha_1 => return Err(String::from("Disabled algorithm sha-1: unsafe.")), Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)), }, + algo: algo, }) } From e52817e5cda7540f0d49e9ca173bb14051858006 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 18 May 2017 23:14:07 +0100 Subject: [PATCH 168/698] message: Avoid identical match branches. --- src/message.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/message.rs b/src/message.rs index f76824ed95cc77539112e21f05a9ecdaea22037f..a900502b0b05369fb0d2020bd547f56d00d45146 100644 --- a/src/message.rs +++ b/src/message.rs @@ -46,15 +46,15 @@ impl<'a> TryFrom<&'a Element> for MessagePayload { ("error", ns::JABBER_CLIENT) => MessagePayload::StanzaError(StanzaError::try_from(elem)?), // XEP-0085 - ("active", ns::CHATSTATES) => MessagePayload::ChatState(ChatState::try_from(elem)?), - ("inactive", ns::CHATSTATES) => MessagePayload::ChatState(ChatState::try_from(elem)?), - ("composing", ns::CHATSTATES) => MessagePayload::ChatState(ChatState::try_from(elem)?), - ("paused", ns::CHATSTATES) => MessagePayload::ChatState(ChatState::try_from(elem)?), - ("gone", ns::CHATSTATES) => MessagePayload::ChatState(ChatState::try_from(elem)?), + ("active", ns::CHATSTATES) + | ("inactive", ns::CHATSTATES) + | ("composing", ns::CHATSTATES) + | ("paused", ns::CHATSTATES) + | ("gone", ns::CHATSTATES) => MessagePayload::ChatState(ChatState::try_from(elem)?), // XEP-0184 - ("request", ns::RECEIPTS) => MessagePayload::Receipt(Receipt::try_from(elem)?), - ("received", ns::RECEIPTS) => MessagePayload::Receipt(Receipt::try_from(elem)?), + ("request", ns::RECEIPTS) + | ("received", ns::RECEIPTS) => MessagePayload::Receipt(Receipt::try_from(elem)?), // XEP-0203 ("delay", ns::DELAY) => MessagePayload::Delay(Delay::try_from(elem)?), From fe8dccd5df3effd319260109c35f000c7313c116 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 18 May 2017 23:32:26 +0100 Subject: [PATCH 169/698] presence: Remove now-unused enum values. --- src/presence.rs | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index f354d3101e4ced006933811e5feefcb12b765d45..e1212c7442efb60aafff40b5ca632aaa82d0dc5b 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -48,9 +48,6 @@ pub type Priority = i8; /// Lists every known payload of a ``. #[derive(Debug, Clone)] pub enum PresencePayload { - Show(Show), - Status(Status), - Priority(Priority), StanzaError(StanzaError), Delay(Delay), ECaps2(ECaps2), @@ -213,24 +210,6 @@ impl<'a> TryFrom<&'a Element> for Presence { impl<'a> Into for &'a PresencePayload { fn into(self) -> Element { match *self { - PresencePayload::Show(ref show) => { - Element::builder("status") - .ns(ns::JABBER_CLIENT) - .append(show.to_owned()) - .build() - }, - PresencePayload::Status(ref status) => { - Element::builder("status") - .ns(ns::JABBER_CLIENT) - .append(status.to_owned()) - .build() - }, - PresencePayload::Priority(ref priority) => { - Element::builder("status") - .ns(ns::JABBER_CLIENT) - .append(format!("{}", priority)) - .build() - }, PresencePayload::StanzaError(ref stanza_error) => stanza_error.into(), PresencePayload::Delay(ref delay) => delay.into(), PresencePayload::ECaps2(ref ecaps2) => ecaps2.into(), From 0ad6893d5238b4d81a2f7bf49bcbd4e1beba8563 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 18 May 2017 23:32:44 +0100 Subject: [PATCH 170/698] presence: Stop parsing payloads automatically. --- src/presence.rs | 92 ++++++++++++++++++++++++++----------------------- 1 file changed, 48 insertions(+), 44 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index e1212c7442efb60aafff40b5ca632aaa82d0dc5b..67ca5fa026818a47dc262029332393bf2e9e33f3 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -53,6 +53,34 @@ pub enum PresencePayload { ECaps2(ECaps2), } +impl<'a> TryFrom<&'a Element> for PresencePayload { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { + ("error", ns::JABBER_CLIENT) => PresencePayload::StanzaError(StanzaError::try_from(elem)?), + + // XEP-0203 + ("delay", ns::DELAY) => PresencePayload::Delay(Delay::try_from(elem)?), + + // XEP-0390 + ("c", ns::ECAPS2) => PresencePayload::ECaps2(ECaps2::try_from(elem)?), + + _ => return Err(Error::ParseError("Unknown presence payload.")) + }) + } +} + +impl<'a> Into for &'a PresencePayload { + fn into(self) -> Element { + match *self { + PresencePayload::StanzaError(ref stanza_error) => stanza_error.into(), + PresencePayload::Delay(ref delay) => delay.into(), + PresencePayload::ECaps2(ref ecaps2) => ecaps2.into(), + } + } +} + #[derive(Debug, Clone, PartialEq)] pub enum PresenceType { /// This value is not an acceptable 'type' attribute, it is only used @@ -107,12 +135,6 @@ impl IntoAttributeValue for PresenceType { } } -#[derive(Debug, Clone)] -pub enum PresencePayloadType { - XML(Element), - Parsed(PresencePayload), -} - #[derive(Debug, Clone)] pub struct Presence { pub from: Option, @@ -122,7 +144,7 @@ pub struct Presence { pub show: Option, pub statuses: BTreeMap, pub priority: Priority, - pub payloads: Vec, + pub payloads: Vec, } impl<'a> TryFrom<&'a Element> for Presence { @@ -179,19 +201,7 @@ impl<'a> TryFrom<&'a Element> for Presence { } priority = Some(Priority::from_str(elem.text().as_ref())?); } else { - let payload = if let Ok(stanza_error) = StanzaError::try_from(elem) { - Some(PresencePayload::StanzaError(stanza_error)) - } else if let Ok(delay) = Delay::try_from(elem) { - Some(PresencePayload::Delay(delay)) - } else if let Ok(ecaps2) = ECaps2::try_from(elem) { - Some(PresencePayload::ECaps2(ecaps2)) - } else { - None - }; - payloads.push(match payload { - Some(payload) => PresencePayloadType::Parsed(payload), - None => PresencePayloadType::XML(elem.clone()), - }); + payloads.push(elem.clone()); } } Ok(Presence { @@ -207,16 +217,6 @@ impl<'a> TryFrom<&'a Element> for Presence { } } -impl<'a> Into for &'a PresencePayload { - fn into(self) -> Element { - match *self { - PresencePayload::StanzaError(ref stanza_error) => stanza_error.into(), - PresencePayload::Delay(ref delay) => delay.into(), - PresencePayload::ECaps2(ref ecaps2) => ecaps2.into(), - } - } -} - impl<'a> Into for &'a Presence { fn into(self) -> Element { let mut stanza = Element::builder("presence") @@ -225,13 +225,20 @@ impl<'a> Into for &'a Presence { .attr("to", self.to.clone().and_then(|value| Some(String::from(value)))) .attr("id", self.id.clone()) .attr("type", self.type_.clone()) + .append(self.show.clone()) + .append(self.statuses.iter().map(|(lang, status)| { + Element::builder("status") + .attr("xml:lang", match lang.as_ref() { + "" => None, + lang => Some(lang), + }) + .append(status.clone()) + .build() + }).collect::>()) + .append(if self.priority == 0 { None } else { Some(format!("{}", self.priority)) }) .build(); for child in self.payloads.clone() { - let elem = match child { - PresencePayloadType::XML(elem) => elem, - PresencePayloadType::Parsed(payload) => (&payload).into(), - }; - stanza.append_child(elem); + stanza.append_child(child); } stanza } @@ -241,7 +248,6 @@ impl<'a> Into for &'a Presence { mod tests { use std::collections::BTreeMap; use super::*; - use ns; #[test] fn test_simple() { @@ -364,11 +370,8 @@ mod tests { fn test_unknown_child() { let elem: Element = "".parse().unwrap(); let presence = Presence::try_from(&elem).unwrap(); - if let PresencePayloadType::XML(ref payload) = presence.payloads[0] { - assert!(payload.is("test", "invalid")); - } else { - panic!("Did successfully parse an invalid element."); - } + let payload = &presence.payloads[0]; + assert!(payload.is("test", "invalid")); } #[test] @@ -398,16 +401,17 @@ mod tests { #[test] fn test_serialise_status() { let status = Status::from("Hello world!"); - let payloads = vec!(PresencePayloadType::Parsed(PresencePayload::Status(status))); + let mut statuses = BTreeMap::new(); + statuses.insert(String::from(""), status); let presence = Presence { from: None, to: None, id: None, type_: PresenceType::Unavailable, show: None, - statuses: BTreeMap::new(), + statuses: statuses, priority: 0i8, - payloads: payloads, + payloads: vec!(), }; let elem: Element = (&presence).into(); assert!(elem.is("presence", ns::JABBER_CLIENT)); From 44071830108df7e691eea16004278016a0441452 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 18 May 2017 23:50:08 +0100 Subject: [PATCH 171/698] iq: Move IqPayload parsing into its own Into implementation. --- src/iq.rs | 45 +++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index 4f61260d8184481c836fbb5165c7683ba5d41437..1bf3ba14659a9d5b178f44110fc82b585905c26a 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -31,6 +31,30 @@ pub enum IqPayload { Ping(Ping), } +impl<'a> TryFrom<&'a Element> for IqPayload { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { + // XEP-0030 + ("query", ns::DISCO_INFO) => IqPayload::Disco(Disco::try_from(elem)?), + + // XEP-0047 + ("open", ns::IBB) + | ("data", ns::IBB) + | ("close", ns::IBB) => IqPayload::IBB(IBB::try_from(elem)?), + + // XEP-0166 + ("jingle", ns::JINGLE) => IqPayload::Jingle(Jingle::try_from(elem)?), + + // XEP-0199 + ("ping", ns::PING) => IqPayload::Ping(Ping::try_from(elem)?), + + _ => return Err(Error::ParseError("Unknown iq payload.")) + }) + } +} + #[derive(Debug, Clone)] pub enum IqPayloadType { XML(Element), @@ -98,22 +122,11 @@ impl<'a> TryFrom<&'a Element> for Iq { return Err(Error::ParseError("Wrong number of children in iq element.")); } } else { - let parsed_payload = if let Ok(disco) = Disco::try_from(elem) { - Some(IqPayload::Disco(disco)) - } else if let Ok(ibb) = IBB::try_from(elem) { - Some(IqPayload::IBB(ibb)) - } else if let Ok(jingle) = Jingle::try_from(elem) { - Some(IqPayload::Jingle(jingle)) - } else if let Ok(ping) = Ping::try_from(elem) { - Some(IqPayload::Ping(ping)) - } else { - None - }; - - payload = match parsed_payload { - Some(payload) => Some(IqPayloadType::Parsed(payload)), - None => Some(IqPayloadType::XML(elem.clone())), - }; + payload = match IqPayload::try_from(elem) { + Ok(payload) => Some(IqPayloadType::Parsed(payload)), + // TODO: fix the API to avoid having to swallow the error here. + Err(_) => Some(IqPayloadType::XML(elem.clone())), + } } } From 65469db1e3b8835c483f3a1bf849c36e09eb38ee Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 19 May 2017 00:04:42 +0100 Subject: [PATCH 172/698] iq: Stop parsing the payload automatically. --- src/iq.rs | 41 ++++++++++++++++------------------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index 1bf3ba14659a9d5b178f44110fc82b585905c26a..13efbaac0dd870bdec4e6c72580bd6d92af9ee44 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -55,17 +55,11 @@ impl<'a> TryFrom<&'a Element> for IqPayload { } } -#[derive(Debug, Clone)] -pub enum IqPayloadType { - XML(Element), - Parsed(IqPayload), -} - #[derive(Debug, Clone)] pub enum IqType { - Get(IqPayloadType), - Set(IqPayloadType), - Result(Option), + Get(Element), + Set(Element), + Result(Option), Error(StanzaError), } @@ -122,11 +116,7 @@ impl<'a> TryFrom<&'a Element> for Iq { return Err(Error::ParseError("Wrong number of children in iq element.")); } } else { - payload = match IqPayload::try_from(elem) { - Ok(payload) => Some(IqPayloadType::Parsed(payload)), - // TODO: fix the API to avoid having to swallow the error here. - Err(_) => Some(IqPayloadType::XML(elem.clone())), - } + payload = Some(elem); } } @@ -188,13 +178,10 @@ impl<'a> Into for &'a Iq { .attr("type", self.payload.clone()) .build(); let elem = match self.payload.clone() { - IqType::Get(IqPayloadType::XML(elem)) - | IqType::Set(IqPayloadType::XML(elem)) - | IqType::Result(Some(IqPayloadType::XML(elem))) => elem, + IqType::Get(elem) + | IqType::Set(elem) + | IqType::Result(Some(elem)) => elem, IqType::Error(error) => (&error).into(), - IqType::Get(IqPayloadType::Parsed(payload)) - | IqType::Set(IqPayloadType::Parsed(payload)) - | IqType::Result(Some(IqPayloadType::Parsed(payload))) => (&payload).into(), IqType::Result(None) => return stanza, }; stanza.append_child(elem); @@ -229,7 +216,7 @@ mod tests { assert_eq!(iq.to, None); assert_eq!(iq.id, None); assert!(match iq.payload { - IqType::Get(IqPayloadType::XML(element)) => element == query, + IqType::Get(element) => element == query, _ => false }); } @@ -245,7 +232,7 @@ mod tests { assert_eq!(iq.to, None); assert_eq!(iq.id, None); assert!(match iq.payload { - IqType::Set(IqPayloadType::XML(element)) => element == vcard, + IqType::Set(element) => element == vcard, _ => false }); } @@ -274,7 +261,7 @@ mod tests { assert_eq!(iq.to, None); assert_eq!(iq.id, None); assert!(match iq.payload { - IqType::Result(Some(IqPayloadType::XML(element))) => element == query, + IqType::Result(Some(element)) => element == query, _ => false, }); } @@ -331,8 +318,12 @@ mod tests { fn test_disco() { let elem: Element = "".parse().unwrap(); let iq = Iq::try_from(&elem).unwrap(); - assert!(match iq.payload { - IqType::Get(IqPayloadType::Parsed(IqPayload::Disco(Disco { .. }))) => true, + let payload = match iq.payload { + IqType::Get(ref payload) => IqPayload::try_from(payload).unwrap(), + _ => panic!(), + }; + assert!(match payload { + IqPayload::Disco(Disco { .. }) => true, _ => false, }); } From 6df3c4a679b5a939c6eae740bde4533d57859b14 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 19 May 2017 00:07:51 +0100 Subject: [PATCH 173/698] iq: Remove a bunch of clones. --- src/iq.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index 13efbaac0dd870bdec4e6c72580bd6d92af9ee44..f040b1bde2218da1872a21da6ac040c19f6aecde 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -116,31 +116,31 @@ impl<'a> TryFrom<&'a Element> for Iq { return Err(Error::ParseError("Wrong number of children in iq element.")); } } else { - payload = Some(elem); + payload = Some(elem.clone()); } } let type_ = if type_ == "get" { - if let Some(payload) = payload.clone() { - IqType::Get(payload.clone()) + if let Some(payload) = payload { + IqType::Get(payload) } else { return Err(Error::ParseError("Wrong number of children in iq element.")); } } else if type_ == "set" { - if let Some(payload) = payload.clone() { - IqType::Set(payload.clone()) + if let Some(payload) = payload { + IqType::Set(payload) } else { return Err(Error::ParseError("Wrong number of children in iq element.")); } } else if type_ == "result" { - if let Some(payload) = payload.clone() { - IqType::Result(Some(payload.clone())) + if let Some(payload) = payload { + IqType::Result(Some(payload)) } else { IqType::Result(None) } } else if type_ == "error" { if let Some(payload) = error_payload.clone() { - IqType::Error(payload.clone()) + IqType::Error(payload) } else { return Err(Error::ParseError("Wrong number of children in iq element.")); } From dcb7ac8db21548be2a08d2327fb32e02e23006b1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 19 May 2017 00:10:23 +0100 Subject: [PATCH 174/698] iq: Remove a panic! --- src/iq.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iq.rs b/src/iq.rs index f040b1bde2218da1872a21da6ac040c19f6aecde..b62168983f465922b1898dec935c84c70817c0ba 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -145,7 +145,7 @@ impl<'a> TryFrom<&'a Element> for Iq { return Err(Error::ParseError("Wrong number of children in iq element.")); } } else { - panic!() + return Err(Error::ParseError("Unknown iq type.")); }; Ok(Iq { From 2b8bcebfd95338fbee37fd2df018095e24b55e73 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 19 May 2017 02:09:23 +0100 Subject: [PATCH 175/698] message, iq, presence: Return an Unknown instead of an error when the element is unknown. --- src/iq.rs | 6 +++++- src/message.rs | 6 +++++- src/presence.rs | 6 +++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index b62168983f465922b1898dec935c84c70817c0ba..436247505a9ca1310a23c8a8af4b65a6cd9cd1e5 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -29,6 +29,8 @@ pub enum IqPayload { IBB(IBB), Jingle(Jingle), Ping(Ping), + + Unknown(Element), } impl<'a> TryFrom<&'a Element> for IqPayload { @@ -50,7 +52,7 @@ impl<'a> TryFrom<&'a Element> for IqPayload { // XEP-0199 ("ping", ns::PING) => IqPayload::Ping(Ping::try_from(elem)?), - _ => return Err(Error::ParseError("Unknown iq payload.")) + _ => IqPayload::Unknown(elem.clone()), }) } } @@ -164,6 +166,8 @@ impl<'a> Into for &'a IqPayload { IqPayload::IBB(ref ibb) => ibb.into(), IqPayload::Jingle(ref jingle) => jingle.into(), IqPayload::Ping(ref ping) => ping.into(), + + IqPayload::Unknown(ref elem) => elem.clone(), } } } diff --git a/src/message.rs b/src/message.rs index a900502b0b05369fb0d2020bd547f56d00d45146..96153cfabf72e3a30fecd54ab3004127bac99d61 100644 --- a/src/message.rs +++ b/src/message.rs @@ -36,6 +36,8 @@ pub enum MessagePayload { MessageCorrect(Replace), ExplicitMessageEncryption(ExplicitMessageEncryption), StanzaId(StanzaId), + + Unknown(Element), } impl<'a> TryFrom<&'a Element> for MessagePayload { @@ -71,7 +73,7 @@ impl<'a> TryFrom<&'a Element> for MessagePayload { // XEP-0380 ("encryption", ns::EME) => MessagePayload::ExplicitMessageEncryption(ExplicitMessageEncryption::try_from(elem)?), - _ => return Err(Error::ParseError("Unknown message payload.")) + _ => MessagePayload::Unknown(elem.clone()), }) } } @@ -87,6 +89,8 @@ impl<'a> Into for &'a MessagePayload { MessagePayload::MessageCorrect(ref replace) => replace.into(), MessagePayload::ExplicitMessageEncryption(ref eme) => eme.into(), MessagePayload::StanzaId(ref stanza_id) => stanza_id.into(), + + MessagePayload::Unknown(ref elem) => elem.clone(), } } } diff --git a/src/presence.rs b/src/presence.rs index 67ca5fa026818a47dc262029332393bf2e9e33f3..7eb53b411b4b2efe63e0bebd2b839f6266b228fc 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -51,6 +51,8 @@ pub enum PresencePayload { StanzaError(StanzaError), Delay(Delay), ECaps2(ECaps2), + + Unknown(Element), } impl<'a> TryFrom<&'a Element> for PresencePayload { @@ -66,7 +68,7 @@ impl<'a> TryFrom<&'a Element> for PresencePayload { // XEP-0390 ("c", ns::ECAPS2) => PresencePayload::ECaps2(ECaps2::try_from(elem)?), - _ => return Err(Error::ParseError("Unknown presence payload.")) + _ => PresencePayload::Unknown(elem.clone()), }) } } @@ -77,6 +79,8 @@ impl<'a> Into for &'a PresencePayload { PresencePayload::StanzaError(ref stanza_error) => stanza_error.into(), PresencePayload::Delay(ref delay) => delay.into(), PresencePayload::ECaps2(ref ecaps2) => ecaps2.into(), + + PresencePayload::Unknown(ref elem) => elem.clone(), } } } From 967d4af8430f0c3be6ad5a5ec240c073a9d54320 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 19 May 2017 02:58:18 +0100 Subject: [PATCH 176/698] rsm: Rename First id to make it more explicit. --- src/rsm.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rsm.rs b/src/rsm.rs index 4caab08096b9bc75be4ffb687c931c0d2f82e8a6..6a16d38559c6140c89d0e6a297fe536ec0a3a1fe 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -15,7 +15,7 @@ use ns; #[derive(Debug, Clone)] pub struct First { pub index: Option, - pub base: String, + pub id: String, } #[derive(Debug, Clone)] @@ -68,7 +68,7 @@ impl<'a> TryFrom<&'a Element> for Set { Some(index) => Some(index.parse()?), None => None, }, - base: child.text(), + id: child.text(), }); } else if child.is("index", ns::RSM) { if index.is_some() { @@ -123,7 +123,7 @@ impl<'a> Into for &'a Set { Some(index) => Some(format!("{}", index)), None => None, }) - .append(first.base.clone()).build()); + .append(first.id.clone()).build()); } if self.index.is_some() { elem.append_child(Element::builder("index").ns(ns::RSM).append(format!("{}", self.index.unwrap())).build()); From d680c31cf929052e19c3d9eeea3ab09b9c44a19c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 19 May 2017 02:58:35 +0100 Subject: [PATCH 177/698] iq, message: Wire up MAM to the payloads. --- src/iq.rs | 12 ++++++++++++ src/message.rs | 6 ++++++ 2 files changed, 18 insertions(+) diff --git a/src/iq.rs b/src/iq.rs index 436247505a9ca1310a23c8a8af4b65a6cd9cd1e5..d2d4a6899d0848feadcb6194ad104ffa77a2f756 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -21,6 +21,7 @@ use disco::Disco; use ibb::IBB; use jingle::Jingle; use ping::Ping; +use mam::{Query as MamQuery, Fin as MamFin, Prefs as MamPrefs}; /// Lists every known payload of a ``. #[derive(Debug, Clone)] @@ -29,6 +30,9 @@ pub enum IqPayload { IBB(IBB), Jingle(Jingle), Ping(Ping), + MamQuery(MamQuery), + MamFin(MamFin), + MamPrefs(MamPrefs), Unknown(Element), } @@ -52,6 +56,11 @@ impl<'a> TryFrom<&'a Element> for IqPayload { // XEP-0199 ("ping", ns::PING) => IqPayload::Ping(Ping::try_from(elem)?), + // XEP-0313 + ("query", ns::MAM) => IqPayload::MamQuery(MamQuery::try_from(elem)?), + ("fin", ns::MAM) => IqPayload::MamFin(MamFin::try_from(elem)?), + ("prefs", ns::MAM) => IqPayload::MamPrefs(MamPrefs::try_from(elem)?), + _ => IqPayload::Unknown(elem.clone()), }) } @@ -166,6 +175,9 @@ impl<'a> Into for &'a IqPayload { IqPayload::IBB(ref ibb) => ibb.into(), IqPayload::Jingle(ref jingle) => jingle.into(), IqPayload::Ping(ref ping) => ping.into(), + IqPayload::MamQuery(ref query) => query.into(), + IqPayload::MamFin(ref fin) => fin.into(), + IqPayload::MamPrefs(ref prefs) => prefs.into(), IqPayload::Unknown(ref elem) => elem.clone(), } diff --git a/src/message.rs b/src/message.rs index 96153cfabf72e3a30fecd54ab3004127bac99d61..dae600e8d3a5c0dd1013c8878838da4c7634193b 100644 --- a/src/message.rs +++ b/src/message.rs @@ -24,6 +24,7 @@ use attention::Attention; use message_correct::Replace; use eme::ExplicitMessageEncryption; use stanza_id::StanzaId; +use mam::Result_ as MamResult; /// Lists every known payload of a ``. #[derive(Debug, Clone)] @@ -36,6 +37,7 @@ pub enum MessagePayload { MessageCorrect(Replace), ExplicitMessageEncryption(ExplicitMessageEncryption), StanzaId(StanzaId), + MamResult(MamResult), Unknown(Element), } @@ -67,6 +69,9 @@ impl<'a> TryFrom<&'a Element> for MessagePayload { // XEP-0308 ("replace", ns::MESSAGE_CORRECT) => MessagePayload::MessageCorrect(Replace::try_from(elem)?), + // XEP-0313 + ("result", ns::MAM) => MessagePayload::MamResult(MamResult::try_from(elem)?), + // XEP-0359 ("stanza-id", ns::SID) => MessagePayload::StanzaId(StanzaId::try_from(elem)?), @@ -89,6 +94,7 @@ impl<'a> Into for &'a MessagePayload { MessagePayload::MessageCorrect(ref replace) => replace.into(), MessagePayload::ExplicitMessageEncryption(ref eme) => eme.into(), MessagePayload::StanzaId(ref stanza_id) => stanza_id.into(), + MessagePayload::MamResult(ref result) => result.into(), MessagePayload::Unknown(ref elem) => elem.clone(), } From 61839042bd6a1759cc81a93efafbcb70df4d6ade Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 15:30:22 +0100 Subject: [PATCH 178/698] rsm: Split First into two options, and generate Set earlier during parsing. --- src/rsm.rs | 83 ++++++++++++++++++++++-------------------------------- 1 file changed, 34 insertions(+), 49 deletions(-) diff --git a/src/rsm.rs b/src/rsm.rs index 6a16d38559c6140c89d0e6a297fe536ec0a3a1fe..a6aec04de438fd4733401960502986d418f83016 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -12,18 +12,13 @@ use error::Error; use ns; -#[derive(Debug, Clone)] -pub struct First { - pub index: Option, - pub id: String, -} - #[derive(Debug, Clone)] pub struct Set { pub after: Option, pub before: Option, pub count: Option, - pub first: Option, + pub first: Option, + pub first_index: Option, pub index: Option, pub last: Option, pub max: Option, @@ -36,68 +31,61 @@ impl<'a> TryFrom<&'a Element> for Set { if !elem.is("set", ns::RSM) { return Err(Error::ParseError("This is not a RSM element.")); } - let mut after = None; - let mut before = None; - let mut count = None; - let mut first = None; - let mut index = None; - let mut last = None; - let mut max = None; + let mut set = Set { + after: None, + before: None, + count: None, + first: None, + first_index: None, + index: None, + last: None, + max: None, + }; for child in elem.children() { if child.is("after", ns::RSM) { - if after.is_some() { + if set.after.is_some() { return Err(Error::ParseError("Set can’t have more than one after.")); } - after = Some(child.text()); + set.after = Some(child.text()); } else if child.is("before", ns::RSM) { - if before.is_some() { + if set.before.is_some() { return Err(Error::ParseError("Set can’t have more than one before.")); } - before = Some(child.text()); + set.before = Some(child.text()); } else if child.is("count", ns::RSM) { - if count.is_some() { + if set.count.is_some() { return Err(Error::ParseError("Set can’t have more than one count.")); } - count = Some(child.text().parse()?); + set.count = Some(child.text().parse()?); } else if child.is("first", ns::RSM) { - if first.is_some() { + if set.first.is_some() { return Err(Error::ParseError("Set can’t have more than one first.")); } - first = Some(First { - index: match child.attr("index") { - Some(index) => Some(index.parse()?), - None => None, - }, - id: child.text(), - }); + set.first_index = match child.attr("index") { + Some(index) => Some(index.parse()?), + None => None, + }; + set.first = Some(child.text()); } else if child.is("index", ns::RSM) { - if index.is_some() { + if set.index.is_some() { return Err(Error::ParseError("Set can’t have more than one index.")); } - index = Some(child.text().parse()?); + set.index = Some(child.text().parse()?); } else if child.is("last", ns::RSM) { - if last.is_some() { + if set.last.is_some() { return Err(Error::ParseError("Set can’t have more than one last.")); } - last = Some(child.text()); + set.last = Some(child.text()); } else if child.is("max", ns::RSM) { - if max.is_some() { + if set.max.is_some() { return Err(Error::ParseError("Set can’t have more than one max.")); } - max = Some(child.text().parse()?); + set.max = Some(child.text().parse()?); } else { return Err(Error::ParseError("Unknown child in set element.")); } } - Ok(Set { - after: after, - before: before, - count: count, - first: first, - index: index, - last: last, - max: max, - }) + Ok(set) } } @@ -116,14 +104,10 @@ impl<'a> Into for &'a Set { elem.append_child(Element::builder("count").ns(ns::RSM).append(format!("{}", self.count.unwrap())).build()); } if self.first.is_some() { - let first = self.first.clone().unwrap(); elem.append_child(Element::builder("first") .ns(ns::RSM) - .attr("index", match first.index { - Some(index) => Some(format!("{}", index)), - None => None, - }) - .append(first.id.clone()).build()); + .attr("index", self.first_index.map(|index| format!("{}", index))) + .append(self.first.clone()).build()); } if self.index.is_some() { elem.append_child(Element::builder("index").ns(ns::RSM).append(format!("{}", self.index.unwrap())).build()); @@ -188,6 +172,7 @@ mod tests { before: None, count: None, first: None, + first_index: None, index: None, last: None, max: None, From 16899f8c23834df950f8777c5b921309ff16db3b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 15:41:16 +0100 Subject: [PATCH 179/698] rsm: Add a test for . --- src/rsm.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/rsm.rs b/src/rsm.rs index a6aec04de438fd4733401960502986d418f83016..7e139feb5788e03ecc08427f400b1a707e1c1f2f 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -180,4 +180,25 @@ mod tests { let elem2 = (&rsm).into(); assert_eq!(elem, elem2); } + + #[test] + fn test_first_index() { + let elem: Element = "coucou".parse().unwrap(); + let set = Set::try_from(&elem).unwrap(); + assert_eq!(set.first, Some(String::from("coucou"))); + assert_eq!(set.first_index, Some(4)); + + let set2 = Set { + after: None, + before: None, + count: None, + first: Some(String::from("coucou")), + first_index: Some(4), + index: None, + last: None, + max: None, + }; + let elem2 = (&set2).into(); + assert_eq!(elem, elem2); + } } From 3c083709cbd1bb2f6854ffd2190322b9643390b7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 16:03:17 +0100 Subject: [PATCH 180/698] rsm, ibb: Write and use a macro to parse attributes. --- src/ibb.rs | 33 ++++++++++++--------------------- src/lib.rs | 21 +++++++++++++++++++++ src/rsm.rs | 5 +---- 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/ibb.rs b/src/ibb.rs index 0156cfee764230ed366373184c996b23bb319f7c..4d11ec11be37d592a39dcfc4187227f924ba0aec 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -65,12 +65,6 @@ pub enum IBB { }, } -fn required_attr(elem: &Element, attr: &str, err: Error) -> Result { - elem.attr(attr) - .and_then(|value| value.parse().ok()) - .ok_or(err) -} - impl<'a> TryFrom<&'a Element> for IBB { type Error = Error; @@ -79,12 +73,9 @@ impl<'a> TryFrom<&'a Element> for IBB { for _ in elem.children() { return Err(Error::ParseError("Unknown child in open element.")); } - let block_size = required_attr(elem, "block-size", Error::ParseError("Required attribute 'block-size' missing in open element."))?; - let sid = required_attr(elem, "sid", Error::ParseError("Required attribute 'sid' missing in open element."))?; - let stanza = match elem.attr("stanza") { - Some(stanza) => stanza.parse()?, - None => Default::default(), - }; + let block_size = get_attr!(elem, "block-size", required, block_size, block_size.parse()?); + let sid = get_attr!(elem, "sid", required, sid, sid.parse()?); + let stanza = get_attr!(elem, "stanza", default, stanza, stanza.parse()?); Ok(IBB::Open { block_size: block_size, sid: sid, @@ -94,8 +85,8 @@ impl<'a> TryFrom<&'a Element> for IBB { for _ in elem.children() { return Err(Error::ParseError("Unknown child in data element.")); } - let seq = required_attr(elem, "seq", Error::ParseError("Required attribute 'seq' missing in data element."))?; - let sid = required_attr(elem, "sid", Error::ParseError("Required attribute 'sid' missing in data element."))?; + let seq = get_attr!(elem, "seq", required, seq, seq.parse()?); + let sid = get_attr!(elem, "sid", required, sid, sid.parse()?); let data = base64::decode(&elem.text())?; Ok(IBB::Data { seq: seq, @@ -103,10 +94,10 @@ impl<'a> TryFrom<&'a Element> for IBB { data: data }) } else if elem.is("close", ns::IBB) { - let sid = required_attr(elem, "sid", Error::ParseError("Required attribute 'sid' missing in data element."))?; for _ in elem.children() { return Err(Error::ParseError("Unknown child in close element.")); } + let sid = get_attr!(elem, "sid", required, sid, sid.parse()?); Ok(IBB::Close { sid: sid, }) @@ -148,6 +139,7 @@ impl<'a> Into for &'a IBB { #[cfg(test)] mod tests { use super::*; + use std::error::Error as StdError; #[test] fn test_simple() { @@ -191,24 +183,23 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Required attribute 'block-size' missing in open element."); + assert_eq!(message, "Required attribute 'block-size' missing."); - // TODO: maybe make a better error message here. let elem: Element = "".parse().unwrap(); let error = IBB::try_from(&elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + Error::ParseIntError(error) => error, _ => panic!(), }; - assert_eq!(message, "Required attribute 'block-size' missing in open element."); + assert_eq!(message.description(), "invalid digit found in string"); let elem: Element = "".parse().unwrap(); let error = IBB::try_from(&elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + Error::ParseError(error) => error, _ => panic!(), }; - assert_eq!(message, "Required attribute 'sid' missing in open element."); + assert_eq!(message, "Required attribute 'sid' missing."); } #[test] diff --git a/src/lib.rs b/src/lib.rs index 7faf06c6c8f0e672d8a9715668d1ffe96e0ba0bf..248902521a9f92d0029458261a8c93fde03e6228 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,6 +22,27 @@ extern crate sha2; extern crate sha3; extern crate blake2; +macro_rules! get_attr { + ($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => ( + match $elem.attr($attr) { + Some($value) => Some($func), + None => None, + } + ); + ($elem:ident, $attr:tt, required, $value:ident, $func:expr) => ( + match $elem.attr($attr) { + Some($value) => $func, + None => return Err(Error::ParseError(concat!("Required attribute '", $attr, "' missing."))), + } + ); + ($elem:ident, $attr:tt, default, $value:ident, $func:expr) => ( + match $elem.attr($attr) { + Some($value) => $func, + None => Default::default(), + } + ); +} + /// Error type returned by every parser on failure. pub mod error; /// XML namespace definitions used through XMPP. diff --git a/src/rsm.rs b/src/rsm.rs index 7e139feb5788e03ecc08427f400b1a707e1c1f2f..99f18930de9d6603808d60a0b9d065d0190f4336 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -61,10 +61,7 @@ impl<'a> TryFrom<&'a Element> for Set { if set.first.is_some() { return Err(Error::ParseError("Set can’t have more than one first.")); } - set.first_index = match child.attr("index") { - Some(index) => Some(index.parse()?), - None => None, - }; + set.first_index = get_attr!(child, "index", optional, index, index.parse()?); set.first = Some(child.text()); } else if child.is("index", ns::RSM) { if set.index.is_some() { From a4f50f2d43d0e9a2168b0d6bcb2b8eb51d84e525 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 16:08:25 +0100 Subject: [PATCH 181/698] jingle_ibb, ibb, rsm: Simplify attribute parsing. --- src/ibb.rs | 12 ++++++------ src/jingle_ibb.rs | 36 ++++++++++++++++-------------------- src/lib.rs | 3 +++ src/rsm.rs | 2 +- 4 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/ibb.rs b/src/ibb.rs index 4d11ec11be37d592a39dcfc4187227f924ba0aec..ffdb907e15fcabdd4ffba3410c270d217c4c5db6 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -73,9 +73,9 @@ impl<'a> TryFrom<&'a Element> for IBB { for _ in elem.children() { return Err(Error::ParseError("Unknown child in open element.")); } - let block_size = get_attr!(elem, "block-size", required, block_size, block_size.parse()?); - let sid = get_attr!(elem, "sid", required, sid, sid.parse()?); - let stanza = get_attr!(elem, "stanza", default, stanza, stanza.parse()?); + let block_size = get_attr!(elem, "block-size", required); + let sid = get_attr!(elem, "sid", required); + let stanza = get_attr!(elem, "stanza", default); Ok(IBB::Open { block_size: block_size, sid: sid, @@ -85,8 +85,8 @@ impl<'a> TryFrom<&'a Element> for IBB { for _ in elem.children() { return Err(Error::ParseError("Unknown child in data element.")); } - let seq = get_attr!(elem, "seq", required, seq, seq.parse()?); - let sid = get_attr!(elem, "sid", required, sid, sid.parse()?); + let seq = get_attr!(elem, "seq", required); + let sid = get_attr!(elem, "sid", required); let data = base64::decode(&elem.text())?; Ok(IBB::Data { seq: seq, @@ -97,7 +97,7 @@ impl<'a> TryFrom<&'a Element> for IBB { for _ in elem.children() { return Err(Error::ParseError("Unknown child in close element.")); } - let sid = get_attr!(elem, "sid", required, sid, sid.parse()?); + let sid = get_attr!(elem, "sid", required); Ok(IBB::Close { sid: sid, }) diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 4ac847cc70d3d8534a07defe16e8cfb80e207207..73544c26666b6eb2efbff481b42c24a741ca0411 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -5,7 +5,6 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use std::convert::TryFrom; -use std::str::FromStr; use minidom::Element; @@ -22,15 +21,6 @@ pub struct Transport { pub stanza: Stanza, } -fn optional_attr(root: &Element, attr: &str) -> Option { - root.attr(attr) - .and_then(|value| value.parse().ok()) -} - -fn required_attr(root: &Element, attr: &str, err: Error) -> Result { - optional_attr(root, attr).ok_or(err) -} - impl<'a> TryFrom<&'a Element> for Transport { type Error = Error; @@ -39,11 +29,9 @@ impl<'a> TryFrom<&'a Element> for Transport { for _ in elem.children() { return Err(Error::ParseError("Unknown child in JingleIBB element.")); } - let block_size = required_attr(elem, "block-size", Error::ParseError("Required attribute 'block-size' missing in JingleIBB element."))?; - let sid = required_attr(elem, "sid", Error::ParseError("Required attribute 'sid' missing in JingleIBB element."))?; - let stanza = elem.attr("stanza") - .unwrap_or("iq") - .parse()?; + let block_size = get_attr!(elem, "block-size", required); + let sid = get_attr!(elem, "sid", required); + let stanza = get_attr!(elem, "stanza", default); Ok(Transport { block_size: block_size, sid: sid, @@ -69,6 +57,7 @@ impl<'a> Into for &'a Transport { #[cfg(test)] mod tests { use super::*; + use std::error::Error as StdError; #[test] fn test_simple() { @@ -87,16 +76,23 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Required attribute 'block-size' missing in JingleIBB element."); + assert_eq!(message, "Required attribute 'block-size' missing."); + + let elem: Element = "".parse().unwrap(); + let error = Transport::try_from(&elem).unwrap_err(); + let message = match error { + Error::ParseIntError(error) => error, + _ => panic!(), + }; + assert_eq!(message.description(), "number too large to fit in target type"); - // TODO: maybe make a better error message here. let elem: Element = "".parse().unwrap(); let error = Transport::try_from(&elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + Error::ParseIntError(error) => error, _ => panic!(), }; - assert_eq!(message, "Required attribute 'block-size' missing in JingleIBB element."); + assert_eq!(message.description(), "invalid digit found in string"); let elem: Element = "".parse().unwrap(); let error = Transport::try_from(&elem).unwrap_err(); @@ -104,7 +100,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Required attribute 'sid' missing in JingleIBB element."); + assert_eq!(message, "Required attribute 'sid' missing."); } #[test] diff --git a/src/lib.rs b/src/lib.rs index 248902521a9f92d0029458261a8c93fde03e6228..db9b95aa1dc0b6d89caeb1dc1927b73ed4472196 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,6 +23,9 @@ extern crate sha3; extern crate blake2; macro_rules! get_attr { + ($elem:ident, $attr:tt, $type:tt) => ( + get_attr!($elem, $attr, $type, value, value.parse()?) + ); ($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => ( match $elem.attr($attr) { Some($value) => Some($func), diff --git a/src/rsm.rs b/src/rsm.rs index 99f18930de9d6603808d60a0b9d065d0190f4336..a697b3705f3fe6a19fa345b4acfc0101a6bb6ae1 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -61,7 +61,7 @@ impl<'a> TryFrom<&'a Element> for Set { if set.first.is_some() { return Err(Error::ParseError("Set can’t have more than one first.")); } - set.first_index = get_attr!(child, "index", optional, index, index.parse()?); + set.first_index = get_attr!(child, "index", optional); set.first = Some(child.text()); } else if child.is("index", ns::RSM) { if set.index.is_some() { From 814a3bd882114ac8490e94c9d5be3958e19a931d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 16:41:29 +0100 Subject: [PATCH 182/698] data_forms: Add support for required, options, title and instructions. --- src/data_forms.rs | 138 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 107 insertions(+), 31 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index a83ebb824b608dc507989048630f8bd4d06e2a9a..bb443c9baf7ffec886f9a8f8413dbab349b03a3a 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -14,11 +14,19 @@ use ns; use media_element::MediaElement; +#[derive(Debug, Clone)] +pub struct Option_ { + pub label: Option, + pub value: String, +} + #[derive(Debug, Clone)] pub struct Field { pub var: String, pub type_: String, // TODO: use an enum here. pub label: Option, + pub required: bool, + pub options: Vec, pub values: Vec, pub media: Vec, } @@ -50,6 +58,8 @@ impl FromStr for DataFormType { pub struct DataForm { pub type_: DataFormType, pub form_type: Option, + pub title: Option, + pub instructions: Option, pub fields: Vec, } @@ -60,53 +70,119 @@ impl<'a> TryFrom<&'a Element> for DataForm { if !elem.is("x", ns::DATA_FORMS) { return Err(Error::ParseError("This is not a data form element.")); } - - let type_: DataFormType = match elem.attr("type") { - Some(type_) => type_.parse()?, - None => return Err(Error::ParseError("Type attribute on data form is mandatory.")), + let type_ = get_attr!(elem, "type", required); + let mut form = DataForm { + type_: type_, + form_type: None, + title: None, + instructions: None, + fields: vec!(), }; - let mut fields = vec!(); - let mut form_type = None; - for field in elem.children() { - if field.is("field", ns::DATA_FORMS) { - let var = field.attr("var").ok_or(Error::ParseError("Field must have a 'var' attribute."))?; - let field_type = field.attr("type").unwrap_or("text-single"); - let label = field.attr("label").and_then(|label| label.parse().ok()); - let mut values = vec!(); - let mut media = vec!(); - for element in field.children() { + for child in elem.children() { + if child.is("title", ns::DATA_FORMS) { + if form.title.is_some() { + return Err(Error::ParseError("More than one title in form element.")); + } + for _ in child.children() { + return Err(Error::ParseError("Title element must not have any child.")); + } + for _ in child.attrs() { + return Err(Error::ParseError("Title element must not have any attribute.")); + } + form.title = Some(child.text()); + } else if child.is("instructions", ns::DATA_FORMS) { + if form.instructions.is_some() { + return Err(Error::ParseError("More than one instructions in form element.")); + } + for _ in child.children() { + return Err(Error::ParseError("instructions element must not have any child.")); + } + for _ in child.attrs() { + return Err(Error::ParseError("instructions element must not have any attribute.")); + } + form.instructions = Some(child.text()); + } else if child.is("field", ns::DATA_FORMS) { + let var: String = get_attr!(child, "var", required); + // TODO: use Default instead. + let field_type: String = get_attr!(child, "type", optional).unwrap_or(String::from("text-single")); + let label = get_attr!(child, "label", optional); + + let is_form_type = var == "FORM_TYPE" && field_type == "hidden"; + let is_list = field_type == "list-single" || field_type == "list-multi"; + let mut field = Field { + var: var, + type_: field_type, + label: label, + required: false, + options: vec!(), + values: vec!(), + media: vec!(), + }; + for element in child.children() { if element.is("value", ns::DATA_FORMS) { - values.push(element.text()); + for _ in element.children() { + return Err(Error::ParseError("Value element must not have any child.")); + } + for _ in element.attrs() { + return Err(Error::ParseError("Value element must not have any attribute.")); + } + field.values.push(element.text()); + } else if element.is("required", ns::DATA_FORMS) { + if field.required { + return Err(Error::ParseError("More than one required element.")); + } + for _ in element.children() { + return Err(Error::ParseError("Required element must not have any child.")); + } + for _ in element.attrs() { + return Err(Error::ParseError("Required element must not have any attribute.")); + } + field.required = true; + } else if element.is("option", ns::DATA_FORMS) { + if !is_list { + return Err(Error::ParseError("Option element found in non-list field.")); + } + let label = get_attr!(element, "label", optional); + let mut value = None; + for child2 in element.children() { + if child2.is("value", ns::DATA_FORMS) { + if value.is_some() { + return Err(Error::ParseError("More than one value element in option element")); + } + value = Some(child2.text()); + } else { + return Err(Error::ParseError("Non-value element in option element")); + } + } + let value = value.ok_or(Error::ParseError("No value element in option element"))?; + field.options.push(Option_ { + label: label, + value: value, + }); } else if element.is("media", ns::MEDIA_ELEMENT) { match MediaElement::try_from(element) { - Ok(media_element) => media.push(media_element), + Ok(media_element) => field.media.push(media_element), Err(_) => (), // TODO: is it really nice to swallow this error? } } else { return Err(Error::ParseError("Field child isn’t a value or media element.")); } } - if var == "FORM_TYPE" && field_type == "hidden" { - if form_type != None { + if is_form_type { + if form.form_type.is_some() { return Err(Error::ParseError("More than one FORM_TYPE in a data form.")); } - if values.len() != 1 { + if field.values.len() != 1 { return Err(Error::ParseError("Wrong number of values in FORM_TYPE.")); } - form_type = Some(values[0].clone()); + form.form_type = Some(field.values[0].clone()); } - fields.push(Field { - var: var.to_owned(), - type_: field_type.to_owned(), - label: label, - values: values, - media: media, - }); + form.fields.push(field); } else { - return Err(Error::ParseError("Unknown field type in data form.")); + return Err(Error::ParseError("Unknown child in data form element.")); } } - Ok(DataForm { type_: type_, form_type: form_type, fields: fields }) + Ok(form) } } @@ -131,7 +207,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Type attribute on data form is mandatory."); + assert_eq!(message, "Required attribute 'type' missing."); let elem: Element = "".parse().unwrap(); let error = DataForm::try_from(&elem).unwrap_err(); @@ -150,6 +226,6 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown field type in data form."); + assert_eq!(message, "Unknown child in data form element."); } } From 80695edb7257c8d1ff75568b43c3285075c8018b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 16:44:35 +0100 Subject: [PATCH 183/698] chatstates, ping, presence: Check for extraneous attributes. --- Cargo.toml | 2 +- src/chatstates.rs | 4 +++- src/ping.rs | 4 +++- src/presence.rs | 17 +++++++++++++---- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 85e089c9c0e3a6c605c68ba018efda29001f7204..59cafb26c08b2a036504c8a183518a03f52cf535 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ categories = ["parsing", "network-programming"] license = "MPL-2.0" [dependencies] -minidom = "0.3.0" +minidom = "0.3.1" jid = "0.2.0" base64 = "0.5.0" digest = "0.5.0" diff --git a/src/chatstates.rs b/src/chatstates.rs index 9ed2b10c1a2b804e2ce402ae5955fa247a6c3822..f9fdb4f3d26e47dfd853a18ff4f22a1fd9b22e59 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -28,6 +28,9 @@ impl<'a> TryFrom<&'a Element> for ChatState { for _ in elem.children() { return Err(Error::ParseError("Unknown child in chatstate element.")); } + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown attribute in chatstate element.")); + } if elem.is("active", ns::CHATSTATES) { Ok(ChatState::Active) } else if elem.is("composing", ns::CHATSTATES) { @@ -90,7 +93,6 @@ mod tests { } #[test] - #[ignore] fn test_invalid_attribute() { let elem: Element = "".parse().unwrap(); let error = ChatState::try_from(&elem).unwrap_err(); diff --git a/src/ping.rs b/src/ping.rs index 4fe2fd86792ffa9f877ccedb8a7eca6d68dadc53..baebf68b133a9f4ebc07bdf769ece76cd1f360d8 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -26,6 +26,9 @@ impl<'a> TryFrom<&'a Element> for Ping { for _ in elem.children() { return Err(Error::ParseError("Unknown child in ping element.")); } + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown attribute in ping element.")); + } Ok(Ping) } } @@ -60,7 +63,6 @@ mod tests { } #[test] - #[ignore] fn test_invalid_attribute() { let elem: Element = "".parse().unwrap(); let error = Ping::try_from(&elem).unwrap_err(); diff --git a/src/presence.rs b/src/presence.rs index 7eb53b411b4b2efe63e0bebd2b839f6266b228fc..d780d0044af379d02dbb9863e1be61a8214a54e5 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -180,6 +180,9 @@ impl<'a> TryFrom<&'a Element> for Presence { for _ in elem.children() { return Err(Error::ParseError("Unknown child in show element.")); } + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown attribute in show element.")); + } show = Some(match elem.text().as_ref() { "away" => Show::Away, "chat" => Show::Chat, @@ -192,6 +195,11 @@ impl<'a> TryFrom<&'a Element> for Presence { for _ in elem.children() { return Err(Error::ParseError("Unknown child in status element.")); } + for (attr, _) in elem.attrs() { + if attr != "xml:lang" { + return Err(Error::ParseError("Unknown attribute in status element.")); + } + } let lang = elem.attr("xml:lang").unwrap_or("").to_owned(); if statuses.insert(lang, elem.text()).is_some() { return Err(Error::ParseError("Status element present twice for the same xml:lang.")); @@ -203,6 +211,9 @@ impl<'a> TryFrom<&'a Element> for Presence { for _ in elem.children() { return Err(Error::ParseError("Unknown child in priority element.")); } + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown attribute in priority element.")); + } priority = Some(Priority::from_str(elem.text().as_ref())?); } else { payloads.push(elem.clone()); @@ -379,9 +390,8 @@ mod tests { } #[test] - #[ignore] fn test_invalid_status_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); let error = Presence::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -391,9 +401,8 @@ mod tests { } #[test] - #[ignore] fn test_invalid_attribute() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); let error = Presence::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, From 7cdb46b90a7b2e31790f301e62a082b1bb308296 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 17:08:05 +0100 Subject: [PATCH 184/698] data_forms: Replace field_type String with a proper enum. --- src/data_forms.rs | 50 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index bb443c9baf7ffec886f9a8f8413dbab349b03a3a..6d4e3bc41fa450cf1c833af52e6345dc79bfc18f 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -14,6 +14,47 @@ use ns; use media_element::MediaElement; +#[derive(Debug, Clone, PartialEq)] +pub enum FieldType { + Boolean, + Fixed, + Hidden, + JidMulti, + JidSingle, + ListMulti, + ListSingle, + TextMulti, + TextPrivate, + TextSingle, +} + +impl Default for FieldType { + fn default() -> FieldType { + FieldType::TextSingle + } +} + +impl FromStr for FieldType { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "boolean" => FieldType::Boolean, + "fixed" => FieldType::Fixed, + "hidden" => FieldType::Hidden, + "jid-multi" => FieldType::JidMulti, + "jid-single" => FieldType::JidSingle, + "list-multi" => FieldType::ListMulti, + "list-single" => FieldType::ListSingle, + "text-multi" => FieldType::TextMulti, + "text-private" => FieldType::TextPrivate, + "text-single" => FieldType::TextSingle, + + _ => return Err(Error::ParseError("Invalid 'type' attribute in field element.")), + }) + } +} + #[derive(Debug, Clone)] pub struct Option_ { pub label: Option, @@ -23,7 +64,7 @@ pub struct Option_ { #[derive(Debug, Clone)] pub struct Field { pub var: String, - pub type_: String, // TODO: use an enum here. + pub type_: FieldType, pub label: Option, pub required: bool, pub options: Vec, @@ -103,12 +144,11 @@ impl<'a> TryFrom<&'a Element> for DataForm { form.instructions = Some(child.text()); } else if child.is("field", ns::DATA_FORMS) { let var: String = get_attr!(child, "var", required); - // TODO: use Default instead. - let field_type: String = get_attr!(child, "type", optional).unwrap_or(String::from("text-single")); + let field_type = get_attr!(child, "type", default); let label = get_attr!(child, "label", optional); - let is_form_type = var == "FORM_TYPE" && field_type == "hidden"; - let is_list = field_type == "list-single" || field_type == "list-multi"; + let is_form_type = var == "FORM_TYPE" && field_type == FieldType::Hidden; + let is_list = field_type == FieldType::ListSingle || field_type == FieldType::ListMulti; let mut field = Field { var: var, type_: field_type, From 545ce292824fc89b5d217cce4490add277189f45 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 20:15:39 +0100 Subject: [PATCH 185/698] message: Add forgotten origin-id payload. --- src/message.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/message.rs b/src/message.rs index dae600e8d3a5c0dd1013c8878838da4c7634193b..23e6686ef26990007867952b70fcef839b1066ff 100644 --- a/src/message.rs +++ b/src/message.rs @@ -73,7 +73,8 @@ impl<'a> TryFrom<&'a Element> for MessagePayload { ("result", ns::MAM) => MessagePayload::MamResult(MamResult::try_from(elem)?), // XEP-0359 - ("stanza-id", ns::SID) => MessagePayload::StanzaId(StanzaId::try_from(elem)?), + ("stanza-id", ns::SID) + | ("origin-id", ns::SID) => MessagePayload::StanzaId(StanzaId::try_from(elem)?), // XEP-0380 ("encryption", ns::EME) => MessagePayload::ExplicitMessageEncryption(ExplicitMessageEncryption::try_from(elem)?), From dc530a1912f191ca43874f747b9702ea5d482d1f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 20:22:48 +0100 Subject: [PATCH 186/698] Add an idle parser. --- src/idle.rs | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 ++ src/ns.rs | 3 ++ 3 files changed, 91 insertions(+) create mode 100644 src/idle.rs diff --git a/src/idle.rs b/src/idle.rs new file mode 100644 index 0000000000000000000000000000000000000000..5261788e1c10abcee5417873753d87471a82184c --- /dev/null +++ b/src/idle.rs @@ -0,0 +1,85 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::convert::TryFrom; + +use minidom::Element; + +use error::Error; + +use ns; + +type Date = String; + +#[derive(Debug, Clone)] +pub struct Idle { + pub since: Date, +} + +impl<'a> TryFrom<&'a Element> for Idle { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if !elem.is("idle", ns::IDLE) { + return Err(Error::ParseError("This is not an idle element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in idle element.")); + } + let since = get_attr!(elem, "since", required); + Ok(Idle { since: since }) + } +} + +impl<'a> Into for &'a Idle { + fn into(self) -> Element { + Element::builder("idle") + .ns(ns::IDLE) + .attr("since", self.since.clone()) + .build() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + Idle::try_from(&elem).unwrap(); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = Idle::try_from(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in idle element."); + } + + #[test] + fn test_invalid_id() { + let elem: Element = "".parse().unwrap(); + let error = Idle::try_from(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'since' missing."); + } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let idle = Idle { since: Date::from("2017-05-21T20:19:55+01:00") }; + let elem2 = (&idle).into(); + assert_eq!(elem, elem2); + } +} diff --git a/src/lib.rs b/src/lib.rs index db9b95aa1dc0b6d89caeb1dc1927b73ed4472196..2eb2aecfeff51af28115c326204354775c4efa2b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,6 +114,9 @@ pub mod message_correct; /// XEP-0313: Message Archive Management pub mod mam; +/// XEP-0319: Last User Interaction in Presence +pub mod idle; + /// XEP-0359: Unique and Stable Stanza IDs pub mod stanza_id; diff --git a/src/ns.rs b/src/ns.rs index c98bff5839c560fba551fad366bc942358e8f797..b51c6530ed89f05f19132e761ab4626cc6fd246c 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -77,6 +77,9 @@ pub const MESSAGE_CORRECT: &'static str = "urn:xmpp:message-correct:0"; /// XEP-0313: Message Archive Management pub const MAM: &'static str = "urn:xmpp:mam:2"; +/// XEP-0319: Last User Interaction in Presence +pub const IDLE: &'static str = "urn:xmpp:idle:1"; + /// XEP-0359: Unique and Stable Stanza IDs pub const SID: &'static str = "urn:xmpp:sid:0"; From 34ee6d52dc1eda6937f6dea7f5738926f258e83b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 20:30:42 +0100 Subject: [PATCH 187/698] presence: Wire up idle as a payload. --- src/presence.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/presence.rs b/src/presence.rs index d780d0044af379d02dbb9863e1be61a8214a54e5..fd6547b16d0836ccd0adf95d212500af28109800 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -18,6 +18,7 @@ use ns; use stanza_error::StanzaError; use delay::Delay; +use idle::Idle; use ecaps2::ECaps2; #[derive(Debug, Clone, PartialEq)] @@ -50,6 +51,7 @@ pub type Priority = i8; pub enum PresencePayload { StanzaError(StanzaError), Delay(Delay), + Idle(Idle), ECaps2(ECaps2), Unknown(Element), @@ -65,6 +67,9 @@ impl<'a> TryFrom<&'a Element> for PresencePayload { // XEP-0203 ("delay", ns::DELAY) => PresencePayload::Delay(Delay::try_from(elem)?), + // XEP-0319 + ("idle", ns::IDLE) => PresencePayload::Idle(Idle::try_from(elem)?), + // XEP-0390 ("c", ns::ECAPS2) => PresencePayload::ECaps2(ECaps2::try_from(elem)?), @@ -78,6 +83,7 @@ impl<'a> Into for &'a PresencePayload { match *self { PresencePayload::StanzaError(ref stanza_error) => stanza_error.into(), PresencePayload::Delay(ref delay) => delay.into(), + PresencePayload::Idle(ref idle) => idle.into(), PresencePayload::ECaps2(ref ecaps2) => ecaps2.into(), PresencePayload::Unknown(ref elem) => elem.clone(), From 185bd79c72f7e6c442a2cf1891c1e485d54c1d51 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 20:45:50 +0100 Subject: [PATCH 188/698] mam: Simplify attribute management, and make default mandatory. --- src/mam.rs | 81 ++++++++++++++++++++++++++---------------------------- 1 file changed, 39 insertions(+), 42 deletions(-) diff --git a/src/mam.rs b/src/mam.rs index 640a3c042ae1dc61b675d3c5bb901b65a77fe5ca..b673154bd20685a25c160669ce026546ac8c5074 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -5,8 +5,9 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use std::convert::TryFrom; +use std::str::FromStr; -use minidom::Element; +use minidom::{Element, IntoAttributeValue}; use jid::Jid; use error::Error; @@ -45,9 +46,33 @@ pub enum DefaultPrefs { Roster, } +impl FromStr for DefaultPrefs { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "always" => DefaultPrefs::Always, + "never" => DefaultPrefs::Never, + "roster" => DefaultPrefs::Roster, + + _ => return Err(Error::ParseError("Invalid 'default' attribute.")), + }) + } +} + +impl<'a> IntoAttributeValue for &'a DefaultPrefs { + fn into_attribute_value(self) -> Option { + Some(String::from(match *self { + DefaultPrefs::Always => "always", + DefaultPrefs::Never => "never", + DefaultPrefs::Roster => "roster", + })) + } +} + #[derive(Debug, Clone)] pub struct Prefs { - pub default_: Option, + pub default_: DefaultPrefs, pub always: Vec, pub never: Vec, } @@ -70,14 +95,8 @@ impl<'a> TryFrom<&'a Element> for Query { return Err(Error::ParseError("Unknown child in query element.")); } } - let queryid = match elem.attr("queryid") { - Some(queryid) => Some(queryid.to_owned()), - None => None, - }; - let node = match elem.attr("node") { - Some(node) => Some(node.to_owned()), - None => None, - }; + let queryid = get_attr!(elem, "queryid", optional); + let node = get_attr!(elem, "node", optional); Ok(Query { queryid, node, form, set }) } } @@ -97,18 +116,9 @@ impl<'a> TryFrom<&'a Element> for Result_ { return Err(Error::ParseError("Unknown child in result element.")); } } - let queryid = match elem.attr("queryid") { - Some(queryid) => queryid.to_owned(), - None => return Err(Error::ParseError("No 'queryid' attribute present in result.")), - }; - let id = match elem.attr("id") { - Some(id) => id.to_owned(), - None => return Err(Error::ParseError("No 'id' attribute present in result.")), - }; - if forwarded.is_none() { - return Err(Error::ParseError("Mandatory forwarded element missing in result.")); - } - let forwarded = forwarded.unwrap(); + let forwarded = forwarded.ok_or(Error::ParseError("Mandatory forwarded element missing in result."))?; + let queryid = get_attr!(elem, "queryid", required); + let id = get_attr!(elem, "id", required); Ok(Result_ { queryid, id, @@ -132,14 +142,13 @@ impl<'a> TryFrom<&'a Element> for Fin { return Err(Error::ParseError("Unknown child in fin element.")); } } + let set = set.ok_or(Error::ParseError("Mandatory set element missing in fin."))?; let complete = match elem.attr("complete") { - Some(complete) => complete == "true", + Some(complete) if complete == "true" => true, + Some(complete) if complete == "false" => false, None => false, + Some(_) => return Err(Error::ParseError("Invalid value for 'complete' attribute.")), }; - if set.is_none() { - return Err(Error::ParseError("Mandatory set element missing in fin.")); - } - let set = set.unwrap(); Ok(Fin { complete, set }) } } @@ -172,14 +181,7 @@ impl<'a> TryFrom<&'a Element> for Prefs { return Err(Error::ParseError("Unknown child in prefs element.")); } } - let default_ = match elem.attr("default") { - Some("always") => Some(DefaultPrefs::Always), - Some("never") => Some(DefaultPrefs::Never), - Some("roster") => Some(DefaultPrefs::Roster), - None => None, - - _ => return Err(Error::ParseError("Invalid 'default' attribute present in prefs.")), - }; + let default_ = get_attr!(elem, "default", required); Ok(Prefs { default_, always, never }) } } @@ -228,12 +230,7 @@ impl<'a> Into for &'a Prefs { fn into(self) -> Element { let mut elem = Element::builder("prefs") .ns(ns::MAM) - .attr("default", match self.default_ { - Some(DefaultPrefs::Always) => Some("always"), - Some(DefaultPrefs::Never) => Some("never"), - Some(DefaultPrefs::Roster) => Some("roster"), - None => None, - }) + .attr("default", &self.default_) .build(); if !self.always.is_empty() { let mut always = Element::builder("always") @@ -340,7 +337,7 @@ mod tests { #[test] fn test_prefs_get() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); Prefs::try_from(&elem).unwrap(); let elem: Element = r#" From 8c53d6e415fe420aef4375eef765ceb56774329a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 20:56:04 +0100 Subject: [PATCH 189/698] stanza_id: Use the new get_attr! macro to get attributes. --- src/stanza_id.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/stanza_id.rs b/src/stanza_id.rs index d478b1aae17af0f50f5bceab5febaf3e98689657..2b87890bf338daf2f1f1595614b4d5a6981b6be7 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -35,15 +35,9 @@ impl<'a> TryFrom<&'a Element> for StanzaId { for _ in elem.children() { return Err(Error::ParseError("Unknown child in stanza-id or origin-id element.")); } - let id = match elem.attr("id") { - Some(id) => id.to_owned(), - None => return Err(Error::ParseError("No 'id' attribute present in stanza-id or origin-id.")), - }; + let id = get_attr!(elem, "id", required); Ok(if is_stanza_id { - let by = match elem.attr("by") { - Some(by) => by.parse().unwrap(), - None => return Err(Error::ParseError("No 'by' attribute present in stanza-id.")), - }; + let by = get_attr!(elem, "by", required); StanzaId::StanzaId { id, by } } else { StanzaId::OriginId { id } @@ -115,7 +109,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "No 'id' attribute present in stanza-id or origin-id."); + assert_eq!(message, "Required attribute 'id' missing."); } #[test] @@ -126,7 +120,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "No 'by' attribute present in stanza-id."); + assert_eq!(message, "Required attribute 'by' missing."); } #[test] From ca9a250efd3926921d6e5c7f00e379bf581a0529 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 21:00:34 +0100 Subject: [PATCH 190/698] jingle_s5b: Obtain attributes using the get_attr! macro. --- src/jingle_s5b.rs | 46 +++++++++++----------------------------------- 1 file changed, 11 insertions(+), 35 deletions(-) diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 4c55d76583f9effa538dba86b11b9b405d90f028..cb43e743c9fc0d60a37870fe5d2e4d747208ccff 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -134,15 +134,9 @@ impl<'a> TryFrom<&'a Element> for Transport { fn try_from(elem: &'a Element) -> Result { if elem.is("transport", ns::JINGLE_S5B) { - let sid = elem.attr("sid") - .ok_or(Error::ParseError("Required attribute 'sid' missing in JingleS5B transport element."))? - .parse()?; - let dstaddr = elem.attr("dstaddr") - .and_then(|value| Some(value.to_owned())); - let mode = match elem.attr("mode") { - None => Default::default(), - Some(mode) => mode.parse()?, - }; + let sid = get_attr!(elem, "sid", required); + let dstaddr = get_attr!(elem, "dstaddr", optional); + let mode = get_attr!(elem, "mode", default); let mut payload = None; for child in elem.children() { @@ -152,26 +146,12 @@ impl<'a> TryFrom<&'a Element> for Transport { Some(_) => return Err(Error::ParseError("Non-activated child already present in JingleS5B transport element.")), None => vec!(), }; - let cid = child.attr("cid") - .ok_or(Error::ParseError("Required attribute 'cid' missing in JingleS5B candidate element."))? - .parse()?; - let host = child.attr("host") - .ok_or(Error::ParseError("Required attribute 'host' missing in JingleS5B candidate element."))? - .parse()?; - let jid = child.attr("jid") - .ok_or(Error::ParseError("Required attribute 'jid' missing in JingleS5B candidate element."))? - .parse()?; - let port = match child.attr("port") { - Some(s) => Some(s.parse()?), - None => None, - }; - let priority = child.attr("priority") - .ok_or(Error::ParseError("Required attribute 'priority' missing in JingleS5B candidate element."))? - .parse()?; - let type_ = match child.attr("type") { - Some(s) => s.parse()?, - None => Default::default(), - }; + let cid = get_attr!(child, "cid", required); + let host = get_attr!(child, "host", required); + let jid = get_attr!(child, "jid", required); + let port = get_attr!(child, "port", optional); + let priority = get_attr!(child, "priority", required); + let type_ = get_attr!(child, "type", default); candidates.push(Candidate { cid: cid, host: host, @@ -185,9 +165,7 @@ impl<'a> TryFrom<&'a Element> for Transport { if payload.is_some() { return Err(Error::ParseError("Non-activated child already present in JingleS5B transport element.")); } - let cid = child.attr("cid") - .ok_or(Error::ParseError("Required attribute 'cid' missing in JingleS5B activated element."))? - .parse()?; + let cid = get_attr!(child, "cid", required); TransportPayload::Activated(cid) } else if child.is("candidate-error", ns::JINGLE_S5B) { if payload.is_some() { @@ -198,9 +176,7 @@ impl<'a> TryFrom<&'a Element> for Transport { if payload.is_some() { return Err(Error::ParseError("Non-candidate-used child already present in JingleS5B transport element.")); } - let cid = child.attr("cid") - .ok_or(Error::ParseError("Required attribute 'cid' missing in JingleS5B candidate-used element."))? - .parse()?; + let cid = get_attr!(child, "cid", required); TransportPayload::CandidateUsed(cid) } else if child.is("proxy-error", ns::JINGLE_S5B) { if payload.is_some() { From e7e4284a0dfa59ea649a4c4a6eb31d9070d13aba Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 21:02:06 +0100 Subject: [PATCH 191/698] jingle_ft: Obtain attributes using the get_attr! macro. --- src/jingle_ft.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 9dd69df1026826ab7a2dcc685b4b5f311476fcde..454ce8d4a54d1c026e1fcb356515624638d522fc 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -138,11 +138,8 @@ impl<'a> TryFrom<&'a Element> for Description { if range.is_some() { return Err(Error::ParseError("File must not have more than one range.")); } - let offset = file_payload.attr("offset").unwrap_or("0").parse()?; - let length = match file_payload.attr("length") { - Some(length) => Some(length.parse()?), - None => None, - }; + let offset = get_attr!(file_payload, "offset", default); + let length = get_attr!(file_payload, "length", optional); let mut range_hashes = vec!(); for hash_element in file_payload.children() { if !hash_element.is("hash", ns::HASHES) { From d61d09f5b7d28e3d5ce2cab19a94362ef3e4c83d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 22 May 2017 19:00:04 +0100 Subject: [PATCH 192/698] hashes, presence, message, iq, disco: Use get_attr!. --- src/disco.rs | 36 +++++++++++++++--------------------- src/hashes.rs | 5 +---- src/iq.rs | 16 +++++----------- src/message.rs | 18 ++++++------------ src/presence.rs | 16 +++++----------- 5 files changed, 32 insertions(+), 59 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index 48f4a2d0db93b5e426a877af91a2c06b56dd0031..9610ad7e7daa58aa8f0e4f5262936ac0aac12f43 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -46,43 +46,37 @@ impl<'a> TryFrom<&'a Element> for Disco { let mut features: Vec = vec!(); let mut extensions: Vec = vec!(); - let node = elem.attr("node") - .and_then(|node| node.parse().ok()); + let node = get_attr!(elem, "node", optional); for child in elem.children() { if child.is("feature", ns::DISCO_INFO) { - let feature = child.attr("var") - .ok_or(Error::ParseError("Feature must have a 'var' attribute."))?; + let feature = get_attr!(child, "var", required); features.push(Feature { - var: feature.to_owned(), + var: feature, }); } else if child.is("identity", ns::DISCO_INFO) { - let category = child.attr("category") - .ok_or(Error::ParseError("Identity must have a 'category' attribute."))?; + let category = get_attr!(child, "category", required); if category == "" { return Err(Error::ParseError("Identity must have a non-empty 'category' attribute.")) } - let type_ = child.attr("type") - .ok_or(Error::ParseError("Identity must have a 'type' attribute."))?; + let type_ = get_attr!(child, "type", required); if type_ == "" { return Err(Error::ParseError("Identity must have a non-empty 'type' attribute.")) } - let xml_lang = child.attr("xml:lang").unwrap_or(""); - let name = child.attr("name") - .and_then(|name| name.parse().ok()); + let lang = get_attr!(child, "xml:lang", default); + let name = get_attr!(child, "name", optional); identities.push(Identity { - category: category.to_owned(), - type_: type_.to_owned(), - xml_lang: xml_lang.to_owned(), + category: category, + type_: type_, + xml_lang: lang, name: name, }); } else if child.is("x", ns::DATA_FORMS) { let data_form = DataForm::try_from(child)?; - match data_form.type_ { - DataFormType::Result_ => (), - _ => return Err(Error::ParseError("Data form must have a 'result' type in disco#info.")), + if data_form.type_ != DataFormType::Result_ { + return Err(Error::ParseError("Data form must have a 'result' type in disco#info.")); } match data_form.form_type { Some(_) => extensions.push(data_form), @@ -178,7 +172,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Identity must have a 'category' attribute."); + assert_eq!(message, "Required attribute 'category' missing."); let elem: Element = "".parse().unwrap(); let error = Disco::try_from(&elem).unwrap_err(); @@ -194,7 +188,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Identity must have a 'type' attribute."); + assert_eq!(message, "Required attribute 'type' missing."); let elem: Element = "".parse().unwrap(); let error = Disco::try_from(&elem).unwrap_err(); @@ -213,7 +207,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Feature must have a 'var' attribute."); + assert_eq!(message, "Required attribute 'var' missing."); } #[test] diff --git a/src/hashes.rs b/src/hashes.rs index 403cb0c90eca8989d8ad678a72ddf3b1d0479e28..da16788b9578af631dde3f584f8e38bdc6b97cf5 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -76,10 +76,7 @@ impl<'a> TryFrom<&'a Element> for Hash { for _ in elem.children() { return Err(Error::ParseError("Unknown child in hash element.")); } - let algo = match elem.attr("algo") { - None => Err(Error::ParseError("Mandatory argument 'algo' not present in hash element.")), - Some(text) => Algo::from_str(text), - }?; + let algo = get_attr!(elem, "algo", required); let hash = match elem.text().as_ref() { "" => return Err(Error::ParseError("Hash element shouldn’t be empty.")), text => text.to_owned(), diff --git a/src/iq.rs b/src/iq.rs index d2d4a6899d0848feadcb6194ad104ffa77a2f756..b1767735662b6ef4245166c4557fd925c4d3290d 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -100,16 +100,10 @@ impl<'a> TryFrom<&'a Element> for Iq { if !root.is("iq", ns::JABBER_CLIENT) { return Err(Error::ParseError("This is not an iq element.")); } - let from = root.attr("from") - .and_then(|value| value.parse().ok()); - let to = root.attr("to") - .and_then(|value| value.parse().ok()); - let id = root.attr("id") - .and_then(|value| value.parse().ok()); - let type_ = match root.attr("type") { - Some(type_) => type_, - None => return Err(Error::ParseError("Iq element requires a 'type' attribute.")), - }; + let from = get_attr!(root, "from", optional); + let to = get_attr!(root, "to", optional); + let id = get_attr!(root, "id", optional); + let type_: String = get_attr!(root, "type", required); let mut payload = None; let mut error_payload = None; @@ -218,7 +212,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Iq element requires a 'type' attribute."); + assert_eq!(message, "Required attribute 'type' missing."); } #[test] diff --git a/src/message.rs b/src/message.rs index 23e6686ef26990007867952b70fcef839b1066ff..a309fa015ff4a4490148efd8c4218b85685861c5 100644 --- a/src/message.rs +++ b/src/message.rs @@ -169,16 +169,10 @@ impl<'a> TryFrom<&'a Element> for Message { if !root.is("message", ns::JABBER_CLIENT) { return Err(Error::ParseError("This is not a message element.")); } - let from = root.attr("from") - .and_then(|value| value.parse().ok()); - let to = root.attr("to") - .and_then(|value| value.parse().ok()); - let id = root.attr("id") - .and_then(|value| value.parse().ok()); - let type_ = match root.attr("type") { - Some(type_) => type_.parse()?, - None => Default::default(), - }; + let from = get_attr!(root, "from", optional); + let to = get_attr!(root, "to", optional); + let id = get_attr!(root, "id", optional); + let type_ = get_attr!(root, "type", default); let mut bodies = BTreeMap::new(); let mut subjects = BTreeMap::new(); let mut thread = None; @@ -188,7 +182,7 @@ impl<'a> TryFrom<&'a Element> for Message { for _ in elem.children() { return Err(Error::ParseError("Unknown child in body element.")); } - let lang = elem.attr("xml:lang").unwrap_or("").to_owned(); + let lang = get_attr!(root, "xml:lang", default); if bodies.insert(lang, elem.text()).is_some() { return Err(Error::ParseError("Body element present twice for the same xml:lang.")); } @@ -196,7 +190,7 @@ impl<'a> TryFrom<&'a Element> for Message { for _ in elem.children() { return Err(Error::ParseError("Unknown child in subject element.")); } - let lang = elem.attr("xml:lang").unwrap_or("").to_owned(); + let lang = get_attr!(root, "xml:lang", default); if subjects.insert(lang, elem.text()).is_some() { return Err(Error::ParseError("Subject element present twice for the same xml:lang.")); } diff --git a/src/presence.rs b/src/presence.rs index fd6547b16d0836ccd0adf95d212500af28109800..c15950a90082e339e91bfbb56ce03e1d5904cfbb 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -164,16 +164,10 @@ impl<'a> TryFrom<&'a Element> for Presence { if !root.is("presence", ns::JABBER_CLIENT) { return Err(Error::ParseError("This is not a presence element.")); } - let from = root.attr("from") - .and_then(|value| value.parse().ok()); - let to = root.attr("to") - .and_then(|value| value.parse().ok()); - let id = root.attr("id") - .and_then(|value| value.parse().ok()); - let type_ = match root.attr("type") { - Some(type_) => type_.parse()?, - None => Default::default(), - }; + let from = get_attr!(root, "from", optional); + let to = get_attr!(root, "to", optional); + let id = get_attr!(root, "id", optional); + let type_ = get_attr!(root, "type", default); let mut show = None; let mut statuses = BTreeMap::new(); let mut priority = None; @@ -206,7 +200,7 @@ impl<'a> TryFrom<&'a Element> for Presence { return Err(Error::ParseError("Unknown attribute in status element.")); } } - let lang = elem.attr("xml:lang").unwrap_or("").to_owned(); + let lang = get_attr!(elem, "xml:lang", default); if statuses.insert(lang, elem.text()).is_some() { return Err(Error::ParseError("Status element present twice for the same xml:lang.")); } From e3f1f31718c8c5095d11e264ee31dbe02cbc9e03 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 23 May 2017 01:02:23 +0100 Subject: [PATCH 193/698] delay, eme, stanza_error: Use get_attr!. --- src/delay.rs | 4 ++-- src/eme.rs | 4 ++-- src/message_correct.rs | 9 +++------ src/stanza_error.rs | 11 ++++------- 4 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index a15b61bcac8340fc1320e262975fbef2b16d9383..79424dcb5e4a01e3129e00c4c80555d1718c7046 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -30,8 +30,8 @@ impl<'a> TryFrom<&'a Element> for Delay { for _ in elem.children() { return Err(Error::ParseError("Unknown child in delay element.")); } - let from = elem.attr("from").and_then(|value| value.parse().ok()); - let stamp = elem.attr("stamp").ok_or(Error::ParseError("Mandatory argument 'stamp' not present in delay element."))?.to_owned(); + let from = get_attr!(elem, "from", optional); + let stamp = get_attr!(elem, "stamp", required); let data = match elem.text().as_ref() { "" => None, text => Some(text.to_owned()), diff --git a/src/eme.rs b/src/eme.rs index ad28afc705c643fd61da43f32e181771b13cdad0..217517cef0710c13e89920d15205ba5afe40d4ae 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -28,8 +28,8 @@ impl<'a> TryFrom<&'a Element> for ExplicitMessageEncryption { for _ in elem.children() { return Err(Error::ParseError("Unknown child in encryption element.")); } - let namespace = elem.attr("namespace").ok_or(Error::ParseError("Mandatory argument 'namespace' not present in encryption element."))?.to_owned(); - let name = elem.attr("name").and_then(|value| value.parse().ok()); + let namespace = get_attr!(elem, "namespace", required); + let name = get_attr!(elem, "name", optional); Ok(ExplicitMessageEncryption { namespace: namespace, name: name, diff --git a/src/message_correct.rs b/src/message_correct.rs index ecc555cc5aa23681d4e6c8600795a8aaca234d2a..ab0d3b2b9532a9cecd16bd59bba6e7cb72eb9df9 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -27,11 +27,8 @@ impl<'a> TryFrom<&'a Element> for Replace { for _ in elem.children() { return Err(Error::ParseError("Unknown child in replace element.")); } - let id = match elem.attr("id") { - Some(id) => id.to_owned(), - None => return Err(Error::ParseError("No 'id' attribute present in replace.")), - }; - Ok(Replace { id: id }) + let id = get_attr!(elem, "id", required); + Ok(Replace { id }) } } @@ -73,7 +70,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "No 'id' attribute present in replace."); + assert_eq!(message, "Required attribute 'id' missing."); } #[test] diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 2f509ee21f6edb84cb1252104cafca57a5f8271f..85f21298635c99dbae616a859b423e3b4c4c0e9f 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -158,11 +158,8 @@ impl<'a> TryFrom<&'a Element> for StanzaError { return Err(Error::ParseError("This is not an error element.")); } - let type_ = elem.attr("type") - .ok_or(Error::ParseError("Error must have a 'type' attribute."))? - .parse()?; - let by = elem.attr("by") - .and_then(|by| by.parse().ok()); + let type_ = get_attr!(elem, "type", required); + let by = get_attr!(elem, "by", optional); let mut defined_condition = None; let mut texts = BTreeMap::new(); let mut other = None; @@ -172,7 +169,7 @@ impl<'a> TryFrom<&'a Element> for StanzaError { for _ in child.children() { return Err(Error::ParseError("Unknown element in error text.")); } - let lang = child.attr("xml:lang").unwrap_or("").to_owned(); + let lang = get_attr!(elem, "xml:lang", default); if texts.insert(lang, child.text()).is_some() { return Err(Error::ParseError("Text element present twice for the same xml:lang.")); } @@ -256,7 +253,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Error must have a 'type' attribute."); + assert_eq!(message, "Required attribute 'type' missing."); let elem: Element = "".parse().unwrap(); let error = StanzaError::try_from(&elem).unwrap_err(); From c1e62cf69bb833d477d0fa0b53d4def10ab841c8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 23 May 2017 23:28:56 +0100 Subject: [PATCH 194/698] Cargo.toml: Update minidom, probably nice things in it! --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 59cafb26c08b2a036504c8a183518a03f52cf535..41d4a464fedf3e5b83258b0f9608007f6d2433d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ categories = ["parsing", "network-programming"] license = "MPL-2.0" [dependencies] -minidom = "0.3.1" +minidom = "0.4.1" jid = "0.2.0" base64 = "0.5.0" digest = "0.5.0" From 16e43c0b01c512d25b9d5c56948f487cb3ecd1b6 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 23 May 2017 23:31:33 +0100 Subject: [PATCH 195/698] Convert all of the parsers/serialisers into consuming their parameter. --- src/attention.rs | 14 ++++----- src/chatstates.rs | 18 +++++------ src/data_forms.rs | 14 ++++----- src/delay.rs | 16 +++++----- src/disco.rs | 34 ++++++++++---------- src/ecaps2.rs | 20 ++++++------ src/eme.rs | 16 +++++----- src/forwarding.rs | 20 ++++++------ src/hashes.rs | 14 ++++----- src/ibb.rs | 36 +++++++++++----------- src/idle.rs | 14 ++++----- src/iq.rs | 58 +++++++++++++++++----------------- src/jingle.rs | 66 ++++++++++++++++++--------------------- src/jingle_ft.rs | 46 +++++++++++++-------------- src/jingle_ibb.rs | 18 +++++------ src/jingle_s5b.rs | 22 ++++++------- src/mam.rs | 70 +++++++++++++++++++++--------------------- src/media_element.rs | 28 ++++++++--------- src/message.rs | 63 +++++++++++++++++++------------------ src/message_correct.rs | 14 ++++----- src/ping.rs | 12 ++++---- src/presence.rs | 56 ++++++++++++++++----------------- src/receipts.rs | 26 ++++++++-------- src/rsm.rs | 21 +++++++------ src/stanza_error.rs | 14 ++++----- src/stanza_id.rs | 30 +++++++++--------- 26 files changed, 378 insertions(+), 382 deletions(-) diff --git a/src/attention.rs b/src/attention.rs index 21f8e0b5d29c3411bb3a1c6cc47df8b184308088..b28b5cc300967f03c455814f1008cd06c93c264b 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -15,10 +15,10 @@ use ns; #[derive(Debug, Clone)] pub struct Attention; -impl<'a> TryFrom<&'a Element> for Attention { +impl TryFrom for Attention { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("attention", ns::ATTENTION) { return Err(Error::ParseError("This is not an attention element.")); } @@ -29,7 +29,7 @@ impl<'a> TryFrom<&'a Element> for Attention { } } -impl<'a> Into for &'a Attention { +impl Into for Attention { fn into(self) -> Element { Element::builder("attention") .ns(ns::ATTENTION) @@ -47,13 +47,13 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - Attention::try_from(&elem).unwrap(); + Attention::try_from(elem).unwrap(); } #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = Attention::try_from(&elem).unwrap_err(); + let error = Attention::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -65,9 +65,7 @@ mod tests { fn test_serialise() { let elem: Element = "".parse().unwrap(); let attention = Attention; - let elem2: Element = (&attention).into(); - let elem3: Element = (&attention).into(); + let elem2: Element = attention.into(); assert_eq!(elem, elem2); - assert_eq!(elem2, elem3); } } diff --git a/src/chatstates.rs b/src/chatstates.rs index f9fdb4f3d26e47dfd853a18ff4f22a1fd9b22e59..54f0aefe7e93d343be9c46e8df6fc2d83e4f7f2e 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -21,10 +21,10 @@ pub enum ChatState { Paused, } -impl<'a> TryFrom<&'a Element> for ChatState { +impl TryFrom for ChatState { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { for _ in elem.children() { return Err(Error::ParseError("Unknown child in chatstate element.")); } @@ -47,9 +47,9 @@ impl<'a> TryFrom<&'a Element> for ChatState { } } -impl<'a> Into for &'a ChatState { +impl Into for ChatState { fn into(self) -> Element { - Element::builder(match *self { + Element::builder(match self { ChatState::Active => "active", ChatState::Composing => "composing", ChatState::Gone => "gone", @@ -67,13 +67,13 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - ChatState::try_from(&elem).unwrap(); + ChatState::try_from(elem).unwrap(); } #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); - let error = ChatState::try_from(&elem).unwrap_err(); + let error = ChatState::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -84,7 +84,7 @@ mod tests { #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = ChatState::try_from(&elem).unwrap_err(); + let error = ChatState::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -95,7 +95,7 @@ mod tests { #[test] fn test_invalid_attribute() { let elem: Element = "".parse().unwrap(); - let error = ChatState::try_from(&elem).unwrap_err(); + let error = ChatState::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -106,7 +106,7 @@ mod tests { #[test] fn test_serialise() { let chatstate = ChatState::Active; - let elem: Element = (&chatstate).into(); + let elem: Element = chatstate.into(); assert!(elem.is("active", ns::CHATSTATES)); } } diff --git a/src/data_forms.rs b/src/data_forms.rs index 6d4e3bc41fa450cf1c833af52e6345dc79bfc18f..21d2ab11db6276b488936edaf54dc4fb4501d054 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -104,10 +104,10 @@ pub struct DataForm { pub fields: Vec, } -impl<'a> TryFrom<&'a Element> for DataForm { +impl TryFrom for DataForm { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("x", ns::DATA_FORMS) { return Err(Error::ParseError("This is not a data form element.")); } @@ -200,7 +200,7 @@ impl<'a> TryFrom<&'a Element> for DataForm { value: value, }); } else if element.is("media", ns::MEDIA_ELEMENT) { - match MediaElement::try_from(element) { + match MediaElement::try_from(element.clone()) { Ok(media_element) => field.media.push(media_element), Err(_) => (), // TODO: is it really nice to swallow this error? } @@ -233,7 +233,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let form = DataForm::try_from(&elem).unwrap(); + let form = DataForm::try_from(elem).unwrap(); assert_eq!(form.type_, DataFormType::Result_); assert!(form.form_type.is_none()); assert!(form.fields.is_empty()); @@ -242,7 +242,7 @@ mod tests { #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); - let error = DataForm::try_from(&elem).unwrap_err(); + let error = DataForm::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -250,7 +250,7 @@ mod tests { assert_eq!(message, "Required attribute 'type' missing."); let elem: Element = "".parse().unwrap(); - let error = DataForm::try_from(&elem).unwrap_err(); + let error = DataForm::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -261,7 +261,7 @@ mod tests { #[test] fn test_wrong_child() { let elem: Element = "".parse().unwrap(); - let error = DataForm::try_from(&elem).unwrap_err(); + let error = DataForm::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/delay.rs b/src/delay.rs index 79424dcb5e4a01e3129e00c4c80555d1718c7046..08b406c5c7d04b58bf53eeeb73c1cd63a48b4875 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -20,10 +20,10 @@ pub struct Delay { pub data: Option, } -impl<'a> TryFrom<&'a Element> for Delay { +impl TryFrom for Delay { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("delay", ns::DELAY) { return Err(Error::ParseError("This is not a delay element.")); } @@ -44,7 +44,7 @@ impl<'a> TryFrom<&'a Element> for Delay { } } -impl<'a> Into for &'a Delay { +impl Into for Delay { fn into(self) -> Element { Element::builder("delay") .ns(ns::DELAY) @@ -63,7 +63,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let delay = Delay::try_from(&elem).unwrap(); + let delay = Delay::try_from(elem).unwrap(); assert_eq!(delay.from, Some(Jid::from_str("capulet.com").unwrap())); assert_eq!(delay.stamp, "2002-09-10T23:08:25Z"); assert_eq!(delay.data, None); @@ -72,7 +72,7 @@ mod tests { #[test] fn test_unknown() { let elem: Element = "".parse().unwrap(); - let error = Delay::try_from(&elem).unwrap_err(); + let error = Delay::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -83,7 +83,7 @@ mod tests { #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = Delay::try_from(&elem).unwrap_err(); + let error = Delay::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -99,7 +99,7 @@ mod tests { stamp: "2002-09-10T23:08:25Z".to_owned(), data: None, }; - let elem2 = (&delay).into(); + let elem2 = delay.into(); assert_eq!(elem, elem2); } @@ -111,7 +111,7 @@ mod tests { stamp: "2002-09-10T23:08:25Z".to_owned(), data: Some(String::from("Reason")), }; - let elem2 = (&delay).into(); + let elem2 = delay.into(); assert_eq!(elem, elem2); } } diff --git a/src/disco.rs b/src/disco.rs index 9610ad7e7daa58aa8f0e4f5262936ac0aac12f43..acd2d4872f78411fe40f86b22d18cab1ec7144a9 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -34,10 +34,10 @@ pub struct Disco { pub extensions: Vec, } -impl<'a> TryFrom<&'a Element> for Disco { +impl TryFrom for Disco { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("query", ns::DISCO_INFO) { return Err(Error::ParseError("This is not a disco#info element.")); } @@ -74,7 +74,7 @@ impl<'a> TryFrom<&'a Element> for Disco { name: name, }); } else if child.is("x", ns::DATA_FORMS) { - let data_form = DataForm::try_from(child)?; + let data_form = DataForm::try_from(child.clone())?; if data_form.type_ != DataFormType::Result_ { return Err(Error::ParseError("Data form must have a 'result' type in disco#info.")); } @@ -109,13 +109,13 @@ impl<'a> TryFrom<&'a Element> for Disco { } } -impl<'a> Into for &'a Disco { +impl Into for Disco { fn into(self) -> Element { let mut root = Element::builder("query") .ns(ns::DISCO_INFO) .attr("node", self.node.clone()) .build(); - for identity in &self.identities { + for identity in self.identities { let identity_element = Element::builder("identity") .ns(ns::DISCO_INFO) .attr("category", identity.category.clone()) @@ -125,14 +125,14 @@ impl<'a> Into for &'a Disco { .build(); root.append_child(identity_element); } - for feature in &self.features { + for feature in self.features { let feature_element = Element::builder("feature") .ns(ns::DISCO_INFO) .attr("var", feature.var.clone()) .build(); root.append_child(feature_element); } - for _ in &self.extensions { + for _ in self.extensions { panic!("Not yet implemented!"); } root @@ -146,7 +146,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let query = Disco::try_from(&elem).unwrap(); + let query = Disco::try_from(elem).unwrap(); assert!(query.node.is_none()); assert_eq!(query.identities.len(), 1); assert_eq!(query.features.len(), 1); @@ -156,7 +156,7 @@ mod tests { #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(&elem).unwrap_err(); + let error = Disco::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -167,7 +167,7 @@ mod tests { #[test] fn test_invalid_identity() { let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(&elem).unwrap_err(); + let error = Disco::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -175,7 +175,7 @@ mod tests { assert_eq!(message, "Required attribute 'category' missing."); let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(&elem).unwrap_err(); + let error = Disco::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -183,7 +183,7 @@ mod tests { assert_eq!(message, "Identity must have a non-empty 'category' attribute."); let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(&elem).unwrap_err(); + let error = Disco::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -191,7 +191,7 @@ mod tests { assert_eq!(message, "Required attribute 'type' missing."); let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(&elem).unwrap_err(); + let error = Disco::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -202,7 +202,7 @@ mod tests { #[test] fn test_invalid_feature() { let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(&elem).unwrap_err(); + let error = Disco::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -214,7 +214,7 @@ mod tests { #[ignore] fn test_invalid_result() { let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(&elem).unwrap_err(); + let error = Disco::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -222,7 +222,7 @@ mod tests { assert_eq!(message, "There must be at least one identity in disco#info."); let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(&elem).unwrap_err(); + let error = Disco::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -230,7 +230,7 @@ mod tests { assert_eq!(message, "There must be at least one feature in disco#info."); let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(&elem).unwrap_err(); + let error = Disco::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/ecaps2.rs b/src/ecaps2.rs index b69a6449d5efb0eab126dfd5d1e6bcd567a6df42..ac9df22edc6f3e3478963578b41b689037d4dcaa 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -25,17 +25,17 @@ pub struct ECaps2 { hashes: Vec, } -impl<'a> TryFrom<&'a Element> for ECaps2 { +impl TryFrom for ECaps2 { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("c", ns::ECAPS2) { return Err(Error::ParseError("This is not an ecaps2 element.")); } let mut hashes = vec!(); for child in elem.children() { if child.is("hash", ns::HASHES) { - let hash = Hash::try_from(child)?; + let hash = Hash::try_from(child.clone())?; hashes.push(hash); } else { return Err(Error::ParseError("Unknown child in ecaps2 element.")); @@ -47,12 +47,12 @@ impl<'a> TryFrom<&'a Element> for ECaps2 { } } -impl<'a> Into for &'a ECaps2 { +impl Into for ECaps2 { fn into(self) -> Element { Element::builder("c") .ns(ns::ECAPS2) .append(self.hashes.iter() - .map(|hash| hash.into()) + .map(|hash| hash.clone().into()) .collect::>()) .build() } @@ -175,7 +175,7 @@ mod tests { #[test] fn test_parse() { let elem: Element = "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=".parse().unwrap(); - let ecaps2 = ECaps2::try_from(&elem).unwrap(); + let ecaps2 = ECaps2::try_from(elem).unwrap(); assert_eq!(ecaps2.hashes.len(), 2); assert_eq!(ecaps2.hashes[0].algo, Algo::Sha_256); assert_eq!(ecaps2.hashes[0].hash, "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4="); @@ -186,7 +186,7 @@ mod tests { #[test] fn test_invalid_child() { let elem: Element = "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=".parse().unwrap(); - let error = ECaps2::try_from(&elem).unwrap_err(); + let error = ECaps2::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -197,7 +197,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let disco = Disco::try_from(&elem).unwrap(); + let disco = Disco::try_from(elem).unwrap(); let ecaps2 = ecaps2::compute_disco(&disco); assert_eq!(ecaps2.len(), 54); } @@ -260,7 +260,7 @@ mod tests { 105, 109, 101, 31, 28, 99, 108, 105, 101, 110, 116, 31, 109, 111, 98, 105, 108, 101, 31, 31, 66, 111, 109, 98, 117, 115, 77, 111, 100, 31, 30, 28, 28]; - let disco = Disco::try_from(&elem).unwrap(); + let disco = Disco::try_from(elem).unwrap(); let ecaps2 = ecaps2::compute_disco(&disco); assert_eq!(ecaps2.len(), 0x1d9); assert_eq!(ecaps2, expected); @@ -432,7 +432,7 @@ mod tests { 111, 110, 31, 48, 46, 49, 49, 46, 49, 45, 115, 118, 110, 45, 50, 48, 49, 49, 49, 50, 49, 54, 45, 109, 111, 100, 32, 40, 84, 99, 108, 47, 84, 107, 32, 56, 46,54, 98, 50, 41, 31, 30, 29, 28]; - let disco = Disco::try_from(&elem).unwrap(); + let disco = Disco::try_from(elem).unwrap(); let ecaps2 = ecaps2::compute_disco(&disco); assert_eq!(ecaps2.len(), 0x543); assert_eq!(ecaps2, expected); diff --git a/src/eme.rs b/src/eme.rs index 217517cef0710c13e89920d15205ba5afe40d4ae..edf6cd359ba443022f60d9227ed0e032728116a9 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -18,10 +18,10 @@ pub struct ExplicitMessageEncryption { pub name: Option, } -impl<'a> TryFrom<&'a Element> for ExplicitMessageEncryption { +impl TryFrom for ExplicitMessageEncryption { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("encryption", ns::EME) { return Err(Error::ParseError("This is not an encryption element.")); } @@ -37,7 +37,7 @@ impl<'a> TryFrom<&'a Element> for ExplicitMessageEncryption { } } -impl<'a> Into for &'a ExplicitMessageEncryption { +impl Into for ExplicitMessageEncryption { fn into(self) -> Element { Element::builder("encryption") .ns(ns::EME) @@ -54,12 +54,12 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let encryption = ExplicitMessageEncryption::try_from(&elem).unwrap(); + let encryption = ExplicitMessageEncryption::try_from(elem).unwrap(); assert_eq!(encryption.namespace, "urn:xmpp:otr:0"); assert_eq!(encryption.name, None); let elem: Element = "".parse().unwrap(); - let encryption = ExplicitMessageEncryption::try_from(&elem).unwrap(); + let encryption = ExplicitMessageEncryption::try_from(elem).unwrap(); assert_eq!(encryption.namespace, "some.unknown.mechanism"); assert_eq!(encryption.name, Some(String::from("SuperMechanism"))); } @@ -67,7 +67,7 @@ mod tests { #[test] fn test_unknown() { let elem: Element = "".parse().unwrap(); - let error = ExplicitMessageEncryption::try_from(&elem).unwrap_err(); + let error = ExplicitMessageEncryption::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -78,7 +78,7 @@ mod tests { #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = ExplicitMessageEncryption::try_from(&elem).unwrap_err(); + let error = ExplicitMessageEncryption::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -90,7 +90,7 @@ mod tests { fn test_serialise() { let elem: Element = "".parse().unwrap(); let eme = ExplicitMessageEncryption { namespace: String::from("coucou"), name: None }; - let elem2 = (&eme).into(); + let elem2 = eme.into(); assert_eq!(elem, elem2); } } diff --git a/src/forwarding.rs b/src/forwarding.rs index a2162e06b110209c8b9e8001d0da0518c90255ad..9ad416b8fcf5ea23775bcac763b37235763c46c6 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -22,10 +22,10 @@ pub struct Forwarded { pub stanza: Option, } -impl<'a> TryFrom<&'a Element> for Forwarded { +impl TryFrom for Forwarded { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("forwarded", ns::FORWARD) { return Err(Error::ParseError("This is not a forwarded element.")); } @@ -33,9 +33,9 @@ impl<'a> TryFrom<&'a Element> for Forwarded { let mut stanza = None; for child in elem.children() { if child.is("delay", ns::DELAY) { - delay = Some(Delay::try_from(child)?); + delay = Some(Delay::try_from(child.clone())?); } else if child.is("message", ns::JABBER_CLIENT) { - stanza = Some(Message::try_from(child)?); + stanza = Some(Message::try_from(child.clone())?); // TODO: also handle the five other possibilities. } else { return Err(Error::ParseError("Unknown child in forwarded element.")); @@ -48,12 +48,12 @@ impl<'a> TryFrom<&'a Element> for Forwarded { } } -impl<'a> Into for &'a Forwarded { +impl Into for Forwarded { fn into(self) -> Element { Element::builder("forwarded") .ns(ns::FORWARD) - .append(match self.delay { Some(ref delay) => { let elem: Element = delay.into(); Some(elem) }, None => None }) - .append(match self.stanza { Some(ref stanza) => { let elem: Element = stanza.into(); Some(elem) }, None => None }) + .append(match self.delay { Some(delay) => { let elem: Element = delay.into(); Some(elem) }, None => None }) + .append(match self.stanza { Some(stanza) => { let elem: Element = stanza.into(); Some(elem) }, None => None }) .build() } } @@ -65,13 +65,13 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - Forwarded::try_from(&elem).unwrap(); + Forwarded::try_from(elem).unwrap(); } #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = Forwarded::try_from(&elem).unwrap_err(); + let error = Forwarded::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -83,7 +83,7 @@ mod tests { fn test_serialise() { let elem: Element = "".parse().unwrap(); let forwarded = Forwarded { delay: None, stanza: None }; - let elem2 = (&forwarded).into(); + let elem2 = forwarded.into(); assert_eq!(elem, elem2); } } diff --git a/src/hashes.rs b/src/hashes.rs index da16788b9578af631dde3f584f8e38bdc6b97cf5..af9c3a053c81626161b09b2fe0b011249b9899b4 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -66,10 +66,10 @@ pub struct Hash { pub hash: String, } -impl<'a> TryFrom<&'a Element> for Hash { +impl TryFrom for Hash { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("hash", ns::HASHES) { return Err(Error::ParseError("This is not a hash element.")); } @@ -88,11 +88,11 @@ impl<'a> TryFrom<&'a Element> for Hash { } } -impl<'a> Into for &'a Hash { +impl Into for Hash { fn into(self) -> Element { Element::builder("hash") .ns(ns::HASHES) - .attr("algo", self.algo.clone()) + .attr("algo", self.algo) .append(self.hash.clone()) .build() } @@ -105,7 +105,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=".parse().unwrap(); - let hash = Hash::try_from(&elem).unwrap(); + let hash = Hash::try_from(elem).unwrap(); assert_eq!(hash.algo, Algo::Sha_256); assert_eq!(hash.hash, "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU="); } @@ -113,7 +113,7 @@ mod tests { #[test] fn test_unknown() { let elem: Element = "".parse().unwrap(); - let error = Hash::try_from(&elem).unwrap_err(); + let error = Hash::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -124,7 +124,7 @@ mod tests { #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = Hash::try_from(&elem).unwrap_err(); + let error = Hash::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/ibb.rs b/src/ibb.rs index ffdb907e15fcabdd4ffba3410c270d217c4c5db6..71bf69511aa1bcd4143340dd53b6bff328fad5c9 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -65,10 +65,10 @@ pub enum IBB { }, } -impl<'a> TryFrom<&'a Element> for IBB { +impl TryFrom for IBB { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if elem.is("open", ns::IBB) { for _ in elem.children() { return Err(Error::ParseError("Unknown child in open element.")); @@ -107,29 +107,29 @@ impl<'a> TryFrom<&'a Element> for IBB { } } -impl<'a> Into for &'a IBB { +impl Into for IBB { fn into(self) -> Element { - match *self { - IBB::Open { ref block_size, ref sid, ref stanza } => { + match self { + IBB::Open { block_size, sid, stanza } => { Element::builder("open") .ns(ns::IBB) .attr("block-size", format!("{}", block_size)) - .attr("sid", sid.to_owned()) - .attr("stanza", stanza.to_owned()) + .attr("sid", sid) + .attr("stanza", stanza) .build() }, - IBB::Data { ref seq, ref sid, ref data } => { + IBB::Data { seq, sid, data } => { Element::builder("data") .ns(ns::IBB) .attr("seq", format!("{}", seq)) - .attr("sid", sid.to_owned()) + .attr("sid", sid) .append(base64::encode(&data)) .build() }, - IBB::Close { ref sid } => { + IBB::Close { sid } => { Element::builder("close") .ns(ns::IBB) - .attr("sid", sid.to_owned()) + .attr("sid", sid) .build() }, } @@ -144,7 +144,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let open = IBB::try_from(&elem).unwrap(); + let open = IBB::try_from(elem).unwrap(); match open { IBB::Open { block_size, sid, stanza } => { assert_eq!(block_size, 3); @@ -155,7 +155,7 @@ mod tests { } let elem: Element = "AAAA".parse().unwrap(); - let data = IBB::try_from(&elem).unwrap(); + let data = IBB::try_from(elem).unwrap(); match data { IBB::Data { seq, sid, data } => { assert_eq!(seq, 0); @@ -166,7 +166,7 @@ mod tests { } let elem: Element = "".parse().unwrap(); - let close = IBB::try_from(&elem).unwrap(); + let close = IBB::try_from(elem).unwrap(); match close { IBB::Close { sid } => { assert_eq!(sid, "coucou"); @@ -178,7 +178,7 @@ mod tests { #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); - let error = IBB::try_from(&elem).unwrap_err(); + let error = IBB::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -186,7 +186,7 @@ mod tests { assert_eq!(message, "Required attribute 'block-size' missing."); let elem: Element = "".parse().unwrap(); - let error = IBB::try_from(&elem).unwrap_err(); + let error = IBB::try_from(elem).unwrap_err(); let message = match error { Error::ParseIntError(error) => error, _ => panic!(), @@ -194,7 +194,7 @@ mod tests { assert_eq!(message.description(), "invalid digit found in string"); let elem: Element = "".parse().unwrap(); - let error = IBB::try_from(&elem).unwrap_err(); + let error = IBB::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(error) => error, _ => panic!(), @@ -205,7 +205,7 @@ mod tests { #[test] fn test_invalid_stanza() { let elem: Element = "".parse().unwrap(); - let error = IBB::try_from(&elem).unwrap_err(); + let error = IBB::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/idle.rs b/src/idle.rs index 5261788e1c10abcee5417873753d87471a82184c..2800a0e1854b05c1db54970a8ca982eb21f81da2 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -19,10 +19,10 @@ pub struct Idle { pub since: Date, } -impl<'a> TryFrom<&'a Element> for Idle { +impl TryFrom for Idle { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("idle", ns::IDLE) { return Err(Error::ParseError("This is not an idle element.")); } @@ -34,7 +34,7 @@ impl<'a> TryFrom<&'a Element> for Idle { } } -impl<'a> Into for &'a Idle { +impl Into for Idle { fn into(self) -> Element { Element::builder("idle") .ns(ns::IDLE) @@ -50,13 +50,13 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - Idle::try_from(&elem).unwrap(); + Idle::try_from(elem).unwrap(); } #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = Idle::try_from(&elem).unwrap_err(); + let error = Idle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -67,7 +67,7 @@ mod tests { #[test] fn test_invalid_id() { let elem: Element = "".parse().unwrap(); - let error = Idle::try_from(&elem).unwrap_err(); + let error = Idle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -79,7 +79,7 @@ mod tests { fn test_serialise() { let elem: Element = "".parse().unwrap(); let idle = Idle { since: Date::from("2017-05-21T20:19:55+01:00") }; - let elem2 = (&idle).into(); + let elem2 = idle.into(); assert_eq!(elem, elem2); } } diff --git a/src/iq.rs b/src/iq.rs index b1767735662b6ef4245166c4557fd925c4d3290d..aaf7b027998ce5bf2818645892a9a06c79023b00 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -37,13 +37,13 @@ pub enum IqPayload { Unknown(Element), } -impl<'a> TryFrom<&'a Element> for IqPayload { +impl TryFrom for IqPayload { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { // XEP-0030 - ("query", ns::DISCO_INFO) => IqPayload::Disco(Disco::try_from(elem)?), + ("query", ns::DISCO_INFO) => IqPayload::Disco(Disco::try_from(elem.clone())?), // XEP-0047 ("open", ns::IBB) @@ -93,10 +93,10 @@ pub struct Iq { pub payload: IqType, } -impl<'a> TryFrom<&'a Element> for Iq { +impl TryFrom for Iq { type Error = Error; - fn try_from(root: &'a Element) -> Result { + fn try_from(root: Element) -> Result { if !root.is("iq", ns::JABBER_CLIENT) { return Err(Error::ParseError("This is not an iq element.")); } @@ -116,7 +116,7 @@ impl<'a> TryFrom<&'a Element> for Iq { if error_payload.is_some() { return Err(Error::ParseError("Wrong number of children in iq element.")); } - error_payload = Some(StanzaError::try_from(elem)?); + error_payload = Some(StanzaError::try_from(elem.clone())?); } else if root.children().collect::>().len() != 2 { return Err(Error::ParseError("Wrong number of children in iq element.")); } @@ -162,23 +162,23 @@ impl<'a> TryFrom<&'a Element> for Iq { } } -impl<'a> Into for &'a IqPayload { +impl Into for IqPayload { fn into(self) -> Element { - match *self { - IqPayload::Disco(ref disco) => disco.into(), - IqPayload::IBB(ref ibb) => ibb.into(), - IqPayload::Jingle(ref jingle) => jingle.into(), - IqPayload::Ping(ref ping) => ping.into(), - IqPayload::MamQuery(ref query) => query.into(), - IqPayload::MamFin(ref fin) => fin.into(), - IqPayload::MamPrefs(ref prefs) => prefs.into(), - - IqPayload::Unknown(ref elem) => elem.clone(), + match self { + IqPayload::Disco(disco) => disco.into(), + IqPayload::IBB(ibb) => ibb.into(), + IqPayload::Jingle(jingle) => jingle.into(), + IqPayload::Ping(ping) => ping.into(), + IqPayload::MamQuery(query) => query.into(), + IqPayload::MamFin(fin) => fin.into(), + IqPayload::MamPrefs(prefs) => prefs.into(), + + IqPayload::Unknown(elem) => elem.clone(), } } } -impl<'a> Into for &'a Iq { +impl Into for Iq { fn into(self) -> Element { let mut stanza = Element::builder("iq") .ns(ns::JABBER_CLIENT) @@ -191,7 +191,7 @@ impl<'a> Into for &'a Iq { IqType::Get(elem) | IqType::Set(elem) | IqType::Result(Some(elem)) => elem, - IqType::Error(error) => (&error).into(), + IqType::Error(error) => error.into(), IqType::Result(None) => return stanza, }; stanza.append_child(elem); @@ -207,7 +207,7 @@ mod tests { #[test] fn test_require_type() { let elem: Element = "".parse().unwrap(); - let error = Iq::try_from(&elem).unwrap_err(); + let error = Iq::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -220,7 +220,7 @@ mod tests { let elem: Element = " ".parse().unwrap(); - let iq = Iq::try_from(&elem).unwrap(); + let iq = Iq::try_from(elem).unwrap(); let query: Element = "".parse().unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); @@ -236,7 +236,7 @@ mod tests { let elem: Element = " ".parse().unwrap(); - let iq = Iq::try_from(&elem).unwrap(); + let iq = Iq::try_from(elem).unwrap(); let vcard: Element = "".parse().unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); @@ -250,7 +250,7 @@ mod tests { #[test] fn test_result_empty() { let elem: Element = "".parse().unwrap(); - let iq = Iq::try_from(&elem).unwrap(); + let iq = Iq::try_from(elem).unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); assert_eq!(iq.id, None); @@ -265,7 +265,7 @@ mod tests { let elem: Element = " ".parse().unwrap(); - let iq = Iq::try_from(&elem).unwrap(); + let iq = Iq::try_from(elem).unwrap(); let query: Element = "".parse().unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); @@ -284,7 +284,7 @@ mod tests { ".parse().unwrap(); - let iq = Iq::try_from(&elem).unwrap(); + let iq = Iq::try_from(elem).unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); assert_eq!(iq.id, None); @@ -303,7 +303,7 @@ mod tests { #[test] fn test_children_invalid() { let elem: Element = "".parse().unwrap(); - let error = Iq::try_from(&elem).unwrap_err(); + let error = Iq::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -320,16 +320,16 @@ mod tests { id: None, payload: IqType::Result(None), }; - let elem2 = (&iq2).into(); + let elem2 = iq2.into(); assert_eq!(elem, elem2); } #[test] fn test_disco() { let elem: Element = "".parse().unwrap(); - let iq = Iq::try_from(&elem).unwrap(); + let iq = Iq::try_from(elem).unwrap(); let payload = match iq.payload { - IqType::Get(ref payload) => IqPayload::try_from(payload).unwrap(), + IqType::Get(payload) => IqPayload::try_from(payload).unwrap(), _ => panic!(), }; assert!(match payload { diff --git a/src/jingle.rs b/src/jingle.rs index fb928aee94f0a113f05027a6e8abee5105cd154d..4796ae41bcfa926d4f2b859926677fe9c7bfa420 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -204,9 +204,9 @@ impl FromStr for Reason { } } -impl<'a> Into for &'a Reason { +impl Into for Reason { fn into(self) -> Element { - Element::builder(match *self { + Element::builder(match self { Reason::AlternativeSession => "alternative-session", Reason::Busy => "busy", Reason::Cancel => "cancel", @@ -245,10 +245,10 @@ pub struct Jingle { pub other: Vec, } -impl<'a> TryFrom<&'a Element> for Jingle { +impl TryFrom for Jingle { type Error = Error; - fn try_from(root: &'a Element) -> Result { + fn try_from(root: Element) -> Result { if !root.is("jingle", ns::JINGLE) { return Err(Error::ParseError("This is not a Jingle element.")); } @@ -353,7 +353,7 @@ impl<'a> TryFrom<&'a Element> for Jingle { } } -impl<'a> Into for &'a Content { +impl Into for Content { fn into(self) -> Element { let mut root = Element::builder("content") .ns(ns::JINGLE) @@ -375,7 +375,7 @@ impl<'a> Into for &'a Content { } } -impl<'a> Into for &'a Jingle { +impl Into for Jingle { fn into(self) -> Element { let mut root = Element::builder("jingle") .ns(ns::JINGLE) @@ -384,15 +384,15 @@ impl<'a> Into for &'a Jingle { .attr("responder", self.responder.clone()) .attr("sid", self.sid.clone()) .build(); - for content in self.contents.clone() { - let content_elem = (&content).into(); + for content in self.contents { + let content_elem = content.into(); root.append_child(content_elem); } - if let Some(ref reason) = self.reason { - let reason2: Element = (&reason.reason).into(); + if let Some(reason) = self.reason { + let reason2: Element = reason.reason.into(); let reason_elem = Element::builder("reason") .append(reason2) - .append(reason.text.clone()) + .append(reason.text) .build(); root.append_child(reason_elem); } @@ -400,12 +400,6 @@ impl<'a> Into for &'a Jingle { } } -impl Into for Jingle { - fn into(self) -> Element { - (&self).into() - } -} - #[cfg(test)] mod tests { use super::*; @@ -413,7 +407,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let jingle = Jingle::try_from(&elem).unwrap(); + let jingle = Jingle::try_from(elem).unwrap(); assert_eq!(jingle.action, Action::SessionInitiate); assert_eq!(jingle.sid, "coucou"); } @@ -421,7 +415,7 @@ mod tests { #[test] fn test_invalid_jingle() { let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); + let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -429,7 +423,7 @@ mod tests { assert_eq!(message, "Jingle must have an 'action' attribute."); let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); + let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -437,7 +431,7 @@ mod tests { assert_eq!(message, "Jingle must have a 'sid' attribute."); let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); + let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -448,25 +442,25 @@ mod tests { #[test] fn test_content() { let elem: Element = "".parse().unwrap(); - let jingle = Jingle::try_from(&elem).unwrap(); + let jingle = Jingle::try_from(elem).unwrap(); assert_eq!(jingle.contents[0].creator, Creator::Initiator); assert_eq!(jingle.contents[0].name, "coucou"); assert_eq!(jingle.contents[0].senders, Senders::Both); assert_eq!(jingle.contents[0].disposition, "session"); let elem: Element = "".parse().unwrap(); - let jingle = Jingle::try_from(&elem).unwrap(); + let jingle = Jingle::try_from(elem).unwrap(); assert_eq!(jingle.contents[0].senders, Senders::Both); let elem: Element = "".parse().unwrap(); - let jingle = Jingle::try_from(&elem).unwrap(); + let jingle = Jingle::try_from(elem).unwrap(); assert_eq!(jingle.contents[0].disposition, "early-session"); } #[test] fn test_invalid_content() { let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); + let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -474,7 +468,7 @@ mod tests { assert_eq!(message, "Content must have a 'creator' attribute."); let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); + let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -482,7 +476,7 @@ mod tests { assert_eq!(message, "Content must have a 'name' attribute."); let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); + let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -490,7 +484,7 @@ mod tests { assert_eq!(message, "Unknown creator."); let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); + let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -498,7 +492,7 @@ mod tests { assert_eq!(message, "Unknown senders."); let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); + let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -509,13 +503,13 @@ mod tests { #[test] fn test_reason() { let elem: Element = "".parse().unwrap(); - let jingle = Jingle::try_from(&elem).unwrap(); + let jingle = Jingle::try_from(elem).unwrap(); let reason = jingle.reason.unwrap(); assert_eq!(reason.reason, Reason::Success); assert_eq!(reason.text, None); let elem: Element = "coucou".parse().unwrap(); - let jingle = Jingle::try_from(&elem).unwrap(); + let jingle = Jingle::try_from(elem).unwrap(); let reason = jingle.reason.unwrap(); assert_eq!(reason.reason, Reason::Success); assert_eq!(reason.text, Some(String::from("coucou"))); @@ -524,7 +518,7 @@ mod tests { #[test] fn test_invalid_reason() { let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); + let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -532,7 +526,7 @@ mod tests { assert_eq!(message, "Reason doesn’t contain a valid reason."); let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); + let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -540,7 +534,7 @@ mod tests { assert_eq!(message, "Unknown reason."); let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); + let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -548,7 +542,7 @@ mod tests { assert_eq!(message, "Reason contains a foreign element."); let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); + let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -556,7 +550,7 @@ mod tests { assert_eq!(message, "Jingle must not have more than one reason."); let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); + let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 454ce8d4a54d1c026e1fcb356515624638d522fc..57cf626cf6ce235ea06be2b761dd0e5ca8677ae3 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -31,7 +31,7 @@ impl IntoElements for Range { }) .build(); for hash in self.hashes { - elem.append_child((&hash).into()); + elem.append_child(hash.into()); } emitter.append_child(elem); } @@ -86,10 +86,10 @@ impl IntoElements for Received { } } -impl<'a> TryFrom<&'a Element> for Description { +impl TryFrom for Description { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("description", ns::JINGLE_FT) { return Err(Error::ParseError("This is not a JingleFT description element.")); } @@ -145,7 +145,7 @@ impl<'a> TryFrom<&'a Element> for Description { if !hash_element.is("hash", ns::HASHES) { return Err(Error::ParseError("Unknown element in JingleFT range.")); } - range_hashes.push(Hash::try_from(hash_element)?); + range_hashes.push(Hash::try_from(hash_element.clone())?); } range = Some(Range { offset: offset, @@ -153,7 +153,7 @@ impl<'a> TryFrom<&'a Element> for Description { hashes: range_hashes, }); } else if file_payload.is("hash", ns::HASHES) { - hashes.push(Hash::try_from(file_payload)?); + hashes.push(Hash::try_from(file_payload.clone())?); } else { return Err(Error::ParseError("Unknown element in JingleFT file.")); } @@ -174,57 +174,57 @@ impl<'a> TryFrom<&'a Element> for Description { } } -impl<'a> Into for &'a File { +impl Into for File { fn into(self) -> Element { let mut root = Element::builder("file") .ns(ns::JINGLE_FT) .build(); - if let Some(ref date) = self.date { + if let Some(date) = self.date { root.append_child(Element::builder("date") .ns(ns::JINGLE_FT) - .append(date.clone()) + .append(date) .build()); } - if let Some(ref media_type) = self.media_type { + if let Some(media_type) = self.media_type { root.append_child(Element::builder("media-type") .ns(ns::JINGLE_FT) - .append(media_type.clone()) + .append(media_type) .build()); } - if let Some(ref name) = self.name { + if let Some(name) = self.name { root.append_child(Element::builder("name") .ns(ns::JINGLE_FT) - .append(name.clone()) + .append(name) .build()); } - if let Some(ref desc) = self.desc { + if let Some(desc) = self.desc { root.append_child(Element::builder("desc") .ns(ns::JINGLE_FT) - .append(desc.clone()) + .append(desc) .build()); } - if let Some(ref size) = self.size { + if let Some(size) = self.size { root.append_child(Element::builder("size") .ns(ns::JINGLE_FT) .append(format!("{}", size)) .build()); } - if let Some(ref range) = self.range { + if let Some(range) = self.range { root.append_child(Element::builder("range") .ns(ns::JINGLE_FT) - .append(range.clone()) + .append(range) .build()); } - for hash in self.hashes.clone() { - root.append_child((&hash).into()); + for hash in self.hashes { + root.append_child(hash.into()); } root } } -impl<'a> Into for &'a Description { +impl Into for Description { fn into(self) -> Element { - let file: Element = (&self.file).into(); + let file: Element = self.file.into(); Element::builder("description") .ns(ns::JINGLE_FT) .append(file) @@ -252,7 +252,7 @@ mod tests { "#.parse().unwrap(); - let desc = Description::try_from(&elem).unwrap(); + let desc = Description::try_from(elem).unwrap(); assert_eq!(desc.file.media_type, Some(String::from("text/plain"))); assert_eq!(desc.file.name, Some(String::from("test.txt"))); assert_eq!(desc.file.desc, None); @@ -274,7 +274,7 @@ mod tests { "#.parse().unwrap(); - let desc = Description::try_from(&elem).unwrap(); + let desc = Description::try_from(elem).unwrap(); assert_eq!(desc.file.media_type, None); assert_eq!(desc.file.name, None); assert_eq!(desc.file.desc, None); diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 73544c26666b6eb2efbff481b42c24a741ca0411..d89df221f80ce5a79d810d0ae334c03b999e053e 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -21,10 +21,10 @@ pub struct Transport { pub stanza: Stanza, } -impl<'a> TryFrom<&'a Element> for Transport { +impl TryFrom for Transport { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if elem.is("transport", ns::JINGLE_IBB) { for _ in elem.children() { return Err(Error::ParseError("Unknown child in JingleIBB element.")); @@ -43,7 +43,7 @@ impl<'a> TryFrom<&'a Element> for Transport { } } -impl<'a> Into for &'a Transport { +impl Into for Transport { fn into(self) -> Element { Element::builder("transport") .ns(ns::JINGLE_IBB) @@ -62,7 +62,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let transport = Transport::try_from(&elem).unwrap(); + let transport = Transport::try_from(elem).unwrap(); assert_eq!(transport.block_size, 3); assert_eq!(transport.sid, "coucou"); assert_eq!(transport.stanza, Stanza::Iq); @@ -71,7 +71,7 @@ mod tests { #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); - let error = Transport::try_from(&elem).unwrap_err(); + let error = Transport::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -79,7 +79,7 @@ mod tests { assert_eq!(message, "Required attribute 'block-size' missing."); let elem: Element = "".parse().unwrap(); - let error = Transport::try_from(&elem).unwrap_err(); + let error = Transport::try_from(elem).unwrap_err(); let message = match error { Error::ParseIntError(error) => error, _ => panic!(), @@ -87,7 +87,7 @@ mod tests { assert_eq!(message.description(), "number too large to fit in target type"); let elem: Element = "".parse().unwrap(); - let error = Transport::try_from(&elem).unwrap_err(); + let error = Transport::try_from(elem).unwrap_err(); let message = match error { Error::ParseIntError(error) => error, _ => panic!(), @@ -95,7 +95,7 @@ mod tests { assert_eq!(message.description(), "invalid digit found in string"); let elem: Element = "".parse().unwrap(); - let error = Transport::try_from(&elem).unwrap_err(); + let error = Transport::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -106,7 +106,7 @@ mod tests { #[test] fn test_invalid_stanza() { let elem: Element = "".parse().unwrap(); - let error = Transport::try_from(&elem).unwrap_err(); + let error = Transport::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index cb43e743c9fc0d60a37870fe5d2e4d747208ccff..fa5ee667e6bb3b1b7c1d939c944808ac2f2f0167 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -63,7 +63,7 @@ pub struct Candidate { pub type_: Type, } -impl<'a> Into for &'a Candidate { +impl Into for Candidate { fn into(self) -> Element { Element::builder("candidate") .ns(ns::JINGLE_S5B) @@ -129,10 +129,10 @@ pub struct Transport { pub payload: TransportPayload, } -impl<'a> TryFrom<&'a Element> for Transport { +impl TryFrom for Transport { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if elem.is("transport", ns::JINGLE_S5B) { let sid = get_attr!(elem, "sid", required); let dstaddr = get_attr!(elem, "dstaddr", optional); @@ -200,7 +200,7 @@ impl<'a> TryFrom<&'a Element> for Transport { } } -impl<'a> Into for &'a Transport { +impl Into for Transport { fn into(self) -> Element { Element::builder("transport") .ns(ns::JINGLE_S5B) @@ -208,15 +208,15 @@ impl<'a> Into for &'a Transport { .attr("dstaddr", self.dstaddr.clone()) .attr("mode", self.mode.clone()) .append(match self.payload { - TransportPayload::Candidates(ref candidates) => { + TransportPayload::Candidates(candidates) => { candidates.iter() - .map(|candidate| -> Element { candidate.into() }) + .map(|candidate| -> Element { candidate.clone().into() }) .collect::>() }, - TransportPayload::Activated(ref cid) => { + TransportPayload::Activated(cid) => { vec!(Element::builder("activated") .ns(ns::JINGLE_S5B) - .attr("cid", cid.to_owned()) + .attr("cid", cid) .build()) }, TransportPayload::CandidateError => { @@ -248,7 +248,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let transport = Transport::try_from(&elem).unwrap(); + let transport = Transport::try_from(elem).unwrap(); assert_eq!(transport.sid, "coucou"); assert_eq!(transport.dstaddr, None); assert_eq!(transport.mode, Mode::Tcp); @@ -267,7 +267,7 @@ mod tests { mode: Mode::Tcp, payload: TransportPayload::Activated(String::from("coucou")), }; - let elem2: Element = (&transport).into(); + let elem2: Element = transport.into(); assert_eq!(elem, elem2); } @@ -287,7 +287,7 @@ mod tests { type_: Type::Direct, })), }; - let elem2: Element = (&transport).into(); + let elem2: Element = transport.into(); assert_eq!(elem, elem2); } } diff --git a/src/mam.rs b/src/mam.rs index b673154bd20685a25c160669ce026546ac8c5074..22ec0576fa4f1d45da3eb2fcf1858269a9c8b83a 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -60,9 +60,9 @@ impl FromStr for DefaultPrefs { } } -impl<'a> IntoAttributeValue for &'a DefaultPrefs { +impl IntoAttributeValue for DefaultPrefs { fn into_attribute_value(self) -> Option { - Some(String::from(match *self { + Some(String::from(match self { DefaultPrefs::Always => "always", DefaultPrefs::Never => "never", DefaultPrefs::Roster => "roster", @@ -77,10 +77,10 @@ pub struct Prefs { pub never: Vec, } -impl<'a> TryFrom<&'a Element> for Query { +impl TryFrom for Query { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("query", ns::MAM) { return Err(Error::ParseError("This is not a query element.")); } @@ -88,9 +88,9 @@ impl<'a> TryFrom<&'a Element> for Query { let mut set = None; for child in elem.children() { if child.is("x", ns::DATA_FORMS) { - form = Some(DataForm::try_from(child)?); + form = Some(DataForm::try_from(child.clone())?); } else if child.is("set", ns::RSM) { - set = Some(Set::try_from(child)?); + set = Some(Set::try_from(child.clone())?); } else { return Err(Error::ParseError("Unknown child in query element.")); } @@ -101,17 +101,17 @@ impl<'a> TryFrom<&'a Element> for Query { } } -impl<'a> TryFrom<&'a Element> for Result_ { +impl TryFrom for Result_ { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("result", ns::MAM) { return Err(Error::ParseError("This is not a result element.")); } let mut forwarded = None; for child in elem.children() { if child.is("forwarded", ns::FORWARD) { - forwarded = Some(Forwarded::try_from(child)?); + forwarded = Some(Forwarded::try_from(child.clone())?); } else { return Err(Error::ParseError("Unknown child in result element.")); } @@ -127,17 +127,17 @@ impl<'a> TryFrom<&'a Element> for Result_ { } } -impl<'a> TryFrom<&'a Element> for Fin { +impl TryFrom for Fin { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("fin", ns::MAM) { return Err(Error::ParseError("This is not a fin element.")); } let mut set = None; for child in elem.children() { if child.is("set", ns::RSM) { - set = Some(Set::try_from(child)?); + set = Some(Set::try_from(child.clone())?); } else { return Err(Error::ParseError("Unknown child in fin element.")); } @@ -153,10 +153,10 @@ impl<'a> TryFrom<&'a Element> for Fin { } } -impl<'a> TryFrom<&'a Element> for Prefs { +impl TryFrom for Prefs { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("prefs", ns::MAM) { return Err(Error::ParseError("This is not a prefs element.")); } @@ -186,7 +186,7 @@ impl<'a> TryFrom<&'a Element> for Prefs { } } -impl<'a> Into for &'a Query { +impl Into for Query { fn into(self) -> Element { let mut elem = Element::builder("query") .ns(ns::MAM) @@ -194,43 +194,43 @@ impl<'a> Into for &'a Query { .attr("node", self.node.clone()) .build(); //if let Some(form) = self.form { - // elem.append_child((&form).into()); + // elem.append_child(form.into()); //} - if let Some(ref set) = self.set { + if let Some(set) = self.set { elem.append_child(set.into()); } elem } } -impl<'a> Into for &'a Result_ { +impl Into for Result_ { fn into(self) -> Element { let mut elem = Element::builder("result") .ns(ns::MAM) - .attr("queryid", self.queryid.clone()) - .attr("id", self.id.clone()) + .attr("queryid", self.queryid) + .attr("id", self.id) .build(); - elem.append_child((&self.forwarded).into()); + elem.append_child(self.forwarded.into()); elem } } -impl<'a> Into for &'a Fin { +impl Into for Fin { fn into(self) -> Element { let mut elem = Element::builder("fin") .ns(ns::MAM) .attr("complete", if self.complete { Some("true") } else { None }) .build(); - elem.append_child((&self.set).into()); + elem.append_child(self.set.into()); elem } } -impl<'a> Into for &'a Prefs { +impl Into for Prefs { fn into(self) -> Element { let mut elem = Element::builder("prefs") .ns(ns::MAM) - .attr("default", &self.default_) + .attr("default", self.default_) .build(); if !self.always.is_empty() { let mut always = Element::builder("always") @@ -267,7 +267,7 @@ mod tests { #[test] fn test_query() { let elem: Element = "".parse().unwrap(); - Query::try_from(&elem).unwrap(); + Query::try_from(elem).unwrap(); } #[test] @@ -282,7 +282,7 @@ mod tests { "#.parse().unwrap(); - Result_::try_from(&elem).unwrap(); + Result_::try_from(elem).unwrap(); } #[test] @@ -295,7 +295,7 @@ mod tests { "#.parse().unwrap(); - Fin::try_from(&elem).unwrap(); + Fin::try_from(elem).unwrap(); } #[test] @@ -312,7 +312,7 @@ mod tests { "#.parse().unwrap(); - Query::try_from(&elem).unwrap(); + Query::try_from(elem).unwrap(); } #[test] @@ -332,13 +332,13 @@ mod tests { "#.parse().unwrap(); - Query::try_from(&elem).unwrap(); + Query::try_from(elem).unwrap(); } #[test] fn test_prefs_get() { let elem: Element = "".parse().unwrap(); - Prefs::try_from(&elem).unwrap(); + Prefs::try_from(elem).unwrap(); let elem: Element = r#" @@ -346,7 +346,7 @@ mod tests { "#.parse().unwrap(); - Prefs::try_from(&elem).unwrap(); + Prefs::try_from(elem).unwrap(); } #[test] @@ -361,13 +361,13 @@ mod tests { "#.parse().unwrap(); - Prefs::try_from(&elem).unwrap(); + Prefs::try_from(elem).unwrap(); } #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = Query::try_from(&elem).unwrap_err(); + let error = Query::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -379,7 +379,7 @@ mod tests { fn test_serialise() { let elem: Element = "".parse().unwrap(); let replace = Query { queryid: None, node: None, form: None, set: None }; - let elem2 = (&replace).into(); + let elem2 = replace.into(); assert_eq!(elem, elem2); } } diff --git a/src/media_element.rs b/src/media_element.rs index d4d31985caad71b54afa050ddd0594a30a9650f3..54ffee132eb5a6ee7b20bbd4012e166b6d7d8cb4 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -25,10 +25,10 @@ pub struct MediaElement { pub uris: Vec, } -impl<'a> TryFrom<&'a Element> for MediaElement { +impl TryFrom for MediaElement { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("media", ns::MEDIA_ELEMENT) { return Err(Error::ParseError("This is not a media element.")); } @@ -60,7 +60,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let media = MediaElement::try_from(&elem).unwrap(); + let media = MediaElement::try_from(elem).unwrap(); assert!(media.width.is_none()); assert!(media.height.is_none()); assert!(media.uris.is_empty()); @@ -69,7 +69,7 @@ mod tests { #[test] fn test_width_height() { let elem: Element = "".parse().unwrap(); - let media = MediaElement::try_from(&elem).unwrap(); + let media = MediaElement::try_from(elem).unwrap(); assert_eq!(media.width.unwrap(), 32); assert_eq!(media.height.unwrap(), 32); } @@ -77,7 +77,7 @@ mod tests { #[test] fn test_uri() { let elem: Element = "https://example.org/".parse().unwrap(); - let media = MediaElement::try_from(&elem).unwrap(); + let media = MediaElement::try_from(elem).unwrap(); assert_eq!(media.uris.len(), 1); assert_eq!(media.uris[0].type_, "text/html"); assert_eq!(media.uris[0].uri, "https://example.org/"); @@ -86,26 +86,26 @@ mod tests { #[test] fn test_invalid_width_height() { let elem: Element = "".parse().unwrap(); - let media = MediaElement::try_from(&elem).unwrap(); + let media = MediaElement::try_from(elem).unwrap(); assert!(media.width.is_none()); let elem: Element = "".parse().unwrap(); - let media = MediaElement::try_from(&elem).unwrap(); + let media = MediaElement::try_from(elem).unwrap(); assert!(media.width.is_none()); let elem: Element = "".parse().unwrap(); - let media = MediaElement::try_from(&elem).unwrap(); + let media = MediaElement::try_from(elem).unwrap(); assert!(media.height.is_none()); let elem: Element = "".parse().unwrap(); - let media = MediaElement::try_from(&elem).unwrap(); + let media = MediaElement::try_from(elem).unwrap(); assert!(media.height.is_none()); } #[test] fn test_unknown_child() { let elem: Element = "".parse().unwrap(); - let error = MediaElement::try_from(&elem).unwrap_err(); + let error = MediaElement::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -116,7 +116,7 @@ mod tests { #[test] fn test_bad_uri() { let elem: Element = "https://example.org/".parse().unwrap(); - let error = MediaElement::try_from(&elem).unwrap_err(); + let error = MediaElement::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -124,7 +124,7 @@ mod tests { assert_eq!(message, "Attribute type on uri is mandatory."); let elem: Element = "".parse().unwrap(); - let error = MediaElement::try_from(&elem).unwrap_err(); + let error = MediaElement::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -146,7 +146,7 @@ mod tests { http://victim.example.com/challenges/speech.mp3?F3A6292C "#.parse().unwrap(); - let media = MediaElement::try_from(&elem).unwrap(); + let media = MediaElement::try_from(elem).unwrap(); assert!(media.width.is_none()); assert!(media.height.is_none()); assert_eq!(media.uris.len(), 3); @@ -177,7 +177,7 @@ mod tests { [ ... ] "#.parse().unwrap(); - let form = DataForm::try_from(&elem).unwrap(); + let form = DataForm::try_from(elem).unwrap(); assert_eq!(form.fields.len(), 1); assert_eq!(form.fields[0].var, "ocr"); assert_eq!(form.fields[0].media[0].width, Some(290)); diff --git a/src/message.rs b/src/message.rs index a309fa015ff4a4490148efd8c4218b85685861c5..be47756a5c2d622dafc77936184c9e13e6621d7f 100644 --- a/src/message.rs +++ b/src/message.rs @@ -42,10 +42,10 @@ pub enum MessagePayload { Unknown(Element), } -impl<'a> TryFrom<&'a Element> for MessagePayload { +impl TryFrom for MessagePayload { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { ("error", ns::JABBER_CLIENT) => MessagePayload::StanzaError(StanzaError::try_from(elem)?), @@ -84,20 +84,20 @@ impl<'a> TryFrom<&'a Element> for MessagePayload { } } -impl<'a> Into for &'a MessagePayload { +impl Into for MessagePayload { fn into(self) -> Element { - match *self { - MessagePayload::StanzaError(ref stanza_error) => stanza_error.into(), - MessagePayload::Attention(ref attention) => attention.into(), - MessagePayload::ChatState(ref chatstate) => chatstate.into(), - MessagePayload::Receipt(ref receipt) => receipt.into(), - MessagePayload::Delay(ref delay) => delay.into(), - MessagePayload::MessageCorrect(ref replace) => replace.into(), - MessagePayload::ExplicitMessageEncryption(ref eme) => eme.into(), - MessagePayload::StanzaId(ref stanza_id) => stanza_id.into(), - MessagePayload::MamResult(ref result) => result.into(), - - MessagePayload::Unknown(ref elem) => elem.clone(), + match self { + MessagePayload::StanzaError(stanza_error) => stanza_error.into(), + MessagePayload::Attention(attention) => attention.into(), + MessagePayload::ChatState(chatstate) => chatstate.into(), + MessagePayload::Receipt(receipt) => receipt.into(), + MessagePayload::Delay(delay) => delay.into(), + MessagePayload::MessageCorrect(replace) => replace.into(), + MessagePayload::ExplicitMessageEncryption(eme) => eme.into(), + MessagePayload::StanzaId(stanza_id) => stanza_id.into(), + MessagePayload::MamResult(result) => result.into(), + + MessagePayload::Unknown(elem) => elem.clone(), } } } @@ -162,10 +162,10 @@ pub struct Message { pub payloads: Vec, } -impl<'a> TryFrom<&'a Element> for Message { +impl TryFrom for Message { type Error = Error; - fn try_from(root: &'a Element) -> Result { + fn try_from(root: Element) -> Result { if !root.is("message", ns::JABBER_CLIENT) { return Err(Error::ParseError("This is not a message element.")); } @@ -219,7 +219,7 @@ impl<'a> TryFrom<&'a Element> for Message { } } -impl<'a> Into for &'a Message { +impl Into for Message { fn into(self) -> Element { let mut stanza = Element::builder("message") .ns(ns::JABBER_CLIENT) @@ -264,7 +264,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let message = Message::try_from(&elem).unwrap(); + let message = Message::try_from(elem).unwrap(); assert_eq!(message.from, None); assert_eq!(message.to, None); assert_eq!(message.id, None); @@ -285,18 +285,19 @@ mod tests { thread: None, payloads: vec!(), }; - let elem2 = (&message).into(); + let elem2 = message.into(); assert_eq!(elem, elem2); } #[test] fn test_body() { let elem: Element = "Hello world!".parse().unwrap(); - let message = Message::try_from(&elem).unwrap(); + let elem1 = elem.clone(); + let message = Message::try_from(elem).unwrap(); assert_eq!(message.bodies[""], "Hello world!"); - let elem2 = (&message).into(); - assert_eq!(elem, elem2); + let elem2 = message.into(); + assert_eq!(elem1, elem2); } #[test] @@ -314,25 +315,27 @@ mod tests { thread: None, payloads: vec!(), }; - let elem2 = (&message).into(); + let elem2 = message.into(); assert_eq!(elem, elem2); } #[test] fn test_subject() { let elem: Element = "Hello world!".parse().unwrap(); - let message = Message::try_from(&elem).unwrap(); + let elem1 = elem.clone(); + let message = Message::try_from(elem).unwrap(); assert_eq!(message.subjects[""], "Hello world!"); - let elem2 = (&message).into(); - assert_eq!(elem, elem2); + let elem2 = message.into(); + assert_eq!(elem1, elem2); } #[test] fn test_attention() { let elem: Element = "".parse().unwrap(); - let message = Message::try_from(&elem).unwrap(); - let elem2 = (&message).into(); - assert_eq!(elem, elem2); + let elem1 = elem.clone(); + let message = Message::try_from(elem).unwrap(); + let elem2 = message.into(); + assert_eq!(elem1, elem2); } } diff --git a/src/message_correct.rs b/src/message_correct.rs index ab0d3b2b9532a9cecd16bd59bba6e7cb72eb9df9..35494f810b04c59ad3365049a79338655967307c 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -17,10 +17,10 @@ pub struct Replace { pub id: String, } -impl<'a> TryFrom<&'a Element> for Replace { +impl TryFrom for Replace { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("replace", ns::MESSAGE_CORRECT) { return Err(Error::ParseError("This is not a replace element.")); } @@ -32,7 +32,7 @@ impl<'a> TryFrom<&'a Element> for Replace { } } -impl<'a> Into for &'a Replace { +impl Into for Replace { fn into(self) -> Element { Element::builder("replace") .ns(ns::MESSAGE_CORRECT) @@ -48,13 +48,13 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - Replace::try_from(&elem).unwrap(); + Replace::try_from(elem).unwrap(); } #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = Replace::try_from(&elem).unwrap_err(); + let error = Replace::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -65,7 +65,7 @@ mod tests { #[test] fn test_invalid_id() { let elem: Element = "".parse().unwrap(); - let error = Replace::try_from(&elem).unwrap_err(); + let error = Replace::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -77,7 +77,7 @@ mod tests { fn test_serialise() { let elem: Element = "".parse().unwrap(); let replace = Replace { id: String::from("coucou") }; - let elem2 = (&replace).into(); + let elem2 = replace.into(); assert_eq!(elem, elem2); } } diff --git a/src/ping.rs b/src/ping.rs index baebf68b133a9f4ebc07bdf769ece76cd1f360d8..84a7cebf0d9796abc09046bb29346a617c84b99c 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -16,10 +16,10 @@ use ns; #[derive(Debug, Clone)] pub struct Ping; -impl<'a> TryFrom<&'a Element> for Ping { +impl TryFrom for Ping { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("ping", ns::PING) { return Err(Error::ParseError("This is not a ping element.")); } @@ -33,7 +33,7 @@ impl<'a> TryFrom<&'a Element> for Ping { } } -impl<'a> Into for &'a Ping { +impl Into for Ping { fn into(self) -> Element { Element::builder("ping") .ns(ns::PING) @@ -48,13 +48,13 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - Ping::try_from(&elem).unwrap(); + Ping::try_from(elem).unwrap(); } #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); - let error = Ping::try_from(&elem).unwrap_err(); + let error = Ping::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -65,7 +65,7 @@ mod tests { #[test] fn test_invalid_attribute() { let elem: Element = "".parse().unwrap(); - let error = Ping::try_from(&elem).unwrap_err(); + let error = Ping::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/presence.rs b/src/presence.rs index c15950a90082e339e91bfbb56ce03e1d5904cfbb..9dd1c62ca20708e25ce8bedc72412f064b983be2 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -57,10 +57,10 @@ pub enum PresencePayload { Unknown(Element), } -impl<'a> TryFrom<&'a Element> for PresencePayload { +impl TryFrom for PresencePayload { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { ("error", ns::JABBER_CLIENT) => PresencePayload::StanzaError(StanzaError::try_from(elem)?), @@ -73,20 +73,20 @@ impl<'a> TryFrom<&'a Element> for PresencePayload { // XEP-0390 ("c", ns::ECAPS2) => PresencePayload::ECaps2(ECaps2::try_from(elem)?), - _ => PresencePayload::Unknown(elem.clone()), + _ => PresencePayload::Unknown(elem), }) } } -impl<'a> Into for &'a PresencePayload { +impl Into for PresencePayload { fn into(self) -> Element { - match *self { - PresencePayload::StanzaError(ref stanza_error) => stanza_error.into(), - PresencePayload::Delay(ref delay) => delay.into(), - PresencePayload::Idle(ref idle) => idle.into(), - PresencePayload::ECaps2(ref ecaps2) => ecaps2.into(), + match self { + PresencePayload::StanzaError(stanza_error) => stanza_error.into(), + PresencePayload::Delay(delay) => delay.into(), + PresencePayload::Idle(idle) => idle.into(), + PresencePayload::ECaps2(ecaps2) => ecaps2.into(), - PresencePayload::Unknown(ref elem) => elem.clone(), + PresencePayload::Unknown(elem) => elem.clone(), } } } @@ -157,10 +157,10 @@ pub struct Presence { pub payloads: Vec, } -impl<'a> TryFrom<&'a Element> for Presence { +impl TryFrom for Presence { type Error = Error; - fn try_from(root: &'a Element) -> Result { + fn try_from(root: Element) -> Result { if !root.is("presence", ns::JABBER_CLIENT) { return Err(Error::ParseError("This is not a presence element.")); } @@ -232,7 +232,7 @@ impl<'a> TryFrom<&'a Element> for Presence { } } -impl<'a> Into for &'a Presence { +impl Into for Presence { fn into(self) -> Element { let mut stanza = Element::builder("presence") .ns(ns::JABBER_CLIENT) @@ -267,7 +267,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let presence = Presence::try_from(&elem).unwrap(); + let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.from, None); assert_eq!(presence.to, None); assert_eq!(presence.id, None); @@ -288,14 +288,14 @@ mod tests { priority: 0i8, payloads: vec!(), }; - let elem2 = (&presence).into(); + let elem2 = presence.into(); assert_eq!(elem, elem2); } #[test] fn test_show() { let elem: Element = "chat".parse().unwrap(); - let presence = Presence::try_from(&elem).unwrap(); + let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.show, Some(Show::Chat)); } @@ -304,7 +304,7 @@ mod tests { fn test_missing_show_value() { // "online" used to be a pretty common mistake. let elem: Element = "".parse().unwrap(); - let error = Presence::try_from(&elem).unwrap_err(); + let error = Presence::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -316,7 +316,7 @@ mod tests { fn test_invalid_show() { // "online" used to be a pretty common mistake. let elem: Element = "online".parse().unwrap(); - let error = Presence::try_from(&elem).unwrap_err(); + let error = Presence::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -327,7 +327,7 @@ mod tests { #[test] fn test_empty_status() { let elem: Element = "".parse().unwrap(); - let presence = Presence::try_from(&elem).unwrap(); + let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.statuses.len(), 1); assert_eq!(presence.statuses[""], ""); @@ -336,7 +336,7 @@ mod tests { #[test] fn test_status() { let elem: Element = "Here!".parse().unwrap(); - let presence = Presence::try_from(&elem).unwrap(); + let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.statuses.len(), 1); assert_eq!(presence.statuses[""], "Here!"); @@ -345,7 +345,7 @@ mod tests { #[test] fn test_multiple_statuses() { let elem: Element = "Here!Là!".parse().unwrap(); - let presence = Presence::try_from(&elem).unwrap(); + let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.statuses.len(), 2); assert_eq!(presence.statuses[""], "Here!"); @@ -355,7 +355,7 @@ mod tests { #[test] fn test_invalid_multiple_statuses() { let elem: Element = "Here!Là!".parse().unwrap(); - let error = Presence::try_from(&elem).unwrap_err(); + let error = Presence::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -366,7 +366,7 @@ mod tests { #[test] fn test_priority() { let elem: Element = "-1".parse().unwrap(); - let presence = Presence::try_from(&elem).unwrap(); + let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.priority, -1i8); } @@ -374,7 +374,7 @@ mod tests { #[test] fn test_invalid_priority() { let elem: Element = "128".parse().unwrap(); - let error = Presence::try_from(&elem).unwrap_err(); + let error = Presence::try_from(elem).unwrap_err(); match error { Error::ParseIntError(_) => (), _ => panic!(), @@ -384,7 +384,7 @@ mod tests { #[test] fn test_unknown_child() { let elem: Element = "".parse().unwrap(); - let presence = Presence::try_from(&elem).unwrap(); + let presence = Presence::try_from(elem).unwrap(); let payload = &presence.payloads[0]; assert!(payload.is("test", "invalid")); } @@ -392,7 +392,7 @@ mod tests { #[test] fn test_invalid_status_child() { let elem: Element = "".parse().unwrap(); - let error = Presence::try_from(&elem).unwrap_err(); + let error = Presence::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -403,7 +403,7 @@ mod tests { #[test] fn test_invalid_attribute() { let elem: Element = "".parse().unwrap(); - let error = Presence::try_from(&elem).unwrap_err(); + let error = Presence::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -426,7 +426,7 @@ mod tests { priority: 0i8, payloads: vec!(), }; - let elem: Element = (&presence).into(); + let elem: Element = presence.into(); assert!(elem.is("presence", ns::JABBER_CLIENT)); assert!(elem.children().collect::>()[0].is("status", ns::JABBER_CLIENT)); } diff --git a/src/receipts.rs b/src/receipts.rs index 27392fb8cb161c2f1cea195907754b35d06d876b..f7c1d50d2af1352210c98d6c3317742ef04b7e47 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -18,10 +18,10 @@ pub enum Receipt { Received(String), } -impl<'a> TryFrom<&'a Element> for Receipt { +impl TryFrom for Receipt { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { for _ in elem.children() { return Err(Error::ParseError("Unknown child in receipt element.")); } @@ -36,16 +36,16 @@ impl<'a> TryFrom<&'a Element> for Receipt { } } -impl<'a> Into for &'a Receipt { +impl Into for Receipt { fn into(self) -> Element { - match *self { + match self { Receipt::Request => Element::builder("request") .ns(ns::RECEIPTS) .build(), - Receipt::Received(ref id) => Element::builder("received") - .ns(ns::RECEIPTS) - .attr("id", id.clone()) - .build(), + Receipt::Received(id) => Element::builder("received") + .ns(ns::RECEIPTS) + .attr("id", id) + .build(), } } } @@ -57,23 +57,23 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - Receipt::try_from(&elem).unwrap(); + Receipt::try_from(elem).unwrap(); let elem: Element = "".parse().unwrap(); - Receipt::try_from(&elem).unwrap(); + Receipt::try_from(elem).unwrap(); let elem: Element = "".parse().unwrap(); - Receipt::try_from(&elem).unwrap(); + Receipt::try_from(elem).unwrap(); } #[test] fn test_serialise() { let receipt = Receipt::Request; - let elem: Element = (&receipt).into(); + let elem: Element = receipt.into(); assert!(elem.is("request", ns::RECEIPTS)); let receipt = Receipt::Received("coucou".to_owned()); - let elem: Element = (&receipt).into(); + let elem: Element = receipt.into(); assert!(elem.is("received", ns::RECEIPTS)); assert_eq!(elem.attr("id"), Some("coucou")); } diff --git a/src/rsm.rs b/src/rsm.rs index a697b3705f3fe6a19fa345b4acfc0101a6bb6ae1..7ac80b192b711f1f1828b7aa632a7446423d2ef2 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -24,10 +24,10 @@ pub struct Set { pub max: Option, } -impl<'a> TryFrom<&'a Element> for Set { +impl TryFrom for Set { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("set", ns::RSM) { return Err(Error::ParseError("This is not a RSM element.")); } @@ -86,7 +86,7 @@ impl<'a> TryFrom<&'a Element> for Set { } } -impl<'a> Into for &'a Set { +impl Into for Set { fn into(self) -> Element { let mut elem = Element::builder("set") .ns(ns::RSM) @@ -126,7 +126,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let set = Set::try_from(&elem).unwrap(); + let set = Set::try_from(elem).unwrap(); assert_eq!(set.after, None); assert_eq!(set.before, None); assert_eq!(set.count, None); @@ -142,7 +142,7 @@ mod tests { #[test] fn test_unknown() { let elem: Element = "".parse().unwrap(); - let error = Set::try_from(&elem).unwrap_err(); + let error = Set::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -153,7 +153,7 @@ mod tests { #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = Set::try_from(&elem).unwrap_err(); + let error = Set::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -174,14 +174,15 @@ mod tests { last: None, max: None, }; - let elem2 = (&rsm).into(); + let elem2 = rsm.into(); assert_eq!(elem, elem2); } #[test] fn test_first_index() { let elem: Element = "coucou".parse().unwrap(); - let set = Set::try_from(&elem).unwrap(); + let elem1 = elem.clone(); + let set = Set::try_from(elem).unwrap(); assert_eq!(set.first, Some(String::from("coucou"))); assert_eq!(set.first_index, Some(4)); @@ -195,7 +196,7 @@ mod tests { last: None, max: None, }; - let elem2 = (&set2).into(); - assert_eq!(elem, elem2); + let elem2 = set2.into(); + assert_eq!(elem1, elem2); } } diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 85f21298635c99dbae616a859b423e3b4c4c0e9f..4f30f3a44171529b4066c3c931d942ae710de766 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -150,10 +150,10 @@ pub struct StanzaError { pub other: Option, } -impl<'a> TryFrom<&'a Element> for StanzaError { +impl TryFrom for StanzaError { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("error", ns::JABBER_CLIENT) { return Err(Error::ParseError("This is not an error element.")); } @@ -205,7 +205,7 @@ impl<'a> TryFrom<&'a Element> for StanzaError { } } -impl<'a> Into for &'a StanzaError { +impl Into for StanzaError { fn into(self) -> Element { let mut root = Element::builder("error") .ns(ns::JABBER_CLIENT) @@ -240,7 +240,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let error = StanzaError::try_from(&elem).unwrap(); + let error = StanzaError::try_from(elem).unwrap(); assert_eq!(error.type_, ErrorType::Cancel); assert_eq!(error.defined_condition, DefinedCondition::UndefinedCondition); } @@ -248,7 +248,7 @@ mod tests { #[test] fn test_invalid_type() { let elem: Element = "".parse().unwrap(); - let error = StanzaError::try_from(&elem).unwrap_err(); + let error = StanzaError::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -256,7 +256,7 @@ mod tests { assert_eq!(message, "Required attribute 'type' missing."); let elem: Element = "".parse().unwrap(); - let error = StanzaError::try_from(&elem).unwrap_err(); + let error = StanzaError::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -267,7 +267,7 @@ mod tests { #[test] fn test_invalid_condition() { let elem: Element = "".parse().unwrap(); - let error = StanzaError::try_from(&elem).unwrap_err(); + let error = StanzaError::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 2b87890bf338daf2f1f1595614b4d5a6981b6be7..38008d0c5c8230050c7a12c8f200e9dccebe88fc 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -24,10 +24,10 @@ pub enum StanzaId { }, } -impl<'a> TryFrom<&'a Element> for StanzaId { +impl TryFrom for StanzaId { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { let is_stanza_id = elem.is("stanza-id", ns::SID); if !is_stanza_id && !elem.is("origin-id", ns::SID) { return Err(Error::ParseError("This is not a stanza-id or origin-id element.")); @@ -45,20 +45,20 @@ impl<'a> TryFrom<&'a Element> for StanzaId { } } -impl<'a> Into for &'a StanzaId { +impl Into for StanzaId { fn into(self) -> Element { - match *self { - StanzaId::StanzaId { ref id, ref by } => { + match self { + StanzaId::StanzaId { id, by } => { Element::builder("stanza-id") .ns(ns::SID) - .attr("id", id.clone()) - .attr("by", String::from(by.clone())) + .attr("id", id) + .attr("by", String::from(by)) .build() }, - StanzaId::OriginId { ref id } => { + StanzaId::OriginId { id } => { Element::builder("origin-id") .ns(ns::SID) - .attr("id", id.clone()) + .attr("id", id) .build() }, } @@ -73,7 +73,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let stanza_id = StanzaId::try_from(&elem).unwrap(); + let stanza_id = StanzaId::try_from(elem).unwrap(); if let StanzaId::StanzaId { id, by } = stanza_id { assert_eq!(id, String::from("coucou")); assert_eq!(by, Jid::from_str("coucou@coucou").unwrap()); @@ -82,7 +82,7 @@ mod tests { } let elem: Element = "".parse().unwrap(); - let stanza_id = StanzaId::try_from(&elem).unwrap(); + let stanza_id = StanzaId::try_from(elem).unwrap(); if let StanzaId::OriginId { id } = stanza_id { assert_eq!(id, String::from("coucou")); } else { @@ -93,7 +93,7 @@ mod tests { #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = StanzaId::try_from(&elem).unwrap_err(); + let error = StanzaId::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -104,7 +104,7 @@ mod tests { #[test] fn test_invalid_id() { let elem: Element = "".parse().unwrap(); - let error = StanzaId::try_from(&elem).unwrap_err(); + let error = StanzaId::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -115,7 +115,7 @@ mod tests { #[test] fn test_invalid_by() { let elem: Element = "".parse().unwrap(); - let error = StanzaId::try_from(&elem).unwrap_err(); + let error = StanzaId::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -127,7 +127,7 @@ mod tests { fn test_serialise() { let elem: Element = "".parse().unwrap(); let stanza_id = StanzaId::StanzaId { id: String::from("coucou"), by: Jid::from_str("coucou@coucou").unwrap() }; - let elem2 = (&stanza_id).into(); + let elem2 = stanza_id.into(); assert_eq!(elem, elem2); } } From cde19967a62686a2fdc3ce137ed72c2bb7379a5a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 23 May 2017 23:50:00 +0100 Subject: [PATCH 196/698] Release version 0.3.0! --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 41d4a464fedf3e5b83258b0f9608007f6d2433d4..a0d95aa30586e6afa8aa98a760d6069b5770399e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.2.0" +version = "0.3.0" authors = ["Emmanuel Gil Peyrot "] description = "Collection of parsers and serialisers for XMPP extensions" homepage = "https://hg.linkmauve.fr/xmpp-parsers" From 6f7e504c8a82fa64b0ed53221204f82fe9e6755a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 00:12:16 +0100 Subject: [PATCH 198/698] ChangeLog: Forgotten update for 0.3.0. --- ChangeLog | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/ChangeLog b/ChangeLog index d8e85e11df28f7d4fe9955a72dde69553b21afc8..e8c42e2a450e16e1ee37f80a0ac2d2d6015cb07c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,32 @@ +Version 0.3.0: +2017-05-23 Emmanuel Gil Peyrot + * Big changes: + - All parsers and serialisers now consume their argument, this + makes the API way more efficient, but you will have to clone + before passing your structs in it if you want to keep them. + - Payloads of stanzas are not parsed automatically anymore, to + let applications which want to forward them as-is do so more + easily. Parsing now always succeeds on unknown payloads, it + just puts them into an Unknown value containing the existing + minidom Element. + * New parsers/serialisers: + - Last User Interaction in Presence, XEP-0319. + * Improved parsers/serialisers: + - Message now supports subject, bodies and threads as per + RFC 6121 §5.2. + - Replace most attribute reads with a nice macro. + - Use enums for more enum-like things, for example Algo in + Hash, or FieldType in DataForm. + - Wire up stanza-id and origin-id to MessagePayload. + - Wire up MAM elements to message and iq payloads. + - Changes in the RSM API. + - Add support for more data forms elements, but still not the + complete set. + - Thanks to minidom 0.3.1, check for explicitly disallowed + extra attributes in some elements. + * Crate updates: + - minidom 0.4.1 + Version 0.2.0: 2017-05-06 Emmanuel Gil Peyrot * New parsers/serialisers: From fbeeae5c64ec2e4bc2034e50846b3bf56a8345c0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 21:30:33 +0100 Subject: [PATCH 199/698] presence: Simplify the code. --- src/presence.rs | 128 +++++++++++++++++++++++++----------------------- 1 file changed, 67 insertions(+), 61 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index 9dd1c62ca20708e25ce8bedc72412f064b983be2..6b4b1ac4e587d4b70ca89fe7da13b925d57858eb 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -8,7 +8,7 @@ use std::convert::TryFrom; use std::str::FromStr; use std::collections::BTreeMap; -use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; +use minidom::{Element, IntoAttributeValue}; use jid::Jid; @@ -29,15 +29,31 @@ pub enum Show { Xa, } -impl IntoElements for Show { - fn into_elements(self, emitter: &mut ElementEmitter) { - let elem = Element::builder(match self { - Show::Away => "away", - Show::Chat => "chat", - Show::Dnd => "dnd", - Show::Xa => "xa", - }).build(); - emitter.append_child(elem); +impl FromStr for Show { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "away" => Show::Away, + "chat" => Show::Chat, + "dnd" => Show::Dnd, + "xa" => Show::Xa, + + _ => return Err(Error::ParseError("Invalid value for show.")), + }) + } +} + +impl Into for Show { + fn into(self) -> Element { + Element::builder("show") + .append(match self { + Show::Away => "away", + Show::Chat => "chat", + Show::Dnd => "dnd", + Show::Xa => "xa", + }) + .build() } } @@ -86,7 +102,7 @@ impl Into for PresencePayload { PresencePayload::Idle(idle) => idle.into(), PresencePayload::ECaps2(ecaps2) => ecaps2.into(), - PresencePayload::Unknown(elem) => elem.clone(), + PresencePayload::Unknown(elem) => elem, } } } @@ -164,17 +180,20 @@ impl TryFrom for Presence { if !root.is("presence", ns::JABBER_CLIENT) { return Err(Error::ParseError("This is not a presence element.")); } - let from = get_attr!(root, "from", optional); - let to = get_attr!(root, "to", optional); - let id = get_attr!(root, "id", optional); - let type_ = get_attr!(root, "type", default); - let mut show = None; - let mut statuses = BTreeMap::new(); let mut priority = None; - let mut payloads = vec!(); + let mut presence = Presence { + from: get_attr!(root, "from", optional), + to: get_attr!(root, "to", optional), + id: get_attr!(root, "id", optional), + type_: get_attr!(root, "type", default), + show: None, + statuses: BTreeMap::new(), + priority: 0i8, + payloads: vec!(), + }; for elem in root.children() { if elem.is("show", ns::JABBER_CLIENT) { - if show.is_some() { + if presence.show.is_some() { return Err(Error::ParseError("More than one show element in a presence.")); } for _ in elem.children() { @@ -183,14 +202,7 @@ impl TryFrom for Presence { for _ in elem.attrs() { return Err(Error::ParseError("Unknown attribute in show element.")); } - show = Some(match elem.text().as_ref() { - "away" => Show::Away, - "chat" => Show::Chat, - "dnd" => Show::Dnd, - "xa" => Show::Xa, - - _ => return Err(Error::ParseError("Invalid value for show.")), - }); + presence.show = Some(Show::from_str(elem.text().as_ref())?); } else if elem.is("status", ns::JABBER_CLIENT) { for _ in elem.children() { return Err(Error::ParseError("Unknown child in status element.")); @@ -201,7 +213,7 @@ impl TryFrom for Presence { } } let lang = get_attr!(elem, "xml:lang", default); - if statuses.insert(lang, elem.text()).is_some() { + if presence.statuses.insert(lang, elem.text()).is_some() { return Err(Error::ParseError("Status element present twice for the same xml:lang.")); } } else if elem.is("priority", ns::JABBER_CLIENT) { @@ -216,46 +228,40 @@ impl TryFrom for Presence { } priority = Some(Priority::from_str(elem.text().as_ref())?); } else { - payloads.push(elem.clone()); + presence.payloads.push(elem.clone()); } } - Ok(Presence { - from: from, - to: to, - id: id, - type_: type_, - show: show, - statuses: statuses, - priority: priority.unwrap_or(0i8), - payloads: payloads, - }) + if let Some(priority) = priority { + presence.priority = priority; + } + Ok(presence) } } impl Into for Presence { fn into(self) -> Element { - let mut stanza = Element::builder("presence") - .ns(ns::JABBER_CLIENT) - .attr("from", self.from.clone().and_then(|value| Some(String::from(value)))) - .attr("to", self.to.clone().and_then(|value| Some(String::from(value)))) - .attr("id", self.id.clone()) - .attr("type", self.type_.clone()) - .append(self.show.clone()) - .append(self.statuses.iter().map(|(lang, status)| { - Element::builder("status") - .attr("xml:lang", match lang.as_ref() { - "" => None, - lang => Some(lang), - }) - .append(status.clone()) - .build() - }).collect::>()) - .append(if self.priority == 0 { None } else { Some(format!("{}", self.priority)) }) - .build(); - for child in self.payloads.clone() { - stanza.append_child(child); - } - stanza + Element::builder("presence") + .ns(ns::JABBER_CLIENT) + .attr("from", self.from.and_then(|value| Some(String::from(value)))) + .attr("to", self.to.and_then(|value| Some(String::from(value)))) + .attr("id", self.id) + .attr("type", self.type_) + .append(match self.show { + Some(show) => Some({ let elem: Element = show.into(); elem }), + None => None + }) + .append(self.statuses.iter().map(|(lang, status)| { + Element::builder("status") + .attr("xml:lang", match lang.as_ref() { + "" => None, + lang => Some(lang), + }) + .append(status) + .build() + }).collect::>()) + .append(if self.priority == 0 { None } else { Some(format!("{}", self.priority)) }) + .append(self.payloads) + .build() } } From 21b92621f0f7bbf611ab4618f5723399fcad924f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 21:32:04 +0100 Subject: [PATCH 200/698] iq, message: Remove useless clone on unknown element. --- src/iq.rs | 2 +- src/message.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index aaf7b027998ce5bf2818645892a9a06c79023b00..28b49efbaf6bba033b9830def28aef95cb29b76d 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -61,7 +61,7 @@ impl TryFrom for IqPayload { ("fin", ns::MAM) => IqPayload::MamFin(MamFin::try_from(elem)?), ("prefs", ns::MAM) => IqPayload::MamPrefs(MamPrefs::try_from(elem)?), - _ => IqPayload::Unknown(elem.clone()), + _ => IqPayload::Unknown(elem), }) } } diff --git a/src/message.rs b/src/message.rs index be47756a5c2d622dafc77936184c9e13e6621d7f..bc8a003516340bebe499b8e0c70f1798fd87bcbf 100644 --- a/src/message.rs +++ b/src/message.rs @@ -79,7 +79,7 @@ impl TryFrom for MessagePayload { // XEP-0380 ("encryption", ns::EME) => MessagePayload::ExplicitMessageEncryption(ExplicitMessageEncryption::try_from(elem)?), - _ => MessagePayload::Unknown(elem.clone()), + _ => MessagePayload::Unknown(elem), }) } } From 8182213666cfb10607c1e420537a5633c0a20088 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 21:35:09 +0100 Subject: [PATCH 201/698] iq: Reintroduce a reference Into to get the type from the payload. --- src/iq.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index 28b49efbaf6bba033b9830def28aef95cb29b76d..7ab3309fc74b52cb6c7ed1263146afa76b59da14 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -43,7 +43,7 @@ impl TryFrom for IqPayload { fn try_from(elem: Element) -> Result { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { // XEP-0030 - ("query", ns::DISCO_INFO) => IqPayload::Disco(Disco::try_from(elem.clone())?), + ("query", ns::DISCO_INFO) => IqPayload::Disco(Disco::try_from(elem)?), // XEP-0047 ("open", ns::IBB) @@ -74,9 +74,9 @@ pub enum IqType { Error(StanzaError), } -impl IntoAttributeValue for IqType { +impl<'a> IntoAttributeValue for &'a IqType { fn into_attribute_value(self) -> Option { - Some(match self { + Some(match *self { IqType::Get(_) => "get", IqType::Set(_) => "set", IqType::Result(_) => "result", @@ -144,7 +144,7 @@ impl TryFrom for Iq { IqType::Result(None) } } else if type_ == "error" { - if let Some(payload) = error_payload.clone() { + if let Some(payload) = error_payload { IqType::Error(payload) } else { return Err(Error::ParseError("Wrong number of children in iq element.")); @@ -173,7 +173,7 @@ impl Into for IqPayload { IqPayload::MamFin(fin) => fin.into(), IqPayload::MamPrefs(prefs) => prefs.into(), - IqPayload::Unknown(elem) => elem.clone(), + IqPayload::Unknown(elem) => elem, } } } @@ -182,12 +182,12 @@ impl Into for Iq { fn into(self) -> Element { let mut stanza = Element::builder("iq") .ns(ns::JABBER_CLIENT) - .attr("from", self.from.clone().and_then(|value| Some(String::from(value)))) - .attr("to", self.to.clone().and_then(|value| Some(String::from(value)))) - .attr("id", self.id.clone()) - .attr("type", self.payload.clone()) + .attr("from", self.from.and_then(|value| Some(String::from(value)))) + .attr("to", self.to.and_then(|value| Some(String::from(value)))) + .attr("id", self.id) + .attr("type", &self.payload) .build(); - let elem = match self.payload.clone() { + let elem = match self.payload { IqType::Get(elem) | IqType::Set(elem) | IqType::Result(Some(elem)) => elem, From 453a3635fda8034c9f8a6da817f5be87d8cded20 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 21:40:11 +0100 Subject: [PATCH 202/698] message: Remove extra clones, and simplify Into. --- src/message.rs | 65 ++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/src/message.rs b/src/message.rs index bc8a003516340bebe499b8e0c70f1798fd87bcbf..13caed356d623307feedd049e2c79a7ca46c2f5f 100644 --- a/src/message.rs +++ b/src/message.rs @@ -97,7 +97,7 @@ impl Into for MessagePayload { MessagePayload::StanzaId(stanza_id) => stanza_id.into(), MessagePayload::MamResult(result) => result.into(), - MessagePayload::Unknown(elem) => elem.clone(), + MessagePayload::Unknown(elem) => elem, } } } @@ -221,39 +221,36 @@ impl TryFrom for Message { impl Into for Message { fn into(self) -> Element { - let mut stanza = Element::builder("message") - .ns(ns::JABBER_CLIENT) - .attr("from", self.from.clone().and_then(|value| Some(String::from(value)))) - .attr("to", self.to.clone().and_then(|value| Some(String::from(value)))) - .attr("id", self.id.clone()) - .attr("type", self.type_.clone()) - .append(self.subjects.iter() - .map(|(lang, subject)| { - Element::builder("subject") - .ns(ns::JABBER_CLIENT) - .attr("xml:lang", match lang.as_ref() { - "" => None, - lang => Some(lang), - }) - .append(subject.clone()) - .build() }) - .collect::>()) - .append(self.bodies.iter() - .map(|(lang, body)| { - Element::builder("body") - .ns(ns::JABBER_CLIENT) - .attr("xml:lang", match lang.as_ref() { - "" => None, - lang => Some(lang), - }) - .append(body.clone()) - .build() }) - .collect::>()) - .build(); - for child in self.payloads.clone() { - stanza.append_child(child); - } - stanza + Element::builder("message") + .ns(ns::JABBER_CLIENT) + .attr("from", self.from.and_then(|value| Some(String::from(value)))) + .attr("to", self.to.and_then(|value| Some(String::from(value)))) + .attr("id", self.id) + .attr("type", self.type_) + .append(self.subjects.iter() + .map(|(lang, subject)| { + Element::builder("subject") + .ns(ns::JABBER_CLIENT) + .attr("xml:lang", match lang.as_ref() { + "" => None, + lang => Some(lang), + }) + .append(subject) + .build() }) + .collect::>()) + .append(self.bodies.iter() + .map(|(lang, body)| { + Element::builder("body") + .ns(ns::JABBER_CLIENT) + .attr("xml:lang", match lang.as_ref() { + "" => None, + lang => Some(lang), + }) + .append(body) + .build() }) + .collect::>()) + .append(self.payloads) + .build() } } From 6952f3adfc6575e71a6333d5f439aa51e2b47be0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 21:42:29 +0100 Subject: [PATCH 203/698] message_correct: Check for unwanted attributes. --- src/message_correct.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/message_correct.rs b/src/message_correct.rs index 35494f810b04c59ad3365049a79338655967307c..8ce747f114d9139b843c58bf34d52d8a46ec5043 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -27,6 +27,11 @@ impl TryFrom for Replace { for _ in elem.children() { return Err(Error::ParseError("Unknown child in replace element.")); } + for (attr, _) in elem.attrs() { + if attr != "id" { + return Err(Error::ParseError("Unknown attribute in replace element.")); + } + } let id = get_attr!(elem, "id", required); Ok(Replace { id }) } @@ -36,7 +41,7 @@ impl Into for Replace { fn into(self) -> Element { Element::builder("replace") .ns(ns::MESSAGE_CORRECT) - .attr("id", self.id.clone()) + .attr("id", self.id) .build() } } @@ -51,6 +56,17 @@ mod tests { Replace::try_from(elem).unwrap(); } + #[test] + fn test_invalid_attribute() { + let elem: Element = "".parse().unwrap(); + let error = Replace::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in replace element."); + } + #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); From ecd98251bfd7a0421b4ecd791c3efbec617431a1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 21:44:35 +0100 Subject: [PATCH 204/698] rsm: Remove useless clones. --- src/rsm.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rsm.rs b/src/rsm.rs index 7ac80b192b711f1f1828b7aa632a7446423d2ef2..21d86ee790ad189768e48f087d4b13636c0e1397 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -92,10 +92,10 @@ impl Into for Set { .ns(ns::RSM) .build(); if self.after.is_some() { - elem.append_child(Element::builder("after").ns(ns::RSM).append(self.after.clone()).build()); + elem.append_child(Element::builder("after").ns(ns::RSM).append(self.after).build()); } if self.before.is_some() { - elem.append_child(Element::builder("before").ns(ns::RSM).append(self.before.clone()).build()); + elem.append_child(Element::builder("before").ns(ns::RSM).append(self.before).build()); } if self.count.is_some() { elem.append_child(Element::builder("count").ns(ns::RSM).append(format!("{}", self.count.unwrap())).build()); @@ -104,13 +104,13 @@ impl Into for Set { elem.append_child(Element::builder("first") .ns(ns::RSM) .attr("index", self.first_index.map(|index| format!("{}", index))) - .append(self.first.clone()).build()); + .append(self.first).build()); } if self.index.is_some() { elem.append_child(Element::builder("index").ns(ns::RSM).append(format!("{}", self.index.unwrap())).build()); } if self.last.is_some() { - elem.append_child(Element::builder("last").ns(ns::RSM).append(self.last.clone()).build()); + elem.append_child(Element::builder("last").ns(ns::RSM).append(self.last).build()); } if self.max.is_some() { elem.append_child(Element::builder("max").ns(ns::RSM).append(format!("{}", self.max.unwrap())).build()); From 00f3f3eee63eda60ffa1fad4b0d59b306e5381ec Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 22:28:54 +0100 Subject: [PATCH 205/698] jingle: Simplify parsing and serialisation. --- src/jingle.rs | 298 ++++++++++++++++++++++++++------------------------ 1 file changed, 155 insertions(+), 143 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index 4796ae41bcfa926d4f2b859926677fe9c7bfa420..7f925bf80af06a9d52469c3f6480615a5596b52c 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -7,7 +7,8 @@ use std::convert::TryFrom; use std::str::FromStr; -use minidom::Element; +use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; +use jid::Jid; use error::Error; use ns; @@ -57,9 +58,9 @@ impl FromStr for Action { } } -impl From for String { - fn from(action: Action) -> String { - String::from(match action { +impl IntoAttributeValue for Action { + fn into_attribute_value(self) -> Option { + Some(String::from(match self { Action::ContentAccept => "content-accept", Action::ContentAdd => "content-add", Action::ContentModify => "content-modify", @@ -75,13 +76,10 @@ impl From for String { Action::TransportInfo => "transport-info", Action::TransportReject => "transport-reject", Action::TransportReplace => "transport-replace", - }) + })) } } -// TODO: use a real JID type. -type Jid = String; - #[derive(Debug, Clone, PartialEq)] pub enum Creator { Initiator, @@ -118,6 +116,12 @@ pub enum Senders { Responder, } +impl Default for Senders { + fn default() -> Senders { + Senders::Both + } +} + impl FromStr for Senders { type Err = Error; @@ -155,6 +159,66 @@ pub struct Content { pub security: Option, } +impl TryFrom for Content { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("content", ns::JINGLE) { + return Err(Error::ParseError("This is not a content element.")); + } + + let mut content = Content { + creator: get_attr!(elem, "creator", required), + disposition: get_attr!(elem, "disposition", optional).unwrap_or(String::from("session")), + name: get_attr!(elem, "name", required), + senders: get_attr!(elem, "senders", default), + description: None, + transport: None, + security: None, + }; + for child in elem.children() { + if child.name() == "description" { + if content.description.is_some() { + return Err(Error::ParseError("Content must not have more than one description.")); + } + content.description = Some(child.clone()); + } else if child.name() == "transport" { + if content.transport.is_some() { + return Err(Error::ParseError("Content must not have more than one transport.")); + } + content.transport = Some(child.clone()); + } else if child.name() == "security" { + if content.security.is_some() { + return Err(Error::ParseError("Content must not have more than one security.")); + } + content.security = Some(child.clone()); + } + } + Ok(content) + } +} + +impl Into for Content { + fn into(self) -> Element { + Element::builder("content") + .ns(ns::JINGLE) + .attr("creator", String::from(self.creator)) + .attr("disposition", self.disposition) + .attr("name", self.name) + .attr("senders", String::from(self.senders)) + .append(self.description) + .append(self.transport) + .append(self.security) + .build() + } +} + +impl IntoElements for Content { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + #[derive(Debug, Clone, PartialEq)] pub enum Reason { AlternativeSession, //(String), @@ -234,6 +298,58 @@ pub struct ReasonElement { pub text: Option, } +impl TryFrom for ReasonElement { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("reason", ns::JINGLE) { + return Err(Error::ParseError("This is not a reason element.")); + } + let mut reason = None; + let mut text = None; + for child in elem.children() { + if child.ns() != Some(ns::JINGLE) { + return Err(Error::ParseError("Reason contains a foreign element.")); + } + match child.name() { + "text" => { + if text.is_some() { + return Err(Error::ParseError("Reason must not have more than one text.")); + } + text = Some(child.text()); + }, + name => { + if reason.is_some() { + return Err(Error::ParseError("Reason must not have more than one reason.")); + } + reason = Some(name.parse()?); + }, + } + } + let reason = reason.ok_or(Error::ParseError("Reason doesn’t contain a valid reason."))?; + Ok(ReasonElement { + reason: reason, + text: text, + }) + } +} + +impl Into for ReasonElement { + fn into(self) -> Element { + let reason: Element = self.reason.into(); + Element::builder("reason") + .append(reason) + .append(self.text) + .build() + } +} + +impl IntoElements for ReasonElement { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + #[derive(Debug, Clone)] pub struct Jingle { pub action: Action, @@ -253,150 +369,46 @@ impl TryFrom for Jingle { return Err(Error::ParseError("This is not a Jingle element.")); } - let mut contents: Vec = vec!(); - - let action = root.attr("action") - .ok_or(Error::ParseError("Jingle must have an 'action' attribute."))? - .parse()?; - let initiator = root.attr("initiator") - .and_then(|initiator| initiator.parse().ok()); - let responder = root.attr("responder") - .and_then(|responder| responder.parse().ok()); - let sid = root.attr("sid") - .ok_or(Error::ParseError("Jingle must have a 'sid' attribute."))?; - let mut reason_element = None; - let mut other = vec!(); - - for child in root.children() { + let mut jingle = Jingle { + action: get_attr!(root, "action", required), + initiator: get_attr!(root, "initiator", optional), + responder: get_attr!(root, "responder", optional), + sid: get_attr!(root, "sid", required), + contents: vec!(), + reason: None, + other: vec!(), + }; + + for child in root.children().cloned() { if child.is("content", ns::JINGLE) { - let creator = child.attr("creator") - .ok_or(Error::ParseError("Content must have a 'creator' attribute."))? - .parse()?; - let disposition = child.attr("disposition") - .unwrap_or("session"); - let name = child.attr("name") - .ok_or(Error::ParseError("Content must have a 'name' attribute."))?; - let senders = child.attr("senders") - .unwrap_or("both") - .parse()?; - let mut description = None; - let mut transport = None; - let mut security = None; - for stuff in child.children() { - if stuff.name() == "description" { - if description.is_some() { - return Err(Error::ParseError("Content must not have more than one description.")); - } - description = Some(stuff.clone()); - } else if stuff.name() == "transport" { - if transport.is_some() { - return Err(Error::ParseError("Content must not have more than one transport.")); - } - transport = Some(stuff.clone()); - } else if stuff.name() == "security" { - if security.is_some() { - return Err(Error::ParseError("Content must not have more than one security.")); - } - security = Some(stuff.clone()); - } - } - contents.push(Content { - creator: creator, - disposition: disposition.to_owned(), - name: name.to_owned(), - senders: senders, - description: description, - transport: transport, - security: security, - }); + let content = Content::try_from(child.clone())?; + jingle.contents.push(content); } else if child.is("reason", ns::JINGLE) { - if reason_element.is_some() { + if jingle.reason.is_some() { return Err(Error::ParseError("Jingle must not have more than one reason.")); } - let mut reason = None; - let mut text = None; - for stuff in child.children() { - if stuff.ns() != Some(ns::JINGLE) { - return Err(Error::ParseError("Reason contains a foreign element.")); - } - let name = stuff.name(); - if name == "text" { - if text.is_some() { - return Err(Error::ParseError("Reason must not have more than one text.")); - } - text = Some(stuff.text()); - } else { - reason = Some(name.parse()?); - } - } - if reason.is_none() { - return Err(Error::ParseError("Reason doesn’t contain a valid reason.")); - } - reason_element = Some(ReasonElement { - reason: reason.unwrap(), - text: text, - }); + let reason = ReasonElement::try_from(child.clone())?; + jingle.reason = Some(reason); } else { - other.push(child.clone()); + jingle.other.push(child.clone()); } } - Ok(Jingle { - action: action, - initiator: initiator, - responder: responder, - sid: sid.to_owned(), - contents: contents, - reason: reason_element, - other: other, - }) - } -} - -impl Into for Content { - fn into(self) -> Element { - let mut root = Element::builder("content") - .ns(ns::JINGLE) - .attr("creator", String::from(self.creator.clone())) - .attr("disposition", self.disposition.clone()) - .attr("name", self.name.clone()) - .attr("senders", String::from(self.senders.clone())) - .build(); - if let Some(description) = self.description.clone() { - root.append_child(description); - } - if let Some(transport) = self.transport.clone() { - root.append_child(transport); - } - if let Some(security) = self.security.clone() { - root.append_child(security); - } - root + Ok(jingle) } } impl Into for Jingle { fn into(self) -> Element { - let mut root = Element::builder("jingle") - .ns(ns::JINGLE) - .attr("action", String::from(self.action.clone())) - .attr("initiator", self.initiator.clone()) - .attr("responder", self.responder.clone()) - .attr("sid", self.sid.clone()) - .build(); - for content in self.contents { - let content_elem = content.into(); - root.append_child(content_elem); - } - if let Some(reason) = self.reason { - let reason2: Element = reason.reason.into(); - let reason_elem = Element::builder("reason") - .append(reason2) - .append(reason.text) - .build(); - root.append_child(reason_elem); - } - root + Element::builder("jingle") + .ns(ns::JINGLE) + .attr("action", self.action) + .attr("initiator", match self.initiator { Some(initiator) => Some(String::from(initiator)), None => None }) + .attr("responder", match self.responder { Some(responder) => Some(String::from(responder)), None => None }) + .attr("sid", self.sid) + .append(self.contents) + .append(self.reason) + .build() } } @@ -420,7 +432,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Jingle must have an 'action' attribute."); + assert_eq!(message, "Required attribute 'action' missing."); let elem: Element = "".parse().unwrap(); let error = Jingle::try_from(elem).unwrap_err(); @@ -428,7 +440,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Jingle must have a 'sid' attribute."); + assert_eq!(message, "Required attribute 'sid' missing."); let elem: Element = "".parse().unwrap(); let error = Jingle::try_from(elem).unwrap_err(); @@ -465,7 +477,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Content must have a 'creator' attribute."); + assert_eq!(message, "Required attribute 'creator' missing."); let elem: Element = "".parse().unwrap(); let error = Jingle::try_from(elem).unwrap_err(); @@ -473,7 +485,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Content must have a 'name' attribute."); + assert_eq!(message, "Required attribute 'name' missing."); let elem: Element = "".parse().unwrap(); let error = Jingle::try_from(elem).unwrap_err(); From 47fc1169063d582796f64a53770e7aa7d0adaaa0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 22:41:09 +0100 Subject: [PATCH 206/698] media_element: Stop swallowing integer parsing errors. --- src/media_element.rs | 49 ++++++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/src/media_element.rs b/src/media_element.rs index 54ffee132eb5a6ee7b20bbd4012e166b6d7d8cb4..931cab6f1d59dab90aed45034ed4d9e1e741d0ec 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -33,22 +33,24 @@ impl TryFrom for MediaElement { return Err(Error::ParseError("This is not a media element.")); } - let width = elem.attr("width").and_then(|width| width.parse().ok()); - let height = elem.attr("height").and_then(|height| height.parse().ok()); - let mut uris = vec!(); + let mut media = MediaElement { + width: get_attr!(elem, "width", optional), + height: get_attr!(elem, "height", optional), + uris: vec!(), + }; for uri in elem.children() { if uri.is("uri", ns::MEDIA_ELEMENT) { - let type_ = uri.attr("type").ok_or(Error::ParseError("Attribute type on uri is mandatory."))?; + let type_ = get_attr!(uri, "type", required); let text = uri.text().trim().to_owned(); if text == "" { return Err(Error::ParseError("URI missing in uri.")); } - uris.push(URI { type_: type_.to_owned(), uri: text }); + media.uris.push(URI { type_: type_, uri: text }); } else { return Err(Error::ParseError("Unknown child in media element.")); } } - Ok(MediaElement { width: width, height: height, uris: uris }) + Ok(media) } } @@ -56,6 +58,7 @@ impl TryFrom for MediaElement { mod tests { use super::*; use data_forms::DataForm; + use std::error::Error as StdError; #[test] fn test_simple() { @@ -86,20 +89,36 @@ mod tests { #[test] fn test_invalid_width_height() { let elem: Element = "".parse().unwrap(); - let media = MediaElement::try_from(elem).unwrap(); - assert!(media.width.is_none()); + let error = MediaElement::try_from(elem).unwrap_err(); + let error = match error { + Error::ParseIntError(error) => error, + _ => panic!(), + }; + assert_eq!(error.description(), "cannot parse integer from empty string"); let elem: Element = "".parse().unwrap(); - let media = MediaElement::try_from(elem).unwrap(); - assert!(media.width.is_none()); + let error = MediaElement::try_from(elem).unwrap_err(); + let error = match error { + Error::ParseIntError(error) => error, + _ => panic!(), + }; + assert_eq!(error.description(), "invalid digit found in string"); let elem: Element = "".parse().unwrap(); - let media = MediaElement::try_from(elem).unwrap(); - assert!(media.height.is_none()); + let error = MediaElement::try_from(elem).unwrap_err(); + let error = match error { + Error::ParseIntError(error) => error, + _ => panic!(), + }; + assert_eq!(error.description(), "cannot parse integer from empty string"); let elem: Element = "".parse().unwrap(); - let media = MediaElement::try_from(elem).unwrap(); - assert!(media.height.is_none()); + let error = MediaElement::try_from(elem).unwrap_err(); + let error = match error { + Error::ParseIntError(error) => error, + _ => panic!(), + }; + assert_eq!(error.description(), "invalid digit found in string"); } #[test] @@ -121,7 +140,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Attribute type on uri is mandatory."); + assert_eq!(message, "Required attribute 'type' missing."); let elem: Element = "".parse().unwrap(); let error = MediaElement::try_from(elem).unwrap_err(); From 947c49330f62177996821bc3050fdb76b9a7675c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 22:43:21 +0100 Subject: [PATCH 207/698] receipts: Parse 'id' using get_attr!(), and make it optional. --- src/receipts.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/receipts.rs b/src/receipts.rs index f7c1d50d2af1352210c98d6c3317742ef04b7e47..72b357a1de277ef15308ed3704a6c0a58fe17cb7 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -15,7 +15,7 @@ use ns; #[derive(Debug, Clone)] pub enum Receipt { Request, - Received(String), + Received(Option), } impl TryFrom for Receipt { @@ -26,9 +26,17 @@ impl TryFrom for Receipt { return Err(Error::ParseError("Unknown child in receipt element.")); } if elem.is("request", ns::RECEIPTS) { + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown attribute in request element.")); + } Ok(Receipt::Request) } else if elem.is("received", ns::RECEIPTS) { - let id = elem.attr("id").unwrap_or("").to_owned(); + for (attr, _) in elem.attrs() { + if attr != "id" { + return Err(Error::ParseError("Unknown attribute in received element.")); + } + } + let id = get_attr!(elem, "id", optional); Ok(Receipt::Received(id)) } else { Err(Error::ParseError("This is not a receipt element.")) @@ -40,13 +48,11 @@ impl Into for Receipt { fn into(self) -> Element { match self { Receipt::Request => Element::builder("request") - .ns(ns::RECEIPTS) - .build(), + .ns(ns::RECEIPTS), Receipt::Received(id) => Element::builder("received") .ns(ns::RECEIPTS) - .attr("id", id) - .build(), - } + .attr("id", id), + }.build() } } @@ -72,7 +78,7 @@ mod tests { let elem: Element = receipt.into(); assert!(elem.is("request", ns::RECEIPTS)); - let receipt = Receipt::Received("coucou".to_owned()); + let receipt = Receipt::Received(Some(String::from("coucou"))); let elem: Element = receipt.into(); assert!(elem.is("received", ns::RECEIPTS)); assert_eq!(elem.attr("id"), Some("coucou")); From 9bd1e7f295e7f3937d244e9724cb60a28602fc63 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 23:00:38 +0100 Subject: [PATCH 208/698] jingle_s5b: Remove the clones. --- src/jingle_s5b.rs | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index fa5ee667e6bb3b1b7c1d939c944808ac2f2f0167..42c188f8152b5a2ab5a7735c270a6f0bc5ea2636 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -67,12 +67,12 @@ impl Into for Candidate { fn into(self) -> Element { Element::builder("candidate") .ns(ns::JINGLE_S5B) - .attr("cid", self.cid.clone()) - .attr("host", self.host.clone()) - .attr("jid", self.jid.clone()) + .attr("cid", self.cid) + .attr("host", self.host) + .attr("jid", self.jid) .attr("port", match self.port { Some(port) => Some(format!("{}", port)), None => None }) .attr("priority", format!("{}", self.priority)) - .attr("type", self.type_.clone()) + .attr("type", self.type_) .build() } } @@ -143,22 +143,16 @@ impl TryFrom for Transport { payload = Some(if child.is("candidate", ns::JINGLE_S5B) { let mut candidates = match payload { Some(TransportPayload::Candidates(candidates)) => candidates, - Some(_) => return Err(Error::ParseError("Non-activated child already present in JingleS5B transport element.")), + Some(_) => return Err(Error::ParseError("Non-candidate child already present in JingleS5B transport element.")), None => vec!(), }; - let cid = get_attr!(child, "cid", required); - let host = get_attr!(child, "host", required); - let jid = get_attr!(child, "jid", required); - let port = get_attr!(child, "port", optional); - let priority = get_attr!(child, "priority", required); - let type_ = get_attr!(child, "type", default); candidates.push(Candidate { - cid: cid, - host: host, - jid: jid, - port: port, - priority: priority, - type_: type_, + cid: get_attr!(child, "cid", required), + host: get_attr!(child, "host", required), + jid: get_attr!(child, "jid", required), + port: get_attr!(child, "port", optional), + priority: get_attr!(child, "priority", required), + type_: get_attr!(child, "type", default), }); TransportPayload::Candidates(candidates) } else if child.is("activated", ns::JINGLE_S5B) { @@ -204,13 +198,13 @@ impl Into for Transport { fn into(self) -> Element { Element::builder("transport") .ns(ns::JINGLE_S5B) - .attr("sid", self.sid.clone()) - .attr("dstaddr", self.dstaddr.clone()) - .attr("mode", self.mode.clone()) + .attr("sid", self.sid) + .attr("dstaddr", self.dstaddr) + .attr("mode", self.mode) .append(match self.payload { - TransportPayload::Candidates(candidates) => { - candidates.iter() - .map(|candidate| -> Element { candidate.clone().into() }) + TransportPayload::Candidates(mut candidates) => { + candidates.drain(..) + .map(|candidate| candidate.into()) .collect::>() }, TransportPayload::Activated(cid) => { From 17d69596064bb79d77d3b4a75319f0bf48840c3e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 23:38:44 +0100 Subject: [PATCH 209/698] jingle: Remove unused clones. --- src/jingle.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index 7f925bf80af06a9d52469c3f6480615a5596b52c..06582200f93449d14a9f3681cb7bf3cabd792601 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -381,16 +381,16 @@ impl TryFrom for Jingle { for child in root.children().cloned() { if child.is("content", ns::JINGLE) { - let content = Content::try_from(child.clone())?; + let content = Content::try_from(child)?; jingle.contents.push(content); } else if child.is("reason", ns::JINGLE) { if jingle.reason.is_some() { return Err(Error::ParseError("Jingle must not have more than one reason.")); } - let reason = ReasonElement::try_from(child.clone())?; + let reason = ReasonElement::try_from(child)?; jingle.reason = Some(reason); } else { - jingle.other.push(child.clone()); + jingle.other.push(child); } } From 898baddd3f97f0dbe0ecef6fe7df51c2b9f1d653 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 23:47:27 +0100 Subject: [PATCH 210/698] disco: Split Into for Identity and Feature. --- src/disco.rs | 63 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index acd2d4872f78411fe40f86b22d18cab1ec7144a9..46d25dec88161063f9ab0ad17653f22f93856760 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -6,7 +6,7 @@ use std::convert::TryFrom; -use minidom::Element; +use minidom::{Element, IntoElements, ElementEmitter}; use error::Error; use ns; @@ -18,6 +18,21 @@ pub struct Feature { pub var: String, } +impl Into for Feature { + fn into(self) -> Element { + Element::builder("feature") + .ns(ns::DISCO_INFO) + .attr("var", self.var) + .build() + } +} + +impl IntoElements for Feature { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + #[derive(Debug, Clone)] pub struct Identity { pub category: String, // TODO: use an enum here. @@ -26,6 +41,24 @@ pub struct Identity { pub name: Option, } +impl Into for Identity { + fn into(self) -> Element { + Element::builder("identity") + .ns(ns::DISCO_INFO) + .attr("category", self.category) + .attr("type", self.type_) + .attr("xml:lang", self.xml_lang) + .attr("name", self.name) + .build() + } +} + +impl IntoElements for Identity { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + #[derive(Debug, Clone)] pub struct Disco { pub node: Option, @@ -111,31 +144,15 @@ impl TryFrom for Disco { impl Into for Disco { fn into(self) -> Element { - let mut root = Element::builder("query") - .ns(ns::DISCO_INFO) - .attr("node", self.node.clone()) - .build(); - for identity in self.identities { - let identity_element = Element::builder("identity") - .ns(ns::DISCO_INFO) - .attr("category", identity.category.clone()) - .attr("type", identity.type_.clone()) - .attr("xml:lang", identity.xml_lang.clone()) - .attr("name", identity.name.clone()) - .build(); - root.append_child(identity_element); - } - for feature in self.features { - let feature_element = Element::builder("feature") - .ns(ns::DISCO_INFO) - .attr("var", feature.var.clone()) - .build(); - root.append_child(feature_element); - } for _ in self.extensions { panic!("Not yet implemented!"); } - root + Element::builder("query") + .ns(ns::DISCO_INFO) + .attr("node", self.node) + .append(self.identities) + .append(self.features) + .build() } } From 3678d9f0d3dac11b03413f581a76a012b19ce257 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 23:56:35 +0100 Subject: [PATCH 211/698] stanza_error: Simplify attribute handling. --- src/stanza_error.rs | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 4f30f3a44171529b4066c3c931d942ae710de766..0a3275792ad1c3a0ce727408e2963ecef47e7594 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -8,7 +8,7 @@ use std::convert::TryFrom; use std::str::FromStr; use std::collections::BTreeMap; -use minidom::Element; +use minidom::{Element, IntoAttributeValue}; use error::Error; use jid::Jid; @@ -39,15 +39,15 @@ impl FromStr for ErrorType { } } -impl From for String { - fn from(type_: ErrorType) -> String { - String::from(match type_ { +impl IntoAttributeValue for ErrorType { + fn into_attribute_value(self) -> Option { + Some(String::from(match self { ErrorType::Auth => "auth", ErrorType::Cancel => "cancel", ErrorType::Continue => "continue", ErrorType::Modify => "modify", ErrorType::Wait => "wait", - }) + })) } } @@ -189,11 +189,7 @@ impl TryFrom for StanzaError { other = Some(child.clone()); } } - - if defined_condition.is_none() { - return Err(Error::ParseError("Error must have a defined-condition.")); - } - let defined_condition = defined_condition.unwrap(); + let defined_condition = defined_condition.ok_or(Error::ParseError("Error must have a defined-condition."))?; Ok(StanzaError { type_: type_, @@ -209,16 +205,13 @@ impl Into for StanzaError { fn into(self) -> Element { let mut root = Element::builder("error") .ns(ns::JABBER_CLIENT) - .attr("type", String::from(self.type_.clone())) - .attr("by", match self.by { - Some(ref by) => Some(String::from(by.clone())), - None => None, - }) - .append(Element::builder(self.defined_condition.clone()) + .attr("type", self.type_) + .attr("by", self.by.and_then(|by| Some(String::from(by)))) + .append(Element::builder(self.defined_condition) .ns(ns::XMPP_STANZAS) .build()) .build(); - for (lang, text) in self.texts.clone() { + for (lang, text) in self.texts { let elem = Element::builder("text") .ns(ns::XMPP_STANZAS) .attr("xml:lang", lang) @@ -226,8 +219,8 @@ impl Into for StanzaError { .build(); root.append_child(elem); } - if let Some(ref other) = self.other { - root.append_child(other.clone()); + if let Some(other) = self.other { + root.append_child(other); } root } From 9bb65ea8fb85887368ed2c74aebd1c1547eeec83 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 23:59:45 +0100 Subject: [PATCH 212/698] jingle_ibb: Simplify parsing and remove clones. --- src/jingle_ibb.rs | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index d89df221f80ce5a79d810d0ae334c03b999e053e..6f789d3e969b61a021565ebc8db3d3e29f1421ec 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -25,21 +25,17 @@ impl TryFrom for Transport { type Error = Error; fn try_from(elem: Element) -> Result { - if elem.is("transport", ns::JINGLE_IBB) { - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in JingleIBB element.")); - } - let block_size = get_attr!(elem, "block-size", required); - let sid = get_attr!(elem, "sid", required); - let stanza = get_attr!(elem, "stanza", default); - Ok(Transport { - block_size: block_size, - sid: sid, - stanza: stanza - }) - } else { - Err(Error::ParseError("This is not an JingleIBB element.")) + if !elem.is("transport", ns::JINGLE_IBB) { + return Err(Error::ParseError("This is not an JingleIBB element.")) } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in JingleIBB element.")); + } + Ok(Transport { + block_size: get_attr!(elem, "block-size", required), + sid: get_attr!(elem, "sid", required), + stanza: get_attr!(elem, "stanza", default), + }) } } @@ -48,8 +44,8 @@ impl Into for Transport { Element::builder("transport") .ns(ns::JINGLE_IBB) .attr("block-size", format!("{}", self.block_size)) - .attr("sid", self.sid.clone()) - .attr("stanza", self.stanza.clone()) + .attr("sid", self.sid) + .attr("stanza", self.stanza) .build() } } From b172a6e05ca7d7fcffb9accb2a6919d816f1dea1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 25 May 2017 00:04:37 +0100 Subject: [PATCH 213/698] ecaps2: Remove the last clone. --- src/ecaps2.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index ac9df22edc6f3e3478963578b41b689037d4dcaa..57577ca51c4e2145046207217261b18f4c85fcad 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -48,11 +48,11 @@ impl TryFrom for ECaps2 { } impl Into for ECaps2 { - fn into(self) -> Element { + fn into(mut self) -> Element { Element::builder("c") .ns(ns::ECAPS2) - .append(self.hashes.iter() - .map(|hash| hash.clone().into()) + .append(self.hashes.drain(..) + .map(|hash| hash.into()) .collect::>()) .build() } From 070227ea03bd8e983d1a8b46956083d1b584a1c8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 25 May 2017 00:30:00 +0100 Subject: [PATCH 214/698] eme: Remove two clones. --- src/eme.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/eme.rs b/src/eme.rs index edf6cd359ba443022f60d9227ed0e032728116a9..fe9738613ecafe0d5b7e7e13b7a280643bf3955e 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -28,11 +28,9 @@ impl TryFrom for ExplicitMessageEncryption { for _ in elem.children() { return Err(Error::ParseError("Unknown child in encryption element.")); } - let namespace = get_attr!(elem, "namespace", required); - let name = get_attr!(elem, "name", optional); Ok(ExplicitMessageEncryption { - namespace: namespace, - name: name, + namespace: get_attr!(elem, "namespace", required), + name: get_attr!(elem, "name", optional), }) } } @@ -41,8 +39,8 @@ impl Into for ExplicitMessageEncryption { fn into(self) -> Element { Element::builder("encryption") .ns(ns::EME) - .attr("namespace", self.namespace.clone()) - .attr("name", self.name.clone()) + .attr("namespace", self.namespace) + .attr("name", self.name) .build() } } From a6b3152adde69226e3dfc020b51f3a037431f996 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 25 May 2017 00:30:29 +0100 Subject: [PATCH 215/698] hashes: Make the hash a Vec, to avoid base64 issues. --- src/ecaps2.rs | 32 ++++++++++++++++++-------------- src/hashes.rs | 10 ++++++---- src/jingle_ft.rs | 5 +++-- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 57577ca51c4e2145046207217261b18f4c85fcad..03f8a40b73d6ea6d8540182aac9d1a7b3dacc77a 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -18,7 +18,6 @@ use sha2::{Sha256, Sha512}; use sha3::{Sha3_256, Sha3_512}; use blake2::Blake2b; use digest::{Digest, VariableOutput}; -use base64; #[derive(Debug, Clone)] pub struct ECaps2 { @@ -118,6 +117,12 @@ pub fn compute_disco(disco: &Disco) -> Vec { final_string } +fn get_hash_vec(hash: &[u8]) -> Vec { + let mut vec = Vec::with_capacity(hash.len()); + vec.extend_from_slice(hash); + vec +} + pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { Ok(Hash { hash: match algo { @@ -125,39 +130,39 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { let mut hasher = Sha256::default(); hasher.input(data); let hash = hasher.result(); - base64::encode(&hash.as_slice()) + get_hash_vec(hash.as_slice()) }, Algo::Sha_512 => { let mut hasher = Sha512::default(); hasher.input(data); let hash = hasher.result(); - base64::encode(&hash.as_slice()) + get_hash_vec(hash.as_slice()) }, Algo::Sha3_256 => { let mut hasher = Sha3_256::default(); hasher.input(data); let hash = hasher.result(); - base64::encode(&hash.as_slice()) + get_hash_vec(hash.as_slice()) }, Algo::Sha3_512 => { let mut hasher = Sha3_512::default(); hasher.input(data); let hash = hasher.result(); - base64::encode(&hash.as_slice()) + get_hash_vec(hash.as_slice()) }, Algo::Blake2b_256 => { let mut hasher = Blake2b::default(); hasher.input(data); let mut buf: [u8; 32] = [0; 32]; let hash = hasher.variable_result(&mut buf).unwrap(); - base64::encode(hash) + get_hash_vec(hash) }, Algo::Blake2b_512 => { let mut hasher = Blake2b::default(); hasher.input(data); let mut buf: [u8; 64] = [0; 64]; let hash = hasher.variable_result(&mut buf).unwrap(); - base64::encode(hash) + get_hash_vec(hash) }, Algo::Sha_1 => return Err(String::from("Disabled algorithm sha-1: unsafe.")), Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)), @@ -178,9 +183,9 @@ mod tests { let ecaps2 = ECaps2::try_from(elem).unwrap(); assert_eq!(ecaps2.hashes.len(), 2); assert_eq!(ecaps2.hashes[0].algo, Algo::Sha_256); - assert_eq!(ecaps2.hashes[0].hash, "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4="); + assert_eq!(ecaps2.hashes[0].hash, base64::decode("K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=").unwrap()); assert_eq!(ecaps2.hashes[1].algo, Algo::Sha3_256); - assert_eq!(ecaps2.hashes[1].hash, "+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8="); + assert_eq!(ecaps2.hashes[1].hash, base64::decode("+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=").unwrap()); } #[test] @@ -266,9 +271,9 @@ mod tests { assert_eq!(ecaps2, expected); let sha_256 = ecaps2::hash_ecaps2(&ecaps2, Algo::Sha_256).unwrap(); - assert_eq!(sha_256.hash, "kzBZbkqJ3ADrj7v08reD1qcWUwNGHaidNUgD7nHpiw8="); + assert_eq!(sha_256.hash, base64::decode("kzBZbkqJ3ADrj7v08reD1qcWUwNGHaidNUgD7nHpiw8=").unwrap()); let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, Algo::Sha3_256).unwrap(); - assert_eq!(sha3_256.hash, "79mdYAfU9rEdTOcWDO7UEAt6E56SUzk/g6TnqUeuD9Q="); + assert_eq!(sha3_256.hash, base64::decode("79mdYAfU9rEdTOcWDO7UEAt6E56SUzk/g6TnqUeuD9Q=").unwrap()); } #[test] @@ -438,9 +443,9 @@ mod tests { assert_eq!(ecaps2, expected); let sha_256 = ecaps2::hash_ecaps2(&ecaps2, Algo::Sha_256).unwrap(); - assert_eq!(sha_256.hash, "u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY="); + assert_eq!(sha_256.hash, base64::decode("u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY=").unwrap()); let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, Algo::Sha3_256).unwrap(); - assert_eq!(sha3_256.hash, "XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg="); + assert_eq!(sha3_256.hash, base64::decode("XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg=").unwrap()); } #[test] @@ -452,7 +457,6 @@ mod tests { 0x7D, 0x87, 0xC5, 0x39, 0x2A, 0xAB, 0x79, 0x2D, 0xC2, 0x52, 0xD5, 0xDE, 0x45, 0x33, 0xCC, 0x95, 0x18, 0xD3, 0x8A, 0xA8, 0xDB, 0xF1, 0x92, 0x5A, 0xB9, 0x23, 0x86, 0xED, 0xD4, 0x00, 0x99, 0x23, ); - let known_hash = base64::encode(&known_hash); assert_eq!(hash.hash, known_hash); } } diff --git a/src/hashes.rs b/src/hashes.rs index af9c3a053c81626161b09b2fe0b011249b9899b4..3c9a05eba5cdee8c7ff910f6438e93adc85b995e 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -13,6 +13,8 @@ use error::Error; use ns; +use base64; + #[allow(non_camel_case_types)] #[derive(Debug, Clone, PartialEq)] pub enum Algo { @@ -63,7 +65,7 @@ impl IntoAttributeValue for Algo { #[derive(Debug, Clone, PartialEq)] pub struct Hash { pub algo: Algo, - pub hash: String, + pub hash: Vec, } impl TryFrom for Hash { @@ -79,7 +81,7 @@ impl TryFrom for Hash { let algo = get_attr!(elem, "algo", required); let hash = match elem.text().as_ref() { "" => return Err(Error::ParseError("Hash element shouldn’t be empty.")), - text => text.to_owned(), + text => base64::decode(text)?, }; Ok(Hash { algo: algo, @@ -93,7 +95,7 @@ impl Into for Hash { Element::builder("hash") .ns(ns::HASHES) .attr("algo", self.algo) - .append(self.hash.clone()) + .append(base64::encode(&self.hash)) .build() } } @@ -107,7 +109,7 @@ mod tests { let elem: Element = "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=".parse().unwrap(); let hash = Hash::try_from(elem).unwrap(); assert_eq!(hash.algo, Algo::Sha_256); - assert_eq!(hash.hash, "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU="); + assert_eq!(hash.hash, base64::decode("2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=").unwrap()); } #[test] diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 57cf626cf6ce235ea06be2b761dd0e5ca8677ae3..1a4550cf599f9ea6cbf3d091191fa5045808620b 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -236,6 +236,7 @@ impl Into for Description { mod tests { use super::*; use hashes::Algo; + use base64; #[test] fn test_description() { @@ -260,7 +261,7 @@ mod tests { assert_eq!(desc.file.size, Some(6144u64)); assert_eq!(desc.file.range, None); assert_eq!(desc.file.hashes[0].algo, Algo::Sha_1); - assert_eq!(desc.file.hashes[0].hash, "w0mcJylzCn+AfvuGdqkty2+KP48="); + assert_eq!(desc.file.hashes[0].hash, base64::decode("w0mcJylzCn+AfvuGdqkty2+KP48=").unwrap()); } #[test] @@ -282,6 +283,6 @@ mod tests { assert_eq!(desc.file.size, None); assert_eq!(desc.file.range, None); assert_eq!(desc.file.hashes[0].algo, Algo::Sha_1); - assert_eq!(desc.file.hashes[0].hash, "w0mcJylzCn+AfvuGdqkty2+KP48="); + assert_eq!(desc.file.hashes[0].hash, base64::decode("w0mcJylzCn+AfvuGdqkty2+KP48=").unwrap()); } } From b4e47e9a78136be5404e5e307a28d9c0d43cc90b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 25 May 2017 00:41:13 +0100 Subject: [PATCH 216/698] mam: Improve serialisation. --- src/mam.rs | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/mam.rs b/src/mam.rs index 22ec0576fa4f1d45da3eb2fcf1858269a9c8b83a..c8cd2ac81cdab8fedfc33f9b0fab9ac3b83810b5 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -188,18 +188,13 @@ impl TryFrom for Prefs { impl Into for Query { fn into(self) -> Element { - let mut elem = Element::builder("query") - .ns(ns::MAM) - .attr("queryid", self.queryid.clone()) - .attr("node", self.node.clone()) - .build(); - //if let Some(form) = self.form { - // elem.append_child(form.into()); - //} - if let Some(set) = self.set { - elem.append_child(set.into()); - } - elem + Element::builder("query") + .ns(ns::MAM) + .attr("queryid", self.queryid) + .attr("node", self.node) + //.append(self.form.map(|form| -> Element { form.into() })) + .append(self.set.map(|set| -> Element { set.into() })) + .build() } } @@ -236,7 +231,7 @@ impl Into for Prefs { let mut always = Element::builder("always") .ns(ns::RSM) .build(); - for jid in self.always.clone() { + for jid in self.always { always.append_child(Element::builder("jid") .ns(ns::RSM) .append(String::from(jid)) @@ -248,7 +243,7 @@ impl Into for Prefs { let mut never = Element::builder("never") .ns(ns::RSM) .build(); - for jid in self.never.clone() { + for jid in self.never { never.append_child(Element::builder("jid") .ns(ns::RSM) .append(String::from(jid)) From f08c81382c185b52f737f7b0e9e855485a742917 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 25 May 2017 01:00:17 +0100 Subject: [PATCH 217/698] data_forms, media_element: Implement forgotten serialisation. --- src/data_forms.rs | 85 +++++++++++++++++++++++++++++++++++++++++++- src/media_element.rs | 35 +++++++++++++++++- 2 files changed, 118 insertions(+), 2 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index 21d2ab11db6276b488936edaf54dc4fb4501d054..4aba85287ebc26476545fd7dd2dde4c2a4e2271b 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -7,7 +7,7 @@ use std::convert::TryFrom; use std::str::FromStr; -use minidom::Element; +use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; use error::Error; use ns; @@ -55,12 +55,48 @@ impl FromStr for FieldType { } } +impl IntoAttributeValue for FieldType { + fn into_attribute_value(self) -> Option { + Some(String::from(match self { + FieldType::Boolean => "boolean", + FieldType::Fixed => "fixed", + FieldType::Hidden => "hidden", + FieldType::JidMulti => "jid-multi", + FieldType::JidSingle => "jid-single", + FieldType::ListMulti => "list-multi", + FieldType::ListSingle => "list-single", + FieldType::TextMulti => "text-multi", + FieldType::TextPrivate => "text-private", + FieldType::TextSingle => "text-single", + })) + } +} + #[derive(Debug, Clone)] pub struct Option_ { pub label: Option, pub value: String, } +impl From for Element { + fn from(option: Option_) -> Element { + Element::builder("option") + .ns(ns::DATA_FORMS) + .attr("label", option.label) + .append(Element::builder("value") + .ns(ns::DATA_FORMS) + .append(option.value) + .build()) + .build() + } +} + +impl IntoElements for Option_ { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + #[derive(Debug, Clone)] pub struct Field { pub var: String, @@ -72,6 +108,29 @@ pub struct Field { pub media: Vec, } +impl From for Element { + fn from(field: Field) -> Element { + Element::builder("field") + .ns(ns::DATA_FORMS) + .attr("var", field.var) + .attr("type", field.type_) + .attr("label", field.label) + .append(if field.required { Some(Element::builder("required").ns(ns::DATA_FORMS).build()) } else { None }) + .append(field.options) + .append(field.values.iter().map(|value| { + Element::builder("value").ns(ns::DATA_FORMS).append(value).build() + }).collect::>()) + .append(field.media) + .build() + } +} + +impl IntoElements for Field { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + #[derive(Debug, Clone, PartialEq)] pub enum DataFormType { Cancel, @@ -95,6 +154,17 @@ impl FromStr for DataFormType { } } +impl IntoAttributeValue for DataFormType { + fn into_attribute_value(self) -> Option { + Some(String::from(match self { + DataFormType::Cancel => "cancel", + DataFormType::Form => "form", + DataFormType::Result_ => "result", + DataFormType::Submit => "submit", + })) + } +} + #[derive(Debug, Clone)] pub struct DataForm { pub type_: DataFormType, @@ -226,6 +296,19 @@ impl TryFrom for DataForm { } } +impl From for Element { + fn from(form: DataForm) -> Element { + Element::builder("x") + .ns(ns::DATA_FORMS) + .attr("type", form.type_) + .append(form.form_type) + .append(form.title) + .append(form.instructions) + .append(form.fields) + .build() + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/media_element.rs b/src/media_element.rs index 931cab6f1d59dab90aed45034ed4d9e1e741d0ec..b1c36a64638fe7ee28a8de7ab376e4007a1f3e55 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -6,7 +6,7 @@ use std::convert::TryFrom; -use minidom::Element; +use minidom::{Element, IntoElements, ElementEmitter}; use error::Error; @@ -18,6 +18,22 @@ pub struct URI { pub uri: String, } +impl From for Element { + fn from(uri: URI) -> Element { + Element::builder("uri") + .ns(ns::MEDIA_ELEMENT) + .attr("type", uri.type_) + .append(uri.uri) + .build() + } +} + +impl IntoElements for URI { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + #[derive(Debug, Clone)] pub struct MediaElement { pub width: Option, @@ -54,6 +70,23 @@ impl TryFrom for MediaElement { } } +impl From for Element { + fn from(media: MediaElement) -> Element { + Element::builder("media") + .ns(ns::MEDIA_ELEMENT) + .attr("width", media.width) + .attr("height", media.height) + .append(media.uris) + .build() + } +} + +impl IntoElements for MediaElement { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + #[cfg(test)] mod tests { use super::*; From 56b7785942f61d8dd2a57140dc3e121ebf372765 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 25 May 2017 01:04:51 +0100 Subject: [PATCH 218/698] delay: Remove clones. --- src/delay.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index 08b406c5c7d04b58bf53eeeb73c1cd63a48b4875..19b1a495cdb6e492ce2c979af9eb4295f15593a2 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -48,9 +48,9 @@ impl Into for Delay { fn into(self) -> Element { Element::builder("delay") .ns(ns::DELAY) - .attr("from", self.from.clone().and_then(|value| Some(String::from(value)))) - .attr("stamp", self.stamp.clone()) - .append(self.data.clone()) + .attr("from", self.from.and_then(|value| Some(String::from(value)))) + .attr("stamp", self.stamp) + .append(self.data) .build() } } From 764a7190e97e631041472da8039f16661f274536 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 25 May 2017 01:14:36 +0100 Subject: [PATCH 219/698] stanza_error: Simplify with IntoElements. --- src/jingle.rs | 20 ++++++++++---------- src/stanza_error.rs | 14 ++++++-------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index 06582200f93449d14a9f3681cb7bf3cabd792601..c5041b09adc035c065eb75ec41e26ea5392bca24 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -99,12 +99,12 @@ impl FromStr for Creator { } } -impl From for String { - fn from(creator: Creator) -> String { - String::from(match creator { +impl IntoAttributeValue for Creator { + fn into_attribute_value(self) -> Option { + Some(String::from(match self { Creator::Initiator => "initiator", Creator::Responder => "responder", - }) + })) } } @@ -137,14 +137,14 @@ impl FromStr for Senders { } } -impl From for String { - fn from(senders: Senders) -> String { - String::from(match senders { +impl IntoAttributeValue for Senders { + fn into_attribute_value(self) -> Option { + Some(String::from(match self { Senders::Both => "both", Senders::Initiator => "initiator", Senders::None_ => "none", Senders::Responder => "responder", - }) + })) } } @@ -202,10 +202,10 @@ impl Into for Content { fn into(self) -> Element { Element::builder("content") .ns(ns::JINGLE) - .attr("creator", String::from(self.creator)) + .attr("creator", self.creator) .attr("disposition", self.disposition) .attr("name", self.name) - .attr("senders", String::from(self.senders)) + .attr("senders", self.senders) .append(self.description) .append(self.transport) .append(self.security) diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 0a3275792ad1c3a0ce727408e2963ecef47e7594..3841ad5b49fd6343f4bfefa99294576c4da57ebe 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -8,7 +8,7 @@ use std::convert::TryFrom; use std::str::FromStr; use std::collections::BTreeMap; -use minidom::{Element, IntoAttributeValue}; +use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; use error::Error; use jid::Jid; @@ -110,9 +110,9 @@ impl FromStr for DefinedCondition { } } -impl From for String { - fn from(defined_condition: DefinedCondition) -> String { - String::from(match defined_condition { +impl IntoElements for DefinedCondition { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(Element::builder(match self { DefinedCondition::BadRequest => "bad-request", DefinedCondition::Conflict => "conflict", DefinedCondition::FeatureNotImplemented => "feature-not-implemented", @@ -135,7 +135,7 @@ impl From for String { DefinedCondition::SubscriptionRequired => "subscription-required", DefinedCondition::UndefinedCondition => "undefined-condition", DefinedCondition::UnexpectedRequest => "unexpected-request", - }) + }).ns(ns::XMPP_STANZAS).build()); } } @@ -207,9 +207,7 @@ impl Into for StanzaError { .ns(ns::JABBER_CLIENT) .attr("type", self.type_) .attr("by", self.by.and_then(|by| Some(String::from(by)))) - .append(Element::builder(self.defined_condition) - .ns(ns::XMPP_STANZAS) - .build()) + .append(self.defined_condition) .build(); for (lang, text) in self.texts { let elem = Element::builder("text") From dfdfd8cf712c207e8659e9bee07db36b49bca94c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 25 May 2017 02:34:03 +0100 Subject: [PATCH 220/698] Add a legacy caps parser and serialiser. --- Cargo.toml | 1 + src/caps.rs | 296 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 4 + src/ns.rs | 3 + 4 files changed, 304 insertions(+) create mode 100644 src/caps.rs diff --git a/Cargo.toml b/Cargo.toml index a0d95aa30586e6afa8aa98a760d6069b5770399e..59a8a89b3c1fe930088991a08044100a6e4c9e84 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ minidom = "0.4.1" jid = "0.2.0" base64 = "0.5.0" digest = "0.5.0" +sha-1 = "0.3.0" sha2 = "0.5.0" sha3 = "0.5.0" blake2 = "0.5.0" diff --git a/src/caps.rs b/src/caps.rs new file mode 100644 index 0000000000000000000000000000000000000000..d17cb5018976a18c36279b949f319b31f6df7a4c --- /dev/null +++ b/src/caps.rs @@ -0,0 +1,296 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::convert::TryFrom; + +use disco::{Feature, Identity, Disco}; +use data_forms::DataForm; +use hashes::{Hash, Algo}; + +use minidom::Element; +use error::Error; +use ns; +use base64; + +use sha_1::Sha1; +use sha2::{Sha256, Sha512}; +use sha3::{Sha3_256, Sha3_512}; +use blake2::Blake2b; +use digest::{Digest, VariableOutput}; + +#[derive(Debug, Clone)] +pub struct Caps { + pub ext: Option, + pub node: String, + pub hash: Hash, +} + +impl TryFrom for Caps { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("c", ns::CAPS) { + return Err(Error::ParseError("This is not a caps element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in caps element.")); + } + let hash = get_attr!(elem, "hash", required); + let ver: String = get_attr!(elem, "ver", required); + let hash = Hash { + algo: hash, + hash: base64::decode(&ver)?, + }; + Ok(Caps { + ext: get_attr!(elem, "ext", optional), + node: get_attr!(elem, "node", required), + hash: hash, + }) + } +} + +impl From for Element { + fn from(caps: Caps) -> Element { + Element::builder("c") + .ns(ns::CAPS) + .attr("ext", caps.ext) + .attr("hash", caps.hash.algo) + .attr("node", caps.node) + .attr("ver", base64::encode(&caps.hash.hash)) + .build() + } +} + +fn compute_item(field: &str) -> Vec { + let mut bytes = field.as_bytes().to_vec(); + bytes.push(b'<'); + bytes +} + +fn compute_items Vec>(things: &[T], encode: F) -> Vec { + let mut string: Vec = vec!(); + let mut accumulator: Vec> = vec!(); + for thing in things { + let bytes = encode(thing); + accumulator.push(bytes); + } + // This works using the expected i;octet collation. + accumulator.sort(); + for mut bytes in accumulator { + string.append(&mut bytes); + } + string +} + +fn compute_features(features: &[Feature]) -> Vec { + compute_items(features, |feature| compute_item(&feature.var)) +} + +fn compute_identities(identities: &[Identity]) -> Vec { + compute_items(identities, |identity| { + let string = format!("{}/{}/{}/{}", identity.category, identity.type_, identity.xml_lang, match identity.name { Some(ref name) => name.clone(), None => String::new() }); + let bytes = string.as_bytes(); + let mut vec = Vec::with_capacity(bytes.len()); + vec.extend_from_slice(bytes); + vec.push(b'<'); + vec + }) +} + +fn compute_extensions(extensions: &[DataForm]) -> Vec { + compute_items(extensions, |extension| { + let mut bytes = vec!(); + // TODO: maybe handle the error case? + if let Some(ref form_type) = extension.form_type { + bytes.extend_from_slice(form_type.as_bytes()); + } + bytes.push(b'<'); + for field in extension.fields.clone() { + if field.var == "FORM_TYPE" { + continue; + } + bytes.append(&mut compute_item(&field.var)); + bytes.append(&mut compute_items(&field.values, + |value| compute_item(value))); + } + bytes + }) +} + +pub fn compute_disco(disco: &Disco) -> Vec { + let identities_string = compute_identities(&disco.identities); + let features_string = compute_features(&disco.features); + let extensions_string = compute_extensions(&disco.extensions); + + let mut final_string = vec!(); + final_string.extend(identities_string); + final_string.extend(features_string); + final_string.extend(extensions_string); + final_string +} + +fn get_hash_vec(hash: &[u8]) -> Vec { + let mut vec = Vec::with_capacity(hash.len()); + vec.extend_from_slice(hash); + vec +} + +pub fn hash_caps(data: &[u8], algo: Algo) -> Result { + Ok(Hash { + hash: match algo { + Algo::Sha_1 => { + let mut hasher = Sha1::default(); + hasher.input(data); + let hash = hasher.result(); + get_hash_vec(hash.as_slice()) + }, + Algo::Sha_256 => { + let mut hasher = Sha256::default(); + hasher.input(data); + let hash = hasher.result(); + get_hash_vec(hash.as_slice()) + }, + Algo::Sha_512 => { + let mut hasher = Sha512::default(); + hasher.input(data); + let hash = hasher.result(); + get_hash_vec(hash.as_slice()) + }, + Algo::Sha3_256 => { + let mut hasher = Sha3_256::default(); + hasher.input(data); + let hash = hasher.result(); + get_hash_vec(hash.as_slice()) + }, + Algo::Sha3_512 => { + let mut hasher = Sha3_512::default(); + hasher.input(data); + let hash = hasher.result(); + get_hash_vec(hash.as_slice()) + }, + Algo::Blake2b_256 => { + let mut hasher = Blake2b::default(); + hasher.input(data); + let mut buf: [u8; 32] = [0; 32]; + let hash = hasher.variable_result(&mut buf).unwrap(); + get_hash_vec(hash) + }, + Algo::Blake2b_512 => { + let mut hasher = Blake2b::default(); + hasher.input(data); + let mut buf: [u8; 64] = [0; 64]; + let hash = hasher.variable_result(&mut buf).unwrap(); + get_hash_vec(hash) + }, + Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)), + }, + algo: algo, + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use caps; + use base64; + + #[test] + fn test_parse() { + let elem: Element = "".parse().unwrap(); + let caps = Caps::try_from(elem).unwrap(); + assert_eq!(caps.node, String::from("coucou")); + assert_eq!(caps.hash.algo, Algo::Sha_256); + assert_eq!(caps.hash.hash, base64::decode("K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=").unwrap()); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=".parse().unwrap(); + let error = Caps::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in caps element."); + } + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let disco = Disco::try_from(elem).unwrap(); + let caps = caps::compute_disco(&disco); + assert_eq!(caps.len(), 50); + } + + #[test] + fn test_xep_5_2() { + let elem: Element = r#" + + + + + + + +"#.parse().unwrap(); + + let data = b"client/pc//Exodus 0.9.1 + + + + + + + + + urn:xmpp:dataforms:softwareinfo + + + ipv4 + ipv6 + + + Mac + + + 10.5.1 + + + Psi + + + 0.11 + + + +"#.parse().unwrap(); + let data = b"client/pc/el/\xce\xa8 0.11 Date: Sat, 27 May 2017 12:20:19 +0100 Subject: [PATCH 221/698] idle: Add the chrono dependency to actually parse dates. --- Cargo.toml | 1 + src/error.rs | 8 ++++++ src/idle.rs | 69 +++++++++++++++++++++++++++++++++++++++++++++++----- src/lib.rs | 1 + 4 files changed, 73 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 59a8a89b3c1fe930088991a08044100a6e4c9e84..d8b08268132ac7f972aa4488855be7f673e6d81a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,3 +18,4 @@ sha-1 = "0.3.0" sha2 = "0.5.0" sha3 = "0.5.0" blake2 = "0.5.0" +chrono = "0.3.1" diff --git a/src/error.rs b/src/error.rs index 2f7b254f40da4948bb7812cb7f9b6d9fdc5f7192..899e25f088612459d4bb276277e239e3b772622e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -12,6 +12,7 @@ use std::string; use base64; use minidom; use jid; +use chrono; #[derive(Debug)] pub enum Error { @@ -22,6 +23,7 @@ pub enum Error { ParseIntError(num::ParseIntError), ParseStringError(string::ParseError), JidParseError(jid::JidParseError), + ChronoParseError(chrono::ParseError), } impl From for Error { @@ -59,3 +61,9 @@ impl From for Error { Error::JidParseError(err) } } + +impl From for Error { + fn from(err: chrono::ParseError) -> Error { + Error::ChronoParseError(err) + } +} diff --git a/src/idle.rs b/src/idle.rs index 2800a0e1854b05c1db54970a8ca982eb21f81da2..ba13bd8954d660d0c8740aec392475d3c271eb62 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -7,16 +7,15 @@ use std::convert::TryFrom; use minidom::Element; +use chrono::prelude::*; use error::Error; use ns; -type Date = String; - #[derive(Debug, Clone)] pub struct Idle { - pub since: Date, + pub since: DateTime, } impl TryFrom for Idle { @@ -29,7 +28,7 @@ impl TryFrom for Idle { for _ in elem.children() { return Err(Error::ParseError("Unknown child in idle element.")); } - let since = get_attr!(elem, "since", required); + let since = get_attr!(elem, "since", required, since, DateTime::parse_from_rfc3339(since)?); Ok(Idle { since: since }) } } @@ -38,7 +37,7 @@ impl Into for Idle { fn into(self) -> Element { Element::builder("idle") .ns(ns::IDLE) - .attr("since", self.since.clone()) + .attr("since", self.since.to_rfc3339()) .build() } } @@ -46,6 +45,7 @@ impl Into for Idle { #[cfg(test)] mod tests { use super::*; + use std::error::Error as StdError; #[test] fn test_simple() { @@ -75,10 +75,67 @@ mod tests { assert_eq!(message, "Required attribute 'since' missing."); } + #[test] + fn test_invalid_date() { + // There is no thirteenth month. + let elem: Element = "".parse().unwrap(); + let error = Idle::try_from(elem).unwrap_err(); + let message = match error { + Error::ChronoParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message.description(), "input is out of range"); + + // Timezone ≥24:00 aren’t allowed. + let elem: Element = "".parse().unwrap(); + let error = Idle::try_from(elem).unwrap_err(); + let message = match error { + Error::ChronoParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message.description(), "input is out of range"); + + // Timezone without the : separator aren’t allowed. + let elem: Element = "".parse().unwrap(); + let error = Idle::try_from(elem).unwrap_err(); + let message = match error { + Error::ChronoParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message.description(), "input contains invalid characters"); + + // No seconds, error message could be improved. + let elem: Element = "".parse().unwrap(); + let error = Idle::try_from(elem).unwrap_err(); + let message = match error { + Error::ChronoParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message.description(), "input contains invalid characters"); + + // TODO: maybe we’ll want to support this one, as per XEP-0082 §4. + let elem: Element = "".parse().unwrap(); + let error = Idle::try_from(elem).unwrap_err(); + let message = match error { + Error::ChronoParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message.description(), "input contains invalid characters"); + + // No timezone. + let elem: Element = "".parse().unwrap(); + let error = Idle::try_from(elem).unwrap_err(); + let message = match error { + Error::ChronoParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message.description(), "premature end of input"); + } + #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let idle = Idle { since: Date::from("2017-05-21T20:19:55+01:00") }; + let idle = Idle { since: DateTime::parse_from_rfc3339("2017-05-21T20:19:55+01:00").unwrap() }; let elem2 = idle.into(); assert_eq!(elem, elem2); } diff --git a/src/lib.rs b/src/lib.rs index 363dfec3ebe37ca15bf5a36b6128b7a905fad1c6..50ed56e67d624c853a8c32ed5c32b33e3c0fcbee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,6 +22,7 @@ extern crate sha_1; extern crate sha2; extern crate sha3; extern crate blake2; +extern crate chrono; macro_rules! get_attr { ($elem:ident, $attr:tt, $type:tt) => ( From 357568813df887d74e23614261813ba49eab0070 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 27 May 2017 12:21:32 +0100 Subject: [PATCH 222/698] presence: Wire up legacy caps. --- src/presence.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/presence.rs b/src/presence.rs index 6b4b1ac4e587d4b70ca89fe7da13b925d57858eb..4d0391f1471026d2e08453bd008443032bcaf9bf 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -17,6 +17,7 @@ use error::Error; use ns; use stanza_error::StanzaError; +use caps::Caps; use delay::Delay; use idle::Idle; use ecaps2::ECaps2; @@ -66,6 +67,7 @@ pub type Priority = i8; #[derive(Debug, Clone)] pub enum PresencePayload { StanzaError(StanzaError), + Caps(Caps), Delay(Delay), Idle(Idle), ECaps2(ECaps2), @@ -80,6 +82,9 @@ impl TryFrom for PresencePayload { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { ("error", ns::JABBER_CLIENT) => PresencePayload::StanzaError(StanzaError::try_from(elem)?), + // XEP-0115 + ("c", ns::CAPS) => PresencePayload::Caps(Caps::try_from(elem)?), + // XEP-0203 ("delay", ns::DELAY) => PresencePayload::Delay(Delay::try_from(elem)?), @@ -98,6 +103,7 @@ impl Into for PresencePayload { fn into(self) -> Element { match self { PresencePayload::StanzaError(stanza_error) => stanza_error.into(), + PresencePayload::Caps(caps) => caps.into(), PresencePayload::Delay(delay) => delay.into(), PresencePayload::Idle(idle) => idle.into(), PresencePayload::ECaps2(ecaps2) => ecaps2.into(), From 42235c42fb9dcf123482bc6bf2fe53e19b001ea4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 27 May 2017 12:22:11 +0100 Subject: [PATCH 223/698] hashes: Implement From for String. --- src/hashes.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/hashes.rs b/src/hashes.rs index 3c9a05eba5cdee8c7ff910f6438e93adc85b995e..f311788862e15bbce75d6c3ee9100ca0c1f75361 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -47,9 +47,9 @@ impl FromStr for Algo { } } -impl IntoAttributeValue for Algo { - fn into_attribute_value(self) -> Option { - Some(String::from(match self { +impl From for String { + fn from(algo: Algo) -> String { + String::from(match algo { Algo::Sha_1 => "sha-1", Algo::Sha_256 => "sha-256", Algo::Sha_512 => "sha-512", @@ -57,8 +57,14 @@ impl IntoAttributeValue for Algo { Algo::Sha3_512 => "sha3-512", Algo::Blake2b_256 => "blake2b-256", Algo::Blake2b_512 => "blake2b-512", - Algo::Unknown(text) => return Some(text), - })) + Algo::Unknown(text) => return text, + }) + } +} + +impl IntoAttributeValue for Algo { + fn into_attribute_value(self) -> Option { + Some(String::from(self)) } } From bdaced76037cb8d02139948a2b3a39da89959b8f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 27 May 2017 12:22:50 +0100 Subject: [PATCH 224/698] caps, ecaps2: Add a function to create a Disco query from a hash. --- src/caps.rs | 12 +++++++++++- src/ecaps2.rs | 11 ++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/caps.rs b/src/caps.rs index d17cb5018976a18c36279b949f319b31f6df7a4c..4b50424116762f9cd2d9edc8090a348129f9ec29 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -91,7 +91,8 @@ fn compute_features(features: &[Feature]) -> Vec { fn compute_identities(identities: &[Identity]) -> Vec { compute_items(identities, |identity| { - let string = format!("{}/{}/{}/{}", identity.category, identity.type_, identity.xml_lang, match identity.name { Some(ref name) => name.clone(), None => String::new() }); + let empty = String::new(); + let string = format!("{}/{}/{}/{}", identity.category, identity.type_, identity.xml_lang, match identity.name { Some(ref name) => name, None => &empty }); let bytes = string.as_bytes(); let mut vec = Vec::with_capacity(bytes.len()); vec.extend_from_slice(bytes); @@ -191,6 +192,15 @@ pub fn hash_caps(data: &[u8], algo: Algo) -> Result { }) } +pub fn query_caps(caps: Caps) -> Disco { + Disco { + node: Some(format!("{}#{}", caps.node, base64::encode(&caps.hash.hash))), + identities: vec!(), + features: vec!(), + extensions: vec!(), + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 03f8a40b73d6ea6d8540182aac9d1a7b3dacc77a..09f1516ec57e5c2093249d37b5032307dec0665f 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -13,6 +13,7 @@ use hashes::{Hash, Algo}; use minidom::Element; use error::Error; use ns; +use base64; use sha2::{Sha256, Sha512}; use sha3::{Sha3_256, Sha3_512}; @@ -171,11 +172,19 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { }) } +pub fn query_ecaps2(hash: Hash) -> Disco { + Disco { + node: Some(format!("{}#{}.{}", ns::ECAPS2, String::from(hash.algo), base64::encode(&hash.hash))), + identities: vec!(), + features: vec!(), + extensions: vec!(), + } +} + #[cfg(test)] mod tests { use super::*; use ecaps2; - use base64; #[test] fn test_parse() { From d1a7d222f053ea081b2ec2fb747cb58c40314b30 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 27 May 2017 12:29:21 +0100 Subject: [PATCH 225/698] delay: Use chrono to parse the stamp. --- src/delay.rs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index 19b1a495cdb6e492ce2c979af9eb4295f15593a2..65349040c03faa2f0de16349f02ae7999755612e 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -7,6 +7,7 @@ use std::convert::TryFrom; use minidom::Element; +use chrono::prelude::*; use error::Error; use jid::Jid; @@ -16,7 +17,7 @@ use ns; #[derive(Debug, Clone)] pub struct Delay { pub from: Option, - pub stamp: String, + pub stamp: DateTime, pub data: Option, } @@ -31,7 +32,7 @@ impl TryFrom for Delay { return Err(Error::ParseError("Unknown child in delay element.")); } let from = get_attr!(elem, "from", optional); - let stamp = get_attr!(elem, "stamp", required); + let stamp = get_attr!(elem, "stamp", required, stamp, DateTime::parse_from_rfc3339(stamp)?); let data = match elem.text().as_ref() { "" => None, text => Some(text.to_owned()), @@ -49,7 +50,7 @@ impl Into for Delay { Element::builder("delay") .ns(ns::DELAY) .attr("from", self.from.and_then(|value| Some(String::from(value)))) - .attr("stamp", self.stamp) + .attr("stamp", self.stamp.to_rfc3339()) .append(self.data) .build() } @@ -65,7 +66,14 @@ mod tests { let elem: Element = "".parse().unwrap(); let delay = Delay::try_from(elem).unwrap(); assert_eq!(delay.from, Some(Jid::from_str("capulet.com").unwrap())); - assert_eq!(delay.stamp, "2002-09-10T23:08:25Z"); + assert_eq!(delay.stamp.year(), 2002); + assert_eq!(delay.stamp.month(), 9); + assert_eq!(delay.stamp.day(), 10); + assert_eq!(delay.stamp.hour(), 23); + assert_eq!(delay.stamp.minute(), 08); + assert_eq!(delay.stamp.second(), 25); + assert_eq!(delay.stamp.nanosecond(), 0); + assert_eq!(delay.stamp.timezone(), FixedOffset::east(0)); assert_eq!(delay.data, None); } @@ -93,10 +101,10 @@ mod tests { #[test] fn test_serialise() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); let delay = Delay { from: None, - stamp: "2002-09-10T23:08:25Z".to_owned(), + stamp: DateTime::parse_from_rfc3339("2002-09-10T23:08:25Z").unwrap(), data: None, }; let elem2 = delay.into(); @@ -105,10 +113,10 @@ mod tests { #[test] fn test_serialise_data() { - let elem: Element = "Reason".parse().unwrap(); + let elem: Element = "Reason".parse().unwrap(); let delay = Delay { from: Some(Jid::from_str("juliet@example.org").unwrap()), - stamp: "2002-09-10T23:08:25Z".to_owned(), + stamp: DateTime::parse_from_rfc3339("2002-09-10T23:08:25Z").unwrap(), data: Some(String::from("Reason")), }; let elem2 = delay.into(); From 8e1d5e7983ea580def4ac749e96faffd19f85015 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 27 May 2017 22:10:00 +0100 Subject: [PATCH 226/698] disco: Make xml:lang a proper Option and rename it to lang. --- src/caps.rs | 5 +++-- src/disco.rs | 8 ++++---- src/ecaps2.rs | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/caps.rs b/src/caps.rs index 4b50424116762f9cd2d9edc8090a348129f9ec29..b7753a195ed15fad27bd54ea4c60ae425e79da09 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -91,8 +91,9 @@ fn compute_features(features: &[Feature]) -> Vec { fn compute_identities(identities: &[Identity]) -> Vec { compute_items(identities, |identity| { - let empty = String::new(); - let string = format!("{}/{}/{}/{}", identity.category, identity.type_, identity.xml_lang, match identity.name { Some(ref name) => name, None => &empty }); + let lang = identity.lang.clone().unwrap_or_default(); + let name = identity.name.clone().unwrap_or_default(); + let string = format!("{}/{}/{}/{}", identity.category, identity.type_, lang, name); let bytes = string.as_bytes(); let mut vec = Vec::with_capacity(bytes.len()); vec.extend_from_slice(bytes); diff --git a/src/disco.rs b/src/disco.rs index 46d25dec88161063f9ab0ad17653f22f93856760..486f81e99bcdced741b5e655611257364b5828fd 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -37,7 +37,7 @@ impl IntoElements for Feature { pub struct Identity { pub category: String, // TODO: use an enum here. pub type_: String, // TODO: use an enum here. - pub xml_lang: String, + pub lang: Option, pub name: Option, } @@ -47,7 +47,7 @@ impl Into for Identity { .ns(ns::DISCO_INFO) .attr("category", self.category) .attr("type", self.type_) - .attr("xml:lang", self.xml_lang) + .attr("xml:lang", self.lang) .attr("name", self.name) .build() } @@ -98,12 +98,12 @@ impl TryFrom for Disco { return Err(Error::ParseError("Identity must have a non-empty 'type' attribute.")) } - let lang = get_attr!(child, "xml:lang", default); + let lang = get_attr!(child, "xml:lang", optional); let name = get_attr!(child, "name", optional); identities.push(Identity { category: category, type_: type_, - xml_lang: lang, + lang: lang, name: name, }); } else if child.is("x", ns::DATA_FORMS) { diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 09f1516ec57e5c2093249d37b5032307dec0665f..7d9279bb2d73d1ae88a04b1e7add7559b8398009 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -88,7 +88,7 @@ fn compute_identities(identities: &[Identity]) -> Vec { compute_items(identities, 0x1c, |identity| { let mut bytes = compute_item(&identity.category); bytes.append(&mut compute_item(&identity.type_)); - bytes.append(&mut compute_item(&identity.xml_lang)); + bytes.append(&mut compute_item(&identity.lang.clone().unwrap_or_default())); bytes.append(&mut compute_item(&identity.name.clone().unwrap_or_default())); bytes.push(0x1e); bytes From 6794b347149e661f681db96b2ff5f045d5876121 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 27 May 2017 23:18:15 +0100 Subject: [PATCH 227/698] Cargo.toml: Update minidom to 0.4.3 to use its new IntoAttributeValue implementation on numbers. --- Cargo.toml | 2 +- src/ibb.rs | 4 ++-- src/jingle_ft.rs | 7 ++----- src/jingle_ibb.rs | 2 +- src/jingle_s5b.rs | 4 ++-- src/rsm.rs | 2 +- 6 files changed, 9 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d8b08268132ac7f972aa4488855be7f673e6d81a..700d7f1516d839ad031cc82e080017a3095a89a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ categories = ["parsing", "network-programming"] license = "MPL-2.0" [dependencies] -minidom = "0.4.1" +minidom = "0.4.3" jid = "0.2.0" base64 = "0.5.0" digest = "0.5.0" diff --git a/src/ibb.rs b/src/ibb.rs index 71bf69511aa1bcd4143340dd53b6bff328fad5c9..18cb14e812dc7df3a9058563b690c8ecbd311ae5 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -113,7 +113,7 @@ impl Into for IBB { IBB::Open { block_size, sid, stanza } => { Element::builder("open") .ns(ns::IBB) - .attr("block-size", format!("{}", block_size)) + .attr("block-size", block_size) .attr("sid", sid) .attr("stanza", stanza) .build() @@ -121,7 +121,7 @@ impl Into for IBB { IBB::Data { seq, sid, data } => { Element::builder("data") .ns(ns::IBB) - .attr("seq", format!("{}", seq)) + .attr("seq", seq) .attr("sid", sid) .append(base64::encode(&data)) .build() diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 1a4550cf599f9ea6cbf3d091191fa5045808620b..1d37b6a9dda3576e0f93ca3cfe4a75bf56baadac 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -24,11 +24,8 @@ impl IntoElements for Range { fn into_elements(self, emitter: &mut ElementEmitter) { let mut elem = Element::builder("range") .ns(ns::JINGLE_FT) - .attr("offset", format!("{}", self.offset)) - .attr("length", match self.length { - Some(length) => Some(format!("{}", length)), - None => None - }) + .attr("offset", self.offset) + .attr("length", self.length) .build(); for hash in self.hashes { elem.append_child(hash.into()); diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 6f789d3e969b61a021565ebc8db3d3e29f1421ec..afbf11c3308167ea02b4f0d8e589517434654894 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -43,7 +43,7 @@ impl Into for Transport { fn into(self) -> Element { Element::builder("transport") .ns(ns::JINGLE_IBB) - .attr("block-size", format!("{}", self.block_size)) + .attr("block-size", self.block_size) .attr("sid", self.sid) .attr("stanza", self.stanza) .build() diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 42c188f8152b5a2ab5a7735c270a6f0bc5ea2636..316a32102827f5e27c84621ca2926977028431ba 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -70,8 +70,8 @@ impl Into for Candidate { .attr("cid", self.cid) .attr("host", self.host) .attr("jid", self.jid) - .attr("port", match self.port { Some(port) => Some(format!("{}", port)), None => None }) - .attr("priority", format!("{}", self.priority)) + .attr("port", self.port) + .attr("priority", self.priority) .attr("type", self.type_) .build() } diff --git a/src/rsm.rs b/src/rsm.rs index 21d86ee790ad189768e48f087d4b13636c0e1397..e8d7109d8465dc7867077cfa0375bb5aa0ce2d05 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -103,7 +103,7 @@ impl Into for Set { if self.first.is_some() { elem.append_child(Element::builder("first") .ns(ns::RSM) - .attr("index", self.first_index.map(|index| format!("{}", index))) + .attr("index", self.first_index) .append(self.first).build()); } if self.index.is_some() { From ee34bc1a8667b184efb3f57b42407205da436219 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 28 May 2017 01:47:12 +0100 Subject: [PATCH 228/698] jingle_s5b: Remove unused .to_owned() during serialisation. --- src/jingle_s5b.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 316a32102827f5e27c84621ca2926977028431ba..e244a8c05d51ec77b28e6b15c6dff4382873bdb6 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -221,7 +221,7 @@ impl Into for Transport { TransportPayload::CandidateUsed(ref cid) => { vec!(Element::builder("candidate-used") .ns(ns::JINGLE_S5B) - .attr("cid", cid.to_owned()) + .attr("cid", cid) .build()) }, TransportPayload::ProxyError => { From 07fbc0adf1b4e09af6f534f49c939eee4344ff35 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 28 May 2017 01:47:32 +0100 Subject: [PATCH 229/698] ChangeLog: Add version 0.4.0. --- ChangeLog | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/ChangeLog b/ChangeLog index e8c42e2a450e16e1ee37f80a0ac2d2d6015cb07c..375c06fcb35bd58614f282ff681cc7e2eb50046d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +Version 0.4.0: +2017-05-28 Emmanuel Gil Peyrot + * Incompatible changes: + - Receipts now make the id optional, as per the specification. + - Hashes now expose their raw binary value, instead of staying + base64-encoded. + - Parse dates (XEP-0082) in delayed delivery (XEP-0203) and + last user interaction (XEP-0319), using the chrono crate. + * Improvements: + - Removal of most of the remaining clones, the only ones left + are due to minidom not exposing a draining iterator over the + children. + - Finish to parse all of the attributes using get_attr!(). + - More attribute checks. + - Split more parsers into one parser per element. + - Rely on minidom 0.4.3 to serialise more standard types + automatically. + - Implement forgotten serialisation for data forms (XEP-0004). + - Implement legacy capabilities (XEP-0115) for compatibility + with older software. + Version 0.3.0: 2017-05-23 Emmanuel Gil Peyrot * Big changes: From fcfe1888e2893ee81da804061a184210f9efac6c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 28 May 2017 01:48:03 +0100 Subject: [PATCH 230/698] Release version 0.4.0. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 700d7f1516d839ad031cc82e080017a3095a89a6..84fe4addfdc40ae5956099519135e7634e51e8d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.3.0" +version = "0.4.0" authors = ["Emmanuel Gil Peyrot "] description = "Collection of parsers and serialisers for XMPP extensions" homepage = "https://hg.linkmauve.fr/xmpp-parsers" From aae435c4d95d131aecfd9f78fa621f272131bb7b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 28 May 2017 16:30:43 +0100 Subject: [PATCH 232/698] Add a roster parser/serialiser. --- ChangeLog | 6 ++ src/lib.rs | 3 + src/ns.rs | 3 + src/roster.rs | 286 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 298 insertions(+) create mode 100644 src/roster.rs diff --git a/ChangeLog b/ChangeLog index 375c06fcb35bd58614f282ff681cc7e2eb50046d..63f21648c286ded9afd6544e967e512fa94914ca 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +Version 0.?.?: +2017-??-?? Emmanuel Gil Peyrot + * New parsers/serialisers: + - Implementation of the roster management protocol defined in + RFC 6121 §2. + Version 0.4.0: 2017-05-28 Emmanuel Gil Peyrot * Incompatible changes: diff --git a/src/lib.rs b/src/lib.rs index 50ed56e67d624c853a8c32ed5c32b33e3c0fcbee..c6f11031aaebd1b5ac05a32f3a2296c61f0b91a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,6 +62,9 @@ pub mod iq; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod stanza_error; +/// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence +pub mod roster; + /// XEP-0004: Data Forms pub mod data_forms; diff --git a/src/ns.rs b/src/ns.rs index 99865ae56c929772e5140ff82830de313af969a1..4f563e620f8e99c4fcd2b0269aa091f1c6d355a7 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -9,6 +9,9 @@ pub const JABBER_CLIENT: &'static str = "jabber:client"; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub const XMPP_STANZAS: &'static str = "urn:ietf:params:xml:ns:xmpp-stanzas"; +/// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence +pub const ROSTER: &'static str = "jabber:iq:roster"; + /// XEP-0004: Data Forms pub const DATA_FORMS: &'static str = "jabber:x:data"; diff --git a/src/roster.rs b/src/roster.rs new file mode 100644 index 0000000000000000000000000000000000000000..aaca6d8ef2582a1a489fbaf9ee3f257c86be078b --- /dev/null +++ b/src/roster.rs @@ -0,0 +1,286 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::convert::TryFrom; +use std::str::FromStr; + +use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; +use jid::Jid; + +use error::Error; +use ns; + +type Group = String; + +#[derive(Debug, Clone, PartialEq)] +pub enum Subscription { + None, + From, + To, + Both, + Remove, +} + +impl FromStr for Subscription { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "none" => Subscription::None, + "from" => Subscription::From, + "to" => Subscription::To, + "both" => Subscription::Both, + "remove" => Subscription::Remove, + + _ => return Err(Error::ParseError("Unknown value for attribute 'subscription'.")), + }) + } +} + +impl IntoAttributeValue for Subscription { + fn into_attribute_value(self) -> Option { + Some(String::from(match self { + Subscription::None => "none", + Subscription::From => "from", + Subscription::To => "to", + Subscription::Both => "both", + Subscription::Remove => "remove", + })) + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct Item { + pub jid: Jid, + pub name: Option, + pub subscription: Option, + pub groups: Vec, +} + +impl TryFrom for Item { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("item", ns::ROSTER) { + return Err(Error::ParseError("This is not a roster item element.")); + } + + let mut item = Item { + jid: get_attr!(elem, "jid", required), + name: get_attr!(elem, "name", optional), + subscription: get_attr!(elem, "subscription", optional), + groups: vec!(), + }; + for child in elem.children() { + if !child.is("group", ns::ROSTER) { + return Err(Error::ParseError("Unknown element in roster item element.")); + } + for _ in child.children() { + return Err(Error::ParseError("Roster item group can’t have children.")); + } + item.groups.push(child.text()); + } + Ok(item) + } +} + +impl Into for Item { + fn into(self) -> Element { + Element::builder("item") + .ns(ns::ROSTER) + .attr("jid", String::from(self.jid)) + .attr("name", self.name) + .attr("subscription", self.subscription) + .append(self.groups) + .build() + } +} + +impl IntoElements for Item { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + +#[derive(Debug, Clone)] +pub struct Roster { + pub ver: Option, + pub items: Vec, +} + +impl TryFrom for Roster { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("query", ns::ROSTER) { + return Err(Error::ParseError("This is not a roster element.")); + } + for (attr, _) in elem.attrs() { + if attr != "ver" { + return Err(Error::ParseError("Unknown attribute in roster element.")); + } + } + + let mut roster = Roster { + ver: get_attr!(elem, "ver", optional), + items: vec!(), + }; + for child in elem.children() { + if !child.is("item", ns::ROSTER) { + return Err(Error::ParseError("Unknown element in roster element.")); + } + let item = Item::try_from(child.clone())?; + roster.items.push(item); + } + Ok(roster) + } +} + +impl Into for Roster { + fn into(self) -> Element { + Element::builder("query") + .ns(ns::ROSTER) + .attr("ver", self.ver) + .append(self.items) + .build() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_get() { + let elem: Element = "".parse().unwrap(); + let roster = Roster::try_from(elem).unwrap(); + assert!(roster.ver.is_none()); + assert!(roster.items.is_empty()); + } + + #[test] + fn test_result() { + let elem: Element = "".parse().unwrap(); + let roster = Roster::try_from(elem).unwrap(); + assert_eq!(roster.ver, Some(String::from("ver7"))); + assert_eq!(roster.items.len(), 2); + + let elem: Element = "".parse().unwrap(); + let roster = Roster::try_from(elem).unwrap(); + assert_eq!(roster.ver, Some(String::from("ver9"))); + assert!(roster.items.is_empty()); + + let elem: Element = r#" + + + Friends + + + + +"#.parse().unwrap(); + let roster = Roster::try_from(elem).unwrap(); + assert_eq!(roster.ver, Some(String::from("ver11"))); + assert_eq!(roster.items.len(), 3); + assert_eq!(roster.items[0].jid, Jid::from_str("romeo@example.net").unwrap()); + assert_eq!(roster.items[0].name, Some(String::from("Romeo"))); + assert_eq!(roster.items[0].subscription, Some(Subscription::Both)); + assert_eq!(roster.items[0].groups, vec!(String::from("Friends"))); + } + + #[test] + fn test_set() { + let elem: Element = "".parse().unwrap(); + let roster = Roster::try_from(elem).unwrap(); + assert!(roster.ver.is_none()); + assert_eq!(roster.items.len(), 1); + + let elem: Element = r#" + + + Servants + + + +"#.parse().unwrap(); + let roster = Roster::try_from(elem).unwrap(); + assert!(roster.ver.is_none()); + assert_eq!(roster.items.len(), 1); + assert_eq!(roster.items[0].jid, Jid::from_str("nurse@example.com").unwrap()); + assert_eq!(roster.items[0].name, Some(String::from("Nurse"))); + assert_eq!(roster.items[0].groups.len(), 1); + assert_eq!(roster.items[0].groups[0], String::from("Servants")); + + let elem: Element = r#" + + + +"#.parse().unwrap(); + let roster = Roster::try_from(elem).unwrap(); + assert!(roster.ver.is_none()); + assert_eq!(roster.items.len(), 1); + assert_eq!(roster.items[0].jid, Jid::from_str("nurse@example.com").unwrap()); + assert!(roster.items[0].name.is_none()); + assert!(roster.items[0].groups.is_empty()); + assert_eq!(roster.items[0].subscription, Some(Subscription::Remove)); + } + + #[test] + fn test_invalid() { + let elem: Element = "".parse().unwrap(); + let error = Roster::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown element in roster element."); + + let elem: Element = "".parse().unwrap(); + let error = Roster::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in roster element."); + } + + #[test] + fn test_invalid_item() { + let elem: Element = "".parse().unwrap(); + let error = Roster::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'jid' missing."); + + /* + let elem: Element = "".parse().unwrap(); + let error = Roster::try_from(elem).unwrap_err(); + let error = match error { + Error::JidParseError(error) => error, + _ => panic!(), + }; + assert_eq!(error.description(), "Invalid JID, I guess?"); + */ + + let elem: Element = "".parse().unwrap(); + let error = Roster::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown element in roster item element."); + } +} From 32bfa84551e6f5577a0e0bc9a046507ed1e7112a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 28 May 2017 16:33:43 +0100 Subject: [PATCH 233/698] =?UTF-8?q?presence:=20Rename=20Available=20to=20N?= =?UTF-8?q?one,=20since=20that=E2=80=99s=20what=20it=20is.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/presence.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index 4d0391f1471026d2e08453bd008443032bcaf9bf..1d4ff35ed0c66bde1ffc9ad909bb567d839e8a79 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -117,7 +117,7 @@ impl Into for PresencePayload { pub enum PresenceType { /// This value is not an acceptable 'type' attribute, it is only used /// internally to signal the absence of 'type'. - Available, + None, Error, Probe, Subscribe, @@ -129,7 +129,7 @@ pub enum PresenceType { impl Default for PresenceType { fn default() -> PresenceType { - PresenceType::Available + PresenceType::None } } @@ -154,7 +154,7 @@ impl FromStr for PresenceType { impl IntoAttributeValue for PresenceType { fn into_attribute_value(self) -> Option { Some(match self { - PresenceType::Available => return None, + PresenceType::None => return None, PresenceType::Error => "error", PresenceType::Probe => "probe", @@ -283,7 +283,7 @@ mod tests { assert_eq!(presence.from, None); assert_eq!(presence.to, None); assert_eq!(presence.id, None); - assert_eq!(presence.type_, PresenceType::Available); + assert_eq!(presence.type_, PresenceType::None); assert!(presence.payloads.is_empty()); } From 9eb8f39a38ba23238e4bb60395d748beb0037193 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 28 May 2017 17:10:12 +0100 Subject: [PATCH 234/698] presence: Make Show handle the None case, and rename PresenceType to Type. --- src/presence.rs | 111 +++++++++++++++++++++++++++--------------------- 1 file changed, 62 insertions(+), 49 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index 1d4ff35ed0c66bde1ffc9ad909bb567d839e8a79..b359eb5e9b0eea6ac75da016d05484548554340a 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -8,7 +8,7 @@ use std::convert::TryFrom; use std::str::FromStr; use std::collections::BTreeMap; -use minidom::{Element, IntoAttributeValue}; +use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; use jid::Jid; @@ -24,12 +24,19 @@ use ecaps2::ECaps2; #[derive(Debug, Clone, PartialEq)] pub enum Show { + None, Away, Chat, Dnd, Xa, } +impl Default for Show { + fn default() -> Show { + Show::None + } +} + impl FromStr for Show { type Err = Error; @@ -45,16 +52,21 @@ impl FromStr for Show { } } -impl Into for Show { - fn into(self) -> Element { - Element::builder("show") - .append(match self { - Show::Away => "away", - Show::Chat => "chat", - Show::Dnd => "dnd", - Show::Xa => "xa", - }) - .build() +impl IntoElements for Show { + fn into_elements(self, emitter: &mut ElementEmitter) { + if self == Show::None { + return; + } + emitter.append_child( + Element::builder("show") + .append(match self { + Show::None => unreachable!(), + Show::Away => Some("away"), + Show::Chat => Some("chat"), + Show::Dnd => Some("dnd"), + Show::Xa => Some("xa"), + }) + .build()) } } @@ -114,7 +126,7 @@ impl Into for PresencePayload { } #[derive(Debug, Clone, PartialEq)] -pub enum PresenceType { +pub enum Type { /// This value is not an acceptable 'type' attribute, it is only used /// internally to signal the absence of 'type'. None, @@ -127,42 +139,42 @@ pub enum PresenceType { Unsubscribed, } -impl Default for PresenceType { - fn default() -> PresenceType { - PresenceType::None +impl Default for Type { + fn default() -> Type { + Type::None } } -impl FromStr for PresenceType { +impl FromStr for Type { type Err = Error; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { Ok(match s { - "error" => PresenceType::Error, - "probe" => PresenceType::Probe, - "subscribe" => PresenceType::Subscribe, - "subscribed" => PresenceType::Subscribed, - "unavailable" => PresenceType::Unavailable, - "unsubscribe" => PresenceType::Unsubscribe, - "unsubscribed" => PresenceType::Unsubscribed, + "error" => Type::Error, + "probe" => Type::Probe, + "subscribe" => Type::Subscribe, + "subscribed" => Type::Subscribed, + "unavailable" => Type::Unavailable, + "unsubscribe" => Type::Unsubscribe, + "unsubscribed" => Type::Unsubscribed, _ => return Err(Error::ParseError("Invalid 'type' attribute on presence element.")), }) } } -impl IntoAttributeValue for PresenceType { +impl IntoAttributeValue for Type { fn into_attribute_value(self) -> Option { Some(match self { - PresenceType::None => return None, - - PresenceType::Error => "error", - PresenceType::Probe => "probe", - PresenceType::Subscribe => "subscribe", - PresenceType::Subscribed => "subscribed", - PresenceType::Unavailable => "unavailable", - PresenceType::Unsubscribe => "unsubscribe", - PresenceType::Unsubscribed => "unsubscribed", + Type::None => return None, + + Type::Error => "error", + Type::Probe => "probe", + Type::Subscribe => "subscribe", + Type::Subscribed => "subscribed", + Type::Unavailable => "unavailable", + Type::Unsubscribe => "unsubscribe", + Type::Unsubscribed => "unsubscribed", }.to_owned()) } } @@ -172,8 +184,8 @@ pub struct Presence { pub from: Option, pub to: Option, pub id: Option, - pub type_: PresenceType, - pub show: Option, + pub type_: Type, + pub show: Show, pub statuses: BTreeMap, pub priority: Priority, pub payloads: Vec, @@ -186,20 +198,21 @@ impl TryFrom for Presence { if !root.is("presence", ns::JABBER_CLIENT) { return Err(Error::ParseError("This is not a presence element.")); } + let mut show = None; let mut priority = None; let mut presence = Presence { from: get_attr!(root, "from", optional), to: get_attr!(root, "to", optional), id: get_attr!(root, "id", optional), type_: get_attr!(root, "type", default), - show: None, + show: Show::None, statuses: BTreeMap::new(), priority: 0i8, payloads: vec!(), }; for elem in root.children() { if elem.is("show", ns::JABBER_CLIENT) { - if presence.show.is_some() { + if show.is_some() { return Err(Error::ParseError("More than one show element in a presence.")); } for _ in elem.children() { @@ -208,7 +221,7 @@ impl TryFrom for Presence { for _ in elem.attrs() { return Err(Error::ParseError("Unknown attribute in show element.")); } - presence.show = Some(Show::from_str(elem.text().as_ref())?); + show = Some(Show::from_str(elem.text().as_ref())?); } else if elem.is("status", ns::JABBER_CLIENT) { for _ in elem.children() { return Err(Error::ParseError("Unknown child in status element.")); @@ -237,6 +250,9 @@ impl TryFrom for Presence { presence.payloads.push(elem.clone()); } } + if let Some(show) = show { + presence.show = show; + } if let Some(priority) = priority { presence.priority = priority; } @@ -252,10 +268,7 @@ impl Into for Presence { .attr("to", self.to.and_then(|value| Some(String::from(value)))) .attr("id", self.id) .attr("type", self.type_) - .append(match self.show { - Some(show) => Some({ let elem: Element = show.into(); elem }), - None => None - }) + .append(self.show) .append(self.statuses.iter().map(|(lang, status)| { Element::builder("status") .attr("xml:lang", match lang.as_ref() { @@ -283,7 +296,7 @@ mod tests { assert_eq!(presence.from, None); assert_eq!(presence.to, None); assert_eq!(presence.id, None); - assert_eq!(presence.type_, PresenceType::None); + assert_eq!(presence.type_, Type::None); assert!(presence.payloads.is_empty()); } @@ -294,8 +307,8 @@ mod tests { from: None, to: None, id: None, - type_: PresenceType::Unavailable, - show: None, + type_: Type::Unavailable, + show: Show::None, statuses: BTreeMap::new(), priority: 0i8, payloads: vec!(), @@ -309,7 +322,7 @@ mod tests { let elem: Element = "chat".parse().unwrap(); let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); - assert_eq!(presence.show, Some(Show::Chat)); + assert_eq!(presence.show, Show::Chat); } #[test] @@ -432,8 +445,8 @@ mod tests { from: None, to: None, id: None, - type_: PresenceType::Unavailable, - show: None, + type_: Type::Unavailable, + show: Show::None, statuses: statuses, priority: 0i8, payloads: vec!(), From 073e208f060697e423828a3a872dd27a2ba2032c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 28 May 2017 17:12:46 +0100 Subject: [PATCH 235/698] iq: Wire up Roster. --- src/iq.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/iq.rs b/src/iq.rs index 7ab3309fc74b52cb6c7ed1263146afa76b59da14..76a97b1f217408f2973030d93241ed3120b62d2e 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -17,6 +17,7 @@ use error::Error; use ns; use stanza_error::StanzaError; +use roster::Roster; use disco::Disco; use ibb::IBB; use jingle::Jingle; @@ -26,6 +27,7 @@ use mam::{Query as MamQuery, Fin as MamFin, Prefs as MamPrefs}; /// Lists every known payload of a ``. #[derive(Debug, Clone)] pub enum IqPayload { + Roster(Roster), Disco(Disco), IBB(IBB), Jingle(Jingle), @@ -42,6 +44,9 @@ impl TryFrom for IqPayload { fn try_from(elem: Element) -> Result { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { + // RFC-6121 + ("query", ns::ROSTER) => IqPayload::Roster(Roster::try_from(elem)?), + // XEP-0030 ("query", ns::DISCO_INFO) => IqPayload::Disco(Disco::try_from(elem)?), @@ -165,6 +170,7 @@ impl TryFrom for Iq { impl Into for IqPayload { fn into(self) -> Element { match self { + IqPayload::Roster(roster) => roster.into(), IqPayload::Disco(disco) => disco.into(), IqPayload::IBB(ibb) => ibb.into(), IqPayload::Jingle(jingle) => jingle.into(), From 7395e4b88ffcef26d448f78e36e1e6964bcda26f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 29 May 2017 03:40:34 +0100 Subject: [PATCH 236/698] roster: Make an empty name be None instead. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RFC 6121 §2.4.1: “Implementation Note: Including an empty 'name' attribute is equivalent to including no 'name' attribute; both actions set the name to the empty string.” --- src/roster.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/roster.rs b/src/roster.rs index aaca6d8ef2582a1a489fbaf9ee3f257c86be078b..51e8eb131eb113d99778aa71e591b7229cb24bdd 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -70,7 +70,7 @@ impl TryFrom for Item { let mut item = Item { jid: get_attr!(elem, "jid", required), - name: get_attr!(elem, "name", optional), + name: get_attr!(elem, "name", optional).and_then(|name| if name == "" { None } else { Some(name) }), subscription: get_attr!(elem, "subscription", optional), groups: vec!(), }; From 08ba1640733b6e14a17ef73ea668f5ff3a10b28e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 29 May 2017 03:42:11 +0100 Subject: [PATCH 237/698] roster: Add a test for empty name == no name. --- src/roster.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/roster.rs b/src/roster.rs index 51e8eb131eb113d99778aa71e591b7229cb24bdd..e8dd8ad5609299662148cbb3e78797b693b254df 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -168,6 +168,10 @@ mod tests { assert_eq!(roster.ver, Some(String::from("ver7"))); assert_eq!(roster.items.len(), 2); + let elem2: Element = "".parse().unwrap(); + let roster2 = Roster::try_from(elem2).unwrap(); + assert_eq!(roster.items, roster2.items); + let elem: Element = "".parse().unwrap(); let roster = Roster::try_from(elem).unwrap(); assert_eq!(roster.ver, Some(String::from("ver9"))); From 33994c10425fe0609766a6c2190dbbc787133c57 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 29 May 2017 05:14:49 +0100 Subject: [PATCH 238/698] hashes: Implement Eq and Hash. --- src/hashes.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hashes.rs b/src/hashes.rs index f311788862e15bbce75d6c3ee9100ca0c1f75361..07f2822c479fbe9feb8e06ec93308caaad00920f 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -16,7 +16,7 @@ use ns; use base64; #[allow(non_camel_case_types)] -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Algo { Sha_1, Sha_256, @@ -68,7 +68,7 @@ impl IntoAttributeValue for Algo { } } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Hash { pub algo: Algo, pub hash: Vec, From c4c65281976e68af54a03e885c739c737735321d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Tue, 30 May 2017 22:02:56 +0100 Subject: [PATCH 239/698] Muc parser --- Cargo.toml | 5 ++- src/lib.rs | 4 +++ src/muc.rs | 82 +++++++++++++++++++++++++++++++++++++++++++++++++ src/ns.rs | 4 +++ src/presence.rs | 7 +++++ 5 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 src/muc.rs diff --git a/Cargo.toml b/Cargo.toml index 84fe4addfdc40ae5956099519135e7634e51e8d7..1a93c909c8a33752dadc64aa1a629cd1ffaa7b92 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "xmpp-parsers" version = "0.4.0" -authors = ["Emmanuel Gil Peyrot "] +authors = [ + "Emmanuel Gil Peyrot ", + "Maxime “pep” Buquet ", +] description = "Collection of parsers and serialisers for XMPP extensions" homepage = "https://hg.linkmauve.fr/xmpp-parsers" repository = "https://hg.linkmauve.fr/xmpp-parsers" diff --git a/src/lib.rs b/src/lib.rs index c6f11031aaebd1b5ac05a32f3a2296c61f0b91a0..b5ee57ef1fedd8c683f8580eed44c67de7fb6a16 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ //! back before being sent over the wire. // Copyright (c) 2017 Emmanuel Gil Peyrot +// Copyright (c) 2017 Maxime “pep” Buquet // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -71,6 +72,9 @@ pub mod data_forms; /// XEP-0030: Service Discovery pub mod disco; +/// XEP-0045: Multi-User Chat +pub mod muc; + /// XEP-0047: In-Band Bytestreams pub mod ibb; diff --git a/src/muc.rs b/src/muc.rs new file mode 100644 index 0000000000000000000000000000000000000000..12923fc01d27783dc1ad6f9695523761893f4588 --- /dev/null +++ b/src/muc.rs @@ -0,0 +1,82 @@ +// Copyright (c) 2017 Maxime “pep” Buquet +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::convert::TryFrom; + +use minidom::Element; + +use error::Error; + +use ns; + +#[derive(Debug, Clone)] +pub struct Muc; + +impl TryFrom for Muc { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("x", ns::MUC) { + return Err(Error::ParseError("This is not an x element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in x element.")); + } + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown attribute in x element.")); + } + Ok(Muc) + } +} + +impl Into for Muc { + fn into(self) -> Element { + Element::builder("x") + .ns(ns::MUC) + .build() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + Muc::try_from(elem).unwrap(); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = Muc::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in x element."); + } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let muc = Muc; + let elem2 = muc.into(); + assert_eq!(elem, elem2); + } + + #[test] + fn test_invalid_attribute() { + let elem: Element = "".parse().unwrap(); + let error = Muc::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in x element."); + } +} diff --git a/src/ns.rs b/src/ns.rs index 4f563e620f8e99c4fcd2b0269aa091f1c6d355a7..d7706617e29e1746e338c3ec7c37159a1b1a10f6 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -1,4 +1,5 @@ // Copyright (c) 2017 Emmanuel Gil Peyrot +// Copyright (c) 2017 Maxime “pep” Buquet // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -18,6 +19,9 @@ pub const DATA_FORMS: &'static str = "jabber:x:data"; /// XEP-0030: Service Discovery pub const DISCO_INFO: &'static str = "http://jabber.org/protocol/disco#info"; +/// XEP-0045: Multi-User Chat +pub const MUC: &'static str = "http://jabber.org/protocol/muc"; + /// XEP-0047: In-Band Bytestreams pub const IBB: &'static str = "http://jabber.org/protocol/ibb"; diff --git a/src/presence.rs b/src/presence.rs index b359eb5e9b0eea6ac75da016d05484548554340a..d04a3d2e3a227b956adc440e0cfde59a1e73b83e 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -1,4 +1,5 @@ // Copyright (c) 2017 Emmanuel Gil Peyrot +// Copyright (c) 2017 Maxime “pep” Buquet // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -17,6 +18,7 @@ use error::Error; use ns; use stanza_error::StanzaError; +use muc::Muc; use caps::Caps; use delay::Delay; use idle::Idle; @@ -79,6 +81,7 @@ pub type Priority = i8; #[derive(Debug, Clone)] pub enum PresencePayload { StanzaError(StanzaError), + Muc(Muc), Caps(Caps), Delay(Delay), Idle(Idle), @@ -94,6 +97,9 @@ impl TryFrom for PresencePayload { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { ("error", ns::JABBER_CLIENT) => PresencePayload::StanzaError(StanzaError::try_from(elem)?), + // XEP-0045 + ("x", ns::MUC) => PresencePayload::Muc(Muc::try_from(elem)?), + // XEP-0115 ("c", ns::CAPS) => PresencePayload::Caps(Caps::try_from(elem)?), @@ -115,6 +121,7 @@ impl Into for PresencePayload { fn into(self) -> Element { match self { PresencePayload::StanzaError(stanza_error) => stanza_error.into(), + PresencePayload::Muc(muc) => muc.into(), PresencePayload::Caps(caps) => caps.into(), PresencePayload::Delay(delay) => delay.into(), PresencePayload::Idle(idle) => idle.into(), From bd19341f69a7482176dcede95af4c9e3e6763efe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Wed, 31 May 2017 02:54:47 +0100 Subject: [PATCH 240/698] Muc and parser --- src/muc.rs | 274 ++++++++++++++++++++++++++++++++++++++++++++++++++++- src/ns.rs | 2 + 2 files changed, 271 insertions(+), 5 deletions(-) diff --git a/src/muc.rs b/src/muc.rs index 12923fc01d27783dc1ad6f9695523761893f4588..c8248b7500df034a926f847a7c83bdc2572d82b2 100644 --- a/src/muc.rs +++ b/src/muc.rs @@ -6,7 +6,7 @@ use std::convert::TryFrom; -use minidom::Element; +use minidom::{Element, IntoElements, ElementEmitter}; use error::Error; @@ -40,18 +40,189 @@ impl Into for Muc { } } +#[derive(Debug, Clone, PartialEq)] +pub enum Status { + // 100 + NonAnonymousRoom, + + // 101 + AffiliationChange, + + // 102 + ConfigShowsUnavailableMembers, + + // 103 + ConfigHidesUnavailableMembers, + + // 104 + ConfigNonPrivacyRelated, + + // 110 + SelfPresence, + + // 170 + ConfigRoomLoggingEnabled, + + // 171 + ConfigRoomLoggingDisabled, + + // 172 + ConfigRoomNonAnonymous, + + // 173 + ConfigRoomSemiAnonymous, + + // 201 + RoomHasBeenCreated, + + // 210 + AssignedNick, + + // 301 + Banned, + + // 303 + NewNick, + + // 307 + Kicked, + + // 321 + RemovalFromRoom, + + // 322 + ConfigMembersOnly, + + // 332 + ServiceShutdown, +} + +impl TryFrom for Status { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("status", ns::MUC_USER) { + return Err(Error::ParseError("This is not a status element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in status element.")); + } + for (attr, _) in elem.attrs() { + if attr != "code" { + return Err(Error::ParseError("Unknown attribute in status element.")); + } + } + let code = get_attr!(elem, "code", required); + + Ok(match code { + 100 => Status::NonAnonymousRoom, + 101 => Status::AffiliationChange, + 102 => Status::ConfigShowsUnavailableMembers, + 103 => Status::ConfigHidesUnavailableMembers, + 104 => Status::ConfigNonPrivacyRelated, + 110 => Status::SelfPresence, + 170 => Status::ConfigRoomLoggingEnabled, + 171 => Status::ConfigRoomLoggingDisabled, + 172 => Status::ConfigRoomNonAnonymous, + 173 => Status::ConfigRoomSemiAnonymous, + 201 => Status::RoomHasBeenCreated, + 210 => Status::AssignedNick, + 301 => Status::Banned, + 303 => Status::NewNick, + 307 => Status::Kicked, + 321 => Status::RemovalFromRoom, + 322 => Status::ConfigMembersOnly, + 332 => Status::ServiceShutdown, + _ => return Err(Error::ParseError("Invalid status code.")), + }) + } +} + +impl Into for Status { + fn into(self) -> Element { + Element::builder("status") + .ns(ns::MUC_USER) + .attr("code", match self { + Status::NonAnonymousRoom => 100, + Status::AffiliationChange => 101, + Status::ConfigShowsUnavailableMembers => 102, + Status::ConfigHidesUnavailableMembers => 103, + Status::ConfigNonPrivacyRelated => 104, + Status::SelfPresence => 110, + Status::ConfigRoomLoggingEnabled => 170, + Status::ConfigRoomLoggingDisabled => 171, + Status::ConfigRoomNonAnonymous => 172, + Status::ConfigRoomSemiAnonymous => 173, + Status::RoomHasBeenCreated => 201, + Status::AssignedNick => 210, + Status::Banned => 301, + Status::NewNick => 303, + Status::Kicked => 307, + Status::RemovalFromRoom => 321, + Status::ConfigMembersOnly => 322, + Status::ServiceShutdown => 332, + }) + .build() + } +} + +impl IntoElements for Status { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + +#[derive(Debug, Clone)] +pub struct MucUser { + status: Vec, +} + +impl TryFrom for MucUser { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("x", ns::MUC_USER) { + return Err(Error::ParseError("This is not an x element.")); + } + let mut status = vec!(); + for child in elem.children() { + if child.is("status", ns::MUC_USER) { + status.push(Status::try_from(child.clone())?); + } else { + return Err(Error::ParseError("Unknown child in x element.")); + } + } + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown attribute in x element.")); + } + Ok(MucUser { + status: status, + }) + } +} + +impl Into for MucUser { + fn into(self) -> Element { + Element::builder("x") + .ns(ns::MUC_USER) + .append(self.status) + .build() + } +} + #[cfg(test)] mod tests { use super::*; + use std::error::Error as StdError; #[test] - fn test_simple() { + fn test_muc_simple() { let elem: Element = "".parse().unwrap(); Muc::try_from(elem).unwrap(); } #[test] - fn test_invalid_child() { + fn test_muc_invalid_child() { let elem: Element = "".parse().unwrap(); let error = Muc::try_from(elem).unwrap_err(); let message = match error { @@ -62,7 +233,7 @@ mod tests { } #[test] - fn test_serialise() { + fn test_muc_serialise() { let elem: Element = "".parse().unwrap(); let muc = Muc; let elem2 = muc.into(); @@ -70,7 +241,7 @@ mod tests { } #[test] - fn test_invalid_attribute() { + fn test_muc_invalid_attribute() { let elem: Element = "".parse().unwrap(); let error = Muc::try_from(elem).unwrap_err(); let message = match error { @@ -79,4 +250,97 @@ mod tests { }; assert_eq!(message, "Unknown attribute in x element."); } + + #[test] + fn test_muc_user_simple() { + let elem: Element = "".parse().unwrap(); + MucUser::try_from(elem).unwrap(); + } + + #[test] + fn test_muc_user_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = MucUser::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in x element."); + } + + #[test] + fn test_muc_user_serialise() { + let elem: Element = "".parse().unwrap(); + let muc = MucUser { status: vec!() }; + let elem2 = muc.into(); + assert_eq!(elem, elem2); + } + + #[test] + fn test_muc_user_invalid_attribute() { + let elem: Element = "".parse().unwrap(); + let error = MucUser::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in x element."); + } + + #[test] + fn test_status_simple() { + let elem: Element = "".parse().unwrap(); + Status::try_from(elem).unwrap(); + } + + #[test] + fn test_status_invalid() { + let elem: Element = "".parse().unwrap(); + let error = Status::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'code' missing."); + } + + #[test] + fn test_status_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = Status::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in status element."); + } + + #[test] + fn test_status_simple_code() { + let elem: Element = "".parse().unwrap(); + let status = Status::try_from(elem).unwrap(); + assert_eq!(status, Status::Kicked); + } + + #[test] + fn test_status_invalid_code() { + let elem: Element = "".parse().unwrap(); + let error = Status::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Invalid status code."); + } + + #[test] + fn test_status_invalid_code2() { + let elem: Element = "".parse().unwrap(); + let error = Status::try_from(elem).unwrap_err(); + let error = match error { + Error::ParseIntError(error) => error, + _ => panic!(), + }; + assert_eq!(error.description(), "invalid digit found in string"); + } } diff --git a/src/ns.rs b/src/ns.rs index d7706617e29e1746e338c3ec7c37159a1b1a10f6..149025ac5402cd5cef9514a860c7a95345bdb4e9 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -21,6 +21,8 @@ pub const DISCO_INFO: &'static str = "http://jabber.org/protocol/disco#info"; /// XEP-0045: Multi-User Chat pub const MUC: &'static str = "http://jabber.org/protocol/muc"; +/// XEP-0045: Multi-User Chat +pub const MUC_USER: &'static str = "http://jabber.org/protocol/muc#user"; /// XEP-0047: In-Band Bytestreams pub const IBB: &'static str = "http://jabber.org/protocol/ibb"; From 88a3f507f69a0151be8df5e00e22ae1dbff600e7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 8 Jun 2017 22:46:27 +0200 Subject: [PATCH 241/698] =?UTF-8?q?idle,=20delay:=20Don=E2=80=99t=20import?= =?UTF-8?q?=20*=20from=20chrono::prelude.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/delay.rs | 5 +++-- src/idle.rs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index 65349040c03faa2f0de16349f02ae7999755612e..d744751da9ec01e743ae9358d0a3bf1319a6666b 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -7,7 +7,7 @@ use std::convert::TryFrom; use minidom::Element; -use chrono::prelude::*; +use chrono::{DateTime, FixedOffset}; use error::Error; use jid::Jid; @@ -58,8 +58,9 @@ impl Into for Delay { #[cfg(test)] mod tests { - use std::str::FromStr; use super::*; + use std::str::FromStr; + use chrono::{Datelike, Timelike}; #[test] fn test_simple() { diff --git a/src/idle.rs b/src/idle.rs index ba13bd8954d660d0c8740aec392475d3c271eb62..82009668bfbeed21044f8a7ef0b61f47222d2fd3 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -7,7 +7,7 @@ use std::convert::TryFrom; use minidom::Element; -use chrono::prelude::*; +use chrono::{DateTime, FixedOffset}; use error::Error; From 334f2f78f80d02759befd653b2b3df47c088d76a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 11 Jun 2017 14:42:11 +0100 Subject: [PATCH 242/698] data_forms: Implement IntoElements. --- src/data_forms.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/data_forms.rs b/src/data_forms.rs index 4aba85287ebc26476545fd7dd2dde4c2a4e2271b..70c2d3aa127a0923edb82d68f7eb73566304946a 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -309,6 +309,12 @@ impl From for Element { } } +impl IntoElements for DataForm { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + #[cfg(test)] mod tests { use super::*; From 6c1c9d0851c439274c771f5eb2acb7a012f7515e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 11 Jun 2017 14:48:31 +0100 Subject: [PATCH 243/698] Add a PubSub event parser and serialiser. --- src/lib.rs | 3 + src/ns.rs | 9 + src/pubsub/event.rs | 461 ++++++++++++++++++++++++++++++++++++++++++++ src/pubsub/mod.rs | 9 + 4 files changed, 482 insertions(+) create mode 100644 src/pubsub/event.rs create mode 100644 src/pubsub/mod.rs diff --git a/src/lib.rs b/src/lib.rs index b5ee57ef1fedd8c683f8580eed44c67de7fb6a16..46a2b8ef9c0c2cac2bc338c561eabc3178da839a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -81,6 +81,9 @@ pub mod ibb; /// XEP-0059: Result Set Management pub mod rsm; +/// XEP-0060: Publish-Subscribe +pub mod pubsub; + /// XEP-0085: Chat State Notifications pub mod chatstates; diff --git a/src/ns.rs b/src/ns.rs index 149025ac5402cd5cef9514a860c7a95345bdb4e9..88541e0e75e7c7af1e70b5b9d992508dcbf26ce4 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -30,6 +30,15 @@ pub const IBB: &'static str = "http://jabber.org/protocol/ibb"; /// XEP-0059: Result Set Management pub const RSM: &'static str = "http://jabber.org/protocol/rsm"; +/// XEP-0060: Publish-Subscribe +pub const PUBSUB: &'static str = "http://jabber.org/protocol/pubsub"; +/// XEP-0060: Publish-Subscribe +pub const PUBSUB_ERRORS: &'static str = "http://jabber.org/protocol/pubsub#errors"; +/// XEP-0060: Publish-Subscribe +pub const PUBSUB_EVENT: &'static str = "http://jabber.org/protocol/pubsub#event"; +/// XEP-0060: Publish-Subscribe +pub const PUBSUB_OWNER: &'static str = "http://jabber.org/protocol/pubsub#owner"; + /// XEP-0085: Chat State Notifications pub const CHATSTATES: &'static str = "http://jabber.org/protocol/chatstates"; diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs new file mode 100644 index 0000000000000000000000000000000000000000..47d9a4699cd8033cb3a7ad979bbdc1460f295469 --- /dev/null +++ b/src/pubsub/event.rs @@ -0,0 +1,461 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::convert::TryFrom; +use std::str::FromStr; + +use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; +use jid::Jid; +use chrono::{DateTime, FixedOffset}; + +use error::Error; + +use ns; + +use data_forms::DataForm; + +#[derive(Debug, Clone)] +pub struct Item { + payload: Option, + id: Option, + node: Option, + publisher: Option, +} + +impl From for Element { + fn from(item: Item) -> Element { + Element::builder("item") + .ns(ns::PUBSUB_EVENT) + .attr("id", item.id) + .attr("node", item.node) + .attr("publisher", item.publisher.and_then(|publisher| Some(String::from(publisher)))) + .append(item.payload) + .build() + } +} + +impl IntoElements for Item { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum Subscription { + None, + Pending, + Subscribed, + Unconfigured, +} + +impl Default for Subscription { + fn default() -> Subscription { + Subscription::None + } +} + +impl FromStr for Subscription { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "none" => Subscription::None, + "pending" => Subscription::Pending, + "subscribed" => Subscription::Subscribed, + "unconfigured" => Subscription::Unconfigured, + + _ => return Err(Error::ParseError("Invalid 'subscription' attribute.")), + }) + } +} + +impl IntoAttributeValue for Subscription { + fn into_attribute_value(self) -> Option { + Some(String::from(match self { + Subscription::None => return None, + Subscription::Pending => "pending", + Subscription::Subscribed => "subscribed", + Subscription::Unconfigured => "unconfigured", + })) + } +} + +#[derive(Debug, Clone)] +pub enum PubSubEvent { + /* + Collection { + }, + */ + Configuration { + node: String, + form: Option, + }, + Delete { + node: String, + redirect: Option, + }, + EmptyItems { + node: String, + }, + PublishedItems { + node: String, + items: Vec, + }, + RetractedItems { + node: String, + items: Vec, + }, + Purge { + node: String, + }, + Subscription { + node: String, + expiry: Option>, + jid: Option, + subid: Option, + subscription: Option, + }, +} + +fn parse_items(elem: Element, node: String) -> Result { + let mut is_retract = None; + let mut items = vec!(); + let mut retracts = vec!(); + for child in elem.children() { + if child.is("item", ns::PUBSUB_EVENT) { + match is_retract { + None => is_retract = Some(false), + Some(false) => (), + Some(true) => return Err(Error::ParseError("Mix of item and retract in items element.")), + } + let mut payloads = child.children().cloned().collect::>(); + let payload = payloads.pop(); + if !payloads.is_empty() { + return Err(Error::ParseError("More than a single payload in item element.")); + } + let item = Item { + payload, + id: get_attr!(child, "id", optional), + node: get_attr!(child, "node", optional), + publisher: get_attr!(child, "publisher", optional), + }; + items.push(item); + } else if child.is("retract", ns::PUBSUB_EVENT) { + match is_retract { + None => is_retract = Some(true), + Some(true) => (), + Some(false) => return Err(Error::ParseError("Mix of item and retract in items element.")), + } + for _ in child.children() { + return Err(Error::ParseError("Unknown child in retract element.")); + } + for (attr, _) in child.attrs() { + if attr != "id" { + return Err(Error::ParseError("Unknown attribute in retract element.")); + } + } + let id = get_attr!(child, "id", required); + retracts.push(id); + } else { + return Err(Error::ParseError("Invalid child in items element.")); + } + } + Ok(match is_retract { + None => PubSubEvent::EmptyItems { node }, + Some(false) => PubSubEvent::PublishedItems { node, items }, + Some(true) => PubSubEvent::RetractedItems { node, items: retracts }, + }) +} + +impl TryFrom for PubSubEvent { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("event", ns::PUBSUB_EVENT) { + return Err(Error::ParseError("This is not an event element.")); + } + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown attribute in event element.")); + } + let mut payload = None; + for child in elem.children() { + /* + for (attr, _) in child.attrs() { + if attr != "node" { + return Err(Error::ParseError("Unknown attribute in items element.")); + } + } + */ + let node = get_attr!(child, "node", required); + if child.is("configuration", ns::PUBSUB_EVENT) { + let mut payloads = child.children().cloned().collect::>(); + let item = payloads.pop(); + if !payloads.is_empty() { + return Err(Error::ParseError("More than a single payload in configuration element.")); + } + let form = match item { + None => None, + Some(payload) => Some(DataForm::try_from(payload)?), + }; + payload = Some(PubSubEvent::Configuration { node, form }); + } else if child.is("delete", ns::PUBSUB_EVENT) { + let mut redirect = None; + for item in child.children() { + if item.is("redirect", ns::PUBSUB_EVENT) { + if redirect.is_some() { + return Err(Error::ParseError("More than one redirect in delete element.")); + } + let uri = get_attr!(item, "uri", required); + redirect = Some(uri); + } else { + return Err(Error::ParseError("Unknown child in delete element.")); + } + } + payload = Some(PubSubEvent::Delete { node, redirect }); + } else if child.is("items", ns::PUBSUB_EVENT) { + payload = Some(parse_items(child.clone(), node)?); + } else if child.is("purge", ns::PUBSUB_EVENT) { + for _ in child.children() { + return Err(Error::ParseError("Unknown child in purge element.")); + } + payload = Some(PubSubEvent::Purge { node }); + } else if child.is("subscription", ns::PUBSUB_EVENT) { + for _ in child.children() { + return Err(Error::ParseError("Unknown child in purge element.")); + } + payload = Some(PubSubEvent::Subscription { + node: node, + expiry: get_attr!(child, "expiry", optional), + jid: get_attr!(child, "jid", optional), + subid: get_attr!(child, "subid", optional), + subscription: get_attr!(child, "subscription", optional), + }); + } else { + return Err(Error::ParseError("Unknown child in event element.")); + } + } + Ok(payload.ok_or(Error::ParseError("No payload in event element."))?) + } +} + +impl From for Element { + fn from(event: PubSubEvent) -> Element { + let payload = match event { + PubSubEvent::Configuration { node, form } => { + Element::builder("configuration") + .ns(ns::PUBSUB_EVENT) + .attr("node", node) + .append(form) + .build() + }, + PubSubEvent::Delete { node, redirect } => { + Element::builder("purge") + .ns(ns::PUBSUB_EVENT) + .attr("node", node) + .append(redirect.and_then(|redirect| { + Some(Element::builder("redirect") + .ns(ns::PUBSUB_EVENT) + .attr("uri", redirect) + .build()) + })) + .build() + }, + PubSubEvent::EmptyItems { node } => { + Element::builder("items") + .ns(ns::PUBSUB_EVENT) + .attr("node", node) + .build() + }, + PubSubEvent::PublishedItems { node, items } => { + Element::builder("items") + .ns(ns::PUBSUB_EVENT) + .attr("node", node) + .append(items) + .build() + }, + PubSubEvent::RetractedItems { node, items } => { + Element::builder("items") + .ns(ns::PUBSUB_EVENT) + .attr("node", node) + .append(items) + .build() + }, + PubSubEvent::Purge { node } => { + Element::builder("purge") + .ns(ns::PUBSUB_EVENT) + .attr("node", node) + .build() + }, + PubSubEvent::Subscription { node, expiry, jid, subid, subscription } => { + Element::builder("subscription") + .ns(ns::PUBSUB_EVENT) + .attr("node", node) + .attr("expiry", expiry.and_then(|expiry| Some(expiry.to_rfc3339()))) + .attr("jid", jid.and_then(|jid| Some(String::from(jid)))) + .attr("subid", subid) + .attr("subscription", subscription) + .build() + }, + }; + Element::builder("event") + .ns(ns::PUBSUB_EVENT) + .append(payload) + .build() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::str::FromStr; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let event = PubSubEvent::try_from(elem).unwrap(); + match event { + PubSubEvent::EmptyItems { node } => assert_eq!(node, String::from("coucou")), + _ => panic!(), + } + } + + #[test] + fn test_simple_items() { + let elem: Element = "".parse().unwrap(); + let event = PubSubEvent::try_from(elem).unwrap(); + match event { + PubSubEvent::PublishedItems { node, items } => { + assert_eq!(node, String::from("coucou")); + assert_eq!(items[0].id, Some(String::from("test"))); + assert_eq!(items[0].node, Some(String::from("huh?"))); + assert_eq!(items[0].publisher, Some(Jid::from_str("test@coucou").unwrap())); + assert_eq!(items[0].payload, None); + }, + _ => panic!(), + } + } + + #[test] + fn test_simple_pep() { + let elem: Element = "".parse().unwrap(); + let event = PubSubEvent::try_from(elem).unwrap(); + match event { + PubSubEvent::PublishedItems { node, items } => { + assert_eq!(node, String::from("something")); + assert_eq!(items[0].id, None); + assert_eq!(items[0].node, None); + assert_eq!(items[0].publisher, None); + match items[0].payload { + Some(ref elem) => assert!(elem.is("foreign", "example:namespace")), + _ => panic!(), + } + }, + _ => panic!(), + } + } + + #[test] + fn test_simple_retract() { + let elem: Element = "".parse().unwrap(); + let event = PubSubEvent::try_from(elem).unwrap(); + match event { + PubSubEvent::RetractedItems { node, items } => { + assert_eq!(node, String::from("something")); + assert_eq!(items[0], String::from("coucou")); + assert_eq!(items[1], String::from("test")); + }, + _ => panic!(), + } + } + + #[test] + fn test_simple_delete() { + let elem: Element = "".parse().unwrap(); + let event = PubSubEvent::try_from(elem).unwrap(); + match event { + PubSubEvent::Delete { node, redirect } => { + assert_eq!(node, String::from("coucou")); + assert_eq!(redirect, Some(String::from("hello"))); + }, + _ => panic!(), + } + } + + #[test] + fn test_simple_purge() { + let elem: Element = "".parse().unwrap(); + let event = PubSubEvent::try_from(elem).unwrap(); + match event { + PubSubEvent::Purge { node } => { + assert_eq!(node, String::from("coucou")); + }, + _ => panic!(), + } + } + + #[test] + fn test_simple_configure() { + let elem: Element = "http://jabber.org/protocol/pubsub#node_config".parse().unwrap(); + let event = PubSubEvent::try_from(elem).unwrap(); + match event { + PubSubEvent::Configuration { node, form: _ } => { + assert_eq!(node, String::from("coucou")); + //assert_eq!(form.type_, Result_); + }, + _ => panic!(), + } + } + + #[test] + fn test_invalid() { + let elem: Element = "".parse().unwrap(); + let error = PubSubEvent::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in event element."); + } + + #[test] + fn test_invalid_attribute() { + let elem: Element = "".parse().unwrap(); + let error = PubSubEvent::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in event element."); + } + + #[test] + fn test_ex221_subscription() { + let elem: Element = r#" + + + +"#.parse().unwrap(); + let event = PubSubEvent::try_from(elem.clone()).unwrap(); + match event.clone() { + PubSubEvent::Subscription { node, expiry, jid, subid, subscription } => { + assert_eq!(node, String::from("princely_musings")); + assert_eq!(subid, Some(String::from("ba49252aaa4f5d320c24d3766f0bdcade78c78d3"))); + assert_eq!(subscription, Some(Subscription::Subscribed)); + assert_eq!(jid, Some(Jid::from_str("francisco@denmark.lit").unwrap())); + assert_eq!(expiry, Some("2006-02-28T23:59:59Z".parse().unwrap())); + }, + _ => panic!(), + } + + let elem2: Element = event.into(); + assert_eq!(elem, elem2); + } +} diff --git a/src/pubsub/mod.rs b/src/pubsub/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..9eb939442e863cabae522697569d5da6ac324f00 --- /dev/null +++ b/src/pubsub/mod.rs @@ -0,0 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +pub mod event; + +pub use self::event::PubSubEvent; From 60521298d60de10b444d943551ed3b199444c9b4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 11 Jun 2017 14:57:30 +0100 Subject: [PATCH 244/698] Cargo.toml: Bump base64, improve performances and reduce unsafe code. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 1a93c909c8a33752dadc64aa1a629cd1ffaa7b92..51a4a37b66df2fdcf9f9627078a6ccc55158f23e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ license = "MPL-2.0" [dependencies] minidom = "0.4.3" jid = "0.2.0" -base64 = "0.5.0" +base64 = "0.6.0" digest = "0.5.0" sha-1 = "0.3.0" sha2 = "0.5.0" From 7202a6e1906aa84c0cb9705522cf3480f8ac5046 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 11 Jun 2017 14:58:05 +0100 Subject: [PATCH 245/698] ChangeLog: Add release notes for version 0.5.0. --- ChangeLog | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 63f21648c286ded9afd6544e967e512fa94914ca..d219bf0bcc742eebae08be95e8a41ecf8a3efee7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,8 +1,16 @@ -Version 0.?.?: -2017-??-?? Emmanuel Gil Peyrot +Version 0.5.0: +2017-06-11 Emmanuel Gil Peyrot * New parsers/serialisers: - Implementation of the roster management protocol defined in RFC 6121 §2. + - Implementation of PubSub events (except collections). + - Early implementation of MUC. + * Breaking changes: + - Rename presence enums to make them easier to use. + * Improvements: + - Make hashes comparable and hashable. + - Make data forms embeddable easily into minidom + Element::builder. Version 0.4.0: 2017-05-28 Emmanuel Gil Peyrot From 019848c8647f3c043fd8ac52d322b5ff9a6a013c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 11 Jun 2017 14:58:34 +0100 Subject: [PATCH 246/698] Release version 0.5.0. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 51a4a37b66df2fdcf9f9627078a6ccc55158f23e..68d32e5d16e530ced1da8127c69fd5f10304c230 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.4.0" +version = "0.5.0" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", From 9955c1131bc6a283b769652a817c5c577477c1d9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 12 Jun 2017 22:49:27 +0100 Subject: [PATCH 248/698] ns: Remove now useless 'static lifetime. --- src/ns.rs | 80 +++++++++++++++++++++++++++---------------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/src/ns.rs b/src/ns.rs index 88541e0e75e7c7af1e70b5b9d992508dcbf26ce4..af76dd9050c2e2c0a22fd9704ed6aab4cd2b52e5 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -6,108 +6,108 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core -pub const JABBER_CLIENT: &'static str = "jabber:client"; +pub const JABBER_CLIENT: &str = "jabber:client"; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core -pub const XMPP_STANZAS: &'static str = "urn:ietf:params:xml:ns:xmpp-stanzas"; +pub const XMPP_STANZAS: &str = "urn:ietf:params:xml:ns:xmpp-stanzas"; /// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence -pub const ROSTER: &'static str = "jabber:iq:roster"; +pub const ROSTER: &str = "jabber:iq:roster"; /// XEP-0004: Data Forms -pub const DATA_FORMS: &'static str = "jabber:x:data"; +pub const DATA_FORMS: &str = "jabber:x:data"; /// XEP-0030: Service Discovery -pub const DISCO_INFO: &'static str = "http://jabber.org/protocol/disco#info"; +pub const DISCO_INFO: &str = "http://jabber.org/protocol/disco#info"; /// XEP-0045: Multi-User Chat -pub const MUC: &'static str = "http://jabber.org/protocol/muc"; +pub const MUC: &str = "http://jabber.org/protocol/muc"; /// XEP-0045: Multi-User Chat -pub const MUC_USER: &'static str = "http://jabber.org/protocol/muc#user"; +pub const MUC_USER: &str = "http://jabber.org/protocol/muc#user"; /// XEP-0047: In-Band Bytestreams -pub const IBB: &'static str = "http://jabber.org/protocol/ibb"; +pub const IBB: &str = "http://jabber.org/protocol/ibb"; /// XEP-0059: Result Set Management -pub const RSM: &'static str = "http://jabber.org/protocol/rsm"; +pub const RSM: &str = "http://jabber.org/protocol/rsm"; /// XEP-0060: Publish-Subscribe -pub const PUBSUB: &'static str = "http://jabber.org/protocol/pubsub"; +pub const PUBSUB: &str = "http://jabber.org/protocol/pubsub"; /// XEP-0060: Publish-Subscribe -pub const PUBSUB_ERRORS: &'static str = "http://jabber.org/protocol/pubsub#errors"; +pub const PUBSUB_ERRORS: &str = "http://jabber.org/protocol/pubsub#errors"; /// XEP-0060: Publish-Subscribe -pub const PUBSUB_EVENT: &'static str = "http://jabber.org/protocol/pubsub#event"; +pub const PUBSUB_EVENT: &str = "http://jabber.org/protocol/pubsub#event"; /// XEP-0060: Publish-Subscribe -pub const PUBSUB_OWNER: &'static str = "http://jabber.org/protocol/pubsub#owner"; +pub const PUBSUB_OWNER: &str = "http://jabber.org/protocol/pubsub#owner"; /// XEP-0085: Chat State Notifications -pub const CHATSTATES: &'static str = "http://jabber.org/protocol/chatstates"; +pub const CHATSTATES: &str = "http://jabber.org/protocol/chatstates"; /// XEP-0115: Entity Capabilities -pub const CAPS: &'static str = "http://jabber.org/protocol/caps"; +pub const CAPS: &str = "http://jabber.org/protocol/caps"; /// XEP-0166: Jingle -pub const JINGLE: &'static str = "urn:xmpp:jingle:1"; +pub const JINGLE: &str = "urn:xmpp:jingle:1"; /// XEP-0184: Message Delivery Receipts -pub const RECEIPTS: &'static str = "urn:xmpp:receipts"; +pub const RECEIPTS: &str = "urn:xmpp:receipts"; /// XEP-0199: XMPP Ping -pub const PING: &'static str = "urn:xmpp:ping"; +pub const PING: &str = "urn:xmpp:ping"; /// XEP-0203: Delayed Delivery -pub const DELAY: &'static str = "urn:xmpp:delay"; +pub const DELAY: &str = "urn:xmpp:delay"; /// XEP-0221: Data Forms Media Element -pub const MEDIA_ELEMENT: &'static str = "urn:xmpp:media-element"; +pub const MEDIA_ELEMENT: &str = "urn:xmpp:media-element"; /// XEP-0224: Attention -pub const ATTENTION: &'static str = "urn:xmpp:attention:0"; +pub const ATTENTION: &str = "urn:xmpp:attention:0"; /// XEP-0234: Jingle File Transfer -pub const JINGLE_FT: &'static str = "urn:xmpp:jingle:apps:file-transfer:5"; +pub const JINGLE_FT: &str = "urn:xmpp:jingle:apps:file-transfer:5"; /// XEP-0234: Jingle File Transfer -pub const JINGLE_FT_ERROR: &'static str = "urn:xmpp:jingle:apps:file-transfer:errors:0"; +pub const JINGLE_FT_ERROR: &str = "urn:xmpp:jingle:apps:file-transfer:errors:0"; /// XEP-0260: Jingle SOCKS5 Bytestreams Transport Method -pub const JINGLE_S5B: &'static str = "urn:xmpp:jingle:transports:s5b:1"; +pub const JINGLE_S5B: &str = "urn:xmpp:jingle:transports:s5b:1"; /// XEP-0261: Jingle In-Band Bytestreams Transport Method -pub const JINGLE_IBB: &'static str = "urn:xmpp:jingle:transports:ibb:1"; +pub const JINGLE_IBB: &str = "urn:xmpp:jingle:transports:ibb:1"; /// XEP-0297: Stanza Forwarding -pub const FORWARD: &'static str = "urn:xmpp:forward:0"; +pub const FORWARD: &str = "urn:xmpp:forward:0"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP -pub const HASHES: &'static str = "urn:xmpp:hashes:2"; +pub const HASHES: &str = "urn:xmpp:hashes:2"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP -pub const HASH_ALGO_SHA_256: &'static str = "urn:xmpp:hash-function-text-names:sha-256"; +pub const HASH_ALGO_SHA_256: &str = "urn:xmpp:hash-function-text-names:sha-256"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP -pub const HASH_ALGO_SHA_512: &'static str = "urn:xmpp:hash-function-text-names:sha-512"; +pub const HASH_ALGO_SHA_512: &str = "urn:xmpp:hash-function-text-names:sha-512"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP -pub const HASH_ALGO_SHA3_256: &'static str = "urn:xmpp:hash-function-text-names:sha3-256"; +pub const HASH_ALGO_SHA3_256: &str = "urn:xmpp:hash-function-text-names:sha3-256"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP -pub const HASH_ALGO_SHA3_512: &'static str = "urn:xmpp:hash-function-text-names:sha3-512"; +pub const HASH_ALGO_SHA3_512: &str = "urn:xmpp:hash-function-text-names:sha3-512"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP -pub const HASH_ALGO_BLAKE2B_256: &'static str = "urn:xmpp:hash-function-text-names:id-blake2b256"; +pub const HASH_ALGO_BLAKE2B_256: &str = "urn:xmpp:hash-function-text-names:id-blake2b256"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP -pub const HASH_ALGO_BLAKE2B_512: &'static str = "urn:xmpp:hash-function-text-names:id-blake2b512"; +pub const HASH_ALGO_BLAKE2B_512: &str = "urn:xmpp:hash-function-text-names:id-blake2b512"; /// XEP-0308: Last Message Correction -pub const MESSAGE_CORRECT: &'static str = "urn:xmpp:message-correct:0"; +pub const MESSAGE_CORRECT: &str = "urn:xmpp:message-correct:0"; /// XEP-0313: Message Archive Management -pub const MAM: &'static str = "urn:xmpp:mam:2"; +pub const MAM: &str = "urn:xmpp:mam:2"; /// XEP-0319: Last User Interaction in Presence -pub const IDLE: &'static str = "urn:xmpp:idle:1"; +pub const IDLE: &str = "urn:xmpp:idle:1"; /// XEP-0359: Unique and Stable Stanza IDs -pub const SID: &'static str = "urn:xmpp:sid:0"; +pub const SID: &str = "urn:xmpp:sid:0"; /// XEP-0380: Explicit Message Encryption -pub const EME: &'static str = "urn:xmpp:eme:0"; +pub const EME: &str = "urn:xmpp:eme:0"; /// XEP-0390: Entity Capabilities 2.0 -pub const ECAPS2: &'static str = "urn:xmpp:caps"; +pub const ECAPS2: &str = "urn:xmpp:caps"; /// XEP-0390: Entity Capabilities 2.0 -pub const ECAPS2_OPTIMIZE: &'static str = "urn:xmpp:caps:optimize"; +pub const ECAPS2_OPTIMIZE: &str = "urn:xmpp:caps:optimize"; From 0f297d2d2d3a19587d41e0948313930608bf29d8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 14 Jun 2017 00:50:57 +0100 Subject: [PATCH 249/698] Add a generate_attribute! macro, and use it for the common case. --- src/data_forms.rs | 102 +++++---------------------- src/ibb.rs | 37 ++-------- src/jingle.rs | 165 +++++++++----------------------------------- src/jingle_s5b.rs | 82 +++------------------- src/lib.rs | 60 ++++++++++++++++ src/mam.rs | 35 ++-------- src/message.rs | 43 ++---------- src/pubsub/event.rs | 45 ++---------- src/roster.rs | 43 ++---------- src/stanza_error.rs | 45 +++--------- 10 files changed, 158 insertions(+), 499 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index 70c2d3aa127a0923edb82d68f7eb73566304946a..0899cc5ca88df9b01cab812eefe54bf9f3e7e0ad 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -14,19 +14,18 @@ use ns; use media_element::MediaElement; -#[derive(Debug, Clone, PartialEq)] -pub enum FieldType { - Boolean, - Fixed, - Hidden, - JidMulti, - JidSingle, - ListMulti, - ListSingle, - TextMulti, - TextPrivate, - TextSingle, -} +generate_attribute!(FieldType, "type", { + Boolean => "boolean", + Fixed => "fixed", + Hidden => "hidden", + JidMulti => "jid-multi", + JidSingle => "jid-single", + ListMulti => "list-multi", + ListSingle => "list-single", + TextMulti => "text-multi", + TextPrivate => "text-private", + TextSingle => "text-single", +}); impl Default for FieldType { fn default() -> FieldType { @@ -34,44 +33,6 @@ impl Default for FieldType { } } -impl FromStr for FieldType { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "boolean" => FieldType::Boolean, - "fixed" => FieldType::Fixed, - "hidden" => FieldType::Hidden, - "jid-multi" => FieldType::JidMulti, - "jid-single" => FieldType::JidSingle, - "list-multi" => FieldType::ListMulti, - "list-single" => FieldType::ListSingle, - "text-multi" => FieldType::TextMulti, - "text-private" => FieldType::TextPrivate, - "text-single" => FieldType::TextSingle, - - _ => return Err(Error::ParseError("Invalid 'type' attribute in field element.")), - }) - } -} - -impl IntoAttributeValue for FieldType { - fn into_attribute_value(self) -> Option { - Some(String::from(match self { - FieldType::Boolean => "boolean", - FieldType::Fixed => "fixed", - FieldType::Hidden => "hidden", - FieldType::JidMulti => "jid-multi", - FieldType::JidSingle => "jid-single", - FieldType::ListMulti => "list-multi", - FieldType::ListSingle => "list-single", - FieldType::TextMulti => "text-multi", - FieldType::TextPrivate => "text-private", - FieldType::TextSingle => "text-single", - })) - } -} - #[derive(Debug, Clone)] pub struct Option_ { pub label: Option, @@ -131,39 +92,12 @@ impl IntoElements for Field { } } -#[derive(Debug, Clone, PartialEq)] -pub enum DataFormType { - Cancel, - Form, - Result_, - Submit, -} - -impl FromStr for DataFormType { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "cancel" => DataFormType::Cancel, - "form" => DataFormType::Form, - "result" => DataFormType::Result_, - "submit" => DataFormType::Submit, - - _ => return Err(Error::ParseError("Unknown data form type.")), - }) - } -} - -impl IntoAttributeValue for DataFormType { - fn into_attribute_value(self) -> Option { - Some(String::from(match self { - DataFormType::Cancel => "cancel", - DataFormType::Form => "form", - DataFormType::Result_ => "result", - DataFormType::Submit => "submit", - })) - } -} +generate_attribute!(Subscription, "subscription", { + Cancel => "cancel", + Form => "form", + Result_ => "result", + Submit => "submit", +}); #[derive(Debug, Clone)] pub struct DataForm { diff --git a/src/ibb.rs b/src/ibb.rs index 18cb14e812dc7df3a9058563b690c8ecbd311ae5..5463caae809975377e084151a4ac661a63f1a8ba 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -14,39 +14,10 @@ use error::Error; use ns; -#[derive(Debug, Clone, PartialEq)] -pub enum Stanza { - Iq, - Message, -} - -impl Default for Stanza { - fn default() -> Stanza { - Stanza::Iq - } -} - -impl FromStr for Stanza { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "iq" => Stanza::Iq, - "message" => Stanza::Message, - - _ => return Err(Error::ParseError("Invalid 'stanza' attribute.")), - }) - } -} - -impl IntoAttributeValue for Stanza { - fn into_attribute_value(self) -> Option { - match self { - Stanza::Iq => None, - Stanza::Message => Some(String::from("message")), - } - } -} +generate_attribute!(Stanza, "stanza", { + Iq => "iq", + Message => "message", +}, Default = Iq); #[derive(Debug, Clone)] pub enum IBB { diff --git a/src/jingle.rs b/src/jingle.rs index c5041b09adc035c065eb75ec41e26ea5392bca24..e5fab352b53c491151c0045476fd4e534efda515 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -13,108 +13,35 @@ use jid::Jid; use error::Error; use ns; -#[derive(Debug, Clone, PartialEq)] -pub enum Action { - ContentAccept, - ContentAdd, - ContentModify, - ContentReject, - ContentRemove, - DescriptionInfo, - SecurityInfo, - SessionAccept, - SessionInfo, - SessionInitiate, - SessionTerminate, - TransportAccept, - TransportInfo, - TransportReject, - TransportReplace, -} - -impl FromStr for Action { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "content-accept" => Action::ContentAccept, - "content-add" => Action::ContentAdd, - "content-modify" => Action::ContentModify, - "content-reject" => Action::ContentReject, - "content-remove" => Action::ContentRemove, - "description-info" => Action::DescriptionInfo, - "security-info" => Action::SecurityInfo, - "session-accept" => Action::SessionAccept, - "session-info" => Action::SessionInfo, - "session-initiate" => Action::SessionInitiate, - "session-terminate" => Action::SessionTerminate, - "transport-accept" => Action::TransportAccept, - "transport-info" => Action::TransportInfo, - "transport-reject" => Action::TransportReject, - "transport-replace" => Action::TransportReplace, - - _ => return Err(Error::ParseError("Unknown action.")), - }) - } -} - -impl IntoAttributeValue for Action { - fn into_attribute_value(self) -> Option { - Some(String::from(match self { - Action::ContentAccept => "content-accept", - Action::ContentAdd => "content-add", - Action::ContentModify => "content-modify", - Action::ContentReject => "content-reject", - Action::ContentRemove => "content-remove", - Action::DescriptionInfo => "description-info", - Action::SecurityInfo => "security-info", - Action::SessionAccept => "session-accept", - Action::SessionInfo => "session-info", - Action::SessionInitiate => "session-initiate", - Action::SessionTerminate => "session-terminate", - Action::TransportAccept => "transport-accept", - Action::TransportInfo => "transport-info", - Action::TransportReject => "transport-reject", - Action::TransportReplace => "transport-replace", - })) - } -} - -#[derive(Debug, Clone, PartialEq)] -pub enum Creator { - Initiator, - Responder, -} - -impl FromStr for Creator { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "initiator" => Creator::Initiator, - "responder" => Creator::Responder, - - _ => return Err(Error::ParseError("Unknown creator.")), - }) - } -} - -impl IntoAttributeValue for Creator { - fn into_attribute_value(self) -> Option { - Some(String::from(match self { - Creator::Initiator => "initiator", - Creator::Responder => "responder", - })) - } -} - -#[derive(Debug, Clone, PartialEq)] -pub enum Senders { - Both, - Initiator, - None_, - Responder, -} +generate_attribute!(Action, "action", { + ContentAccept => "content-accept", + ContentAdd => "content-add", + ContentModify => "content-modify", + ContentReject => "content-reject", + ContentRemove => "content-remove", + DescriptionInfo => "description-info", + SecurityInfo => "security-info", + SessionAccept => "session-accept", + SessionInfo => "session-info", + SessionInitiate => "session-initiate", + SessionTerminate => "session-terminate", + TransportAccept => "transport-accept", + TransportInfo => "transport-info", + TransportReject => "transport-reject", + TransportReplace => "transport-replace", +}); + +generate_attribute!(Creator, "creator", { + Initiator => "initiator", + Responder => "responder", +}); + +generate_attribute!(Senders, "senders", { + Both => "both", + Initiator => "initiator", + None => "none", + Responder => "responder", +}); impl Default for Senders { fn default() -> Senders { @@ -122,32 +49,6 @@ impl Default for Senders { } } -impl FromStr for Senders { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "both" => Senders::Both, - "initiator" => Senders::Initiator, - "none" => Senders::None_, - "responder" => Senders::Responder, - - _ => return Err(Error::ParseError("Unknown senders.")), - }) - } -} - -impl IntoAttributeValue for Senders { - fn into_attribute_value(self) -> Option { - Some(String::from(match self { - Senders::Both => "both", - Senders::Initiator => "initiator", - Senders::None_ => "none", - Senders::Responder => "responder", - })) - } -} - #[derive(Debug, Clone)] pub struct Content { pub creator: Creator, @@ -448,7 +349,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown action."); + assert_eq!(message, "Unknown value for 'action' attribute."); } #[test] @@ -493,7 +394,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown creator."); + assert_eq!(message, "Unknown value for 'creator' attribute."); let elem: Element = "".parse().unwrap(); let error = Jingle::try_from(elem).unwrap_err(); @@ -501,7 +402,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown senders."); + assert_eq!(message, "Unknown value for 'senders' attribute."); let elem: Element = "".parse().unwrap(); let error = Jingle::try_from(elem).unwrap_err(); @@ -509,7 +410,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown senders."); + assert_eq!(message, "Unknown value for 'senders' attribute."); } #[test] diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index e244a8c05d51ec77b28e6b15c6dff4382873bdb6..089d16056d4530195a344c992fdc4a6b39804362 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -13,45 +13,12 @@ use error::Error; use ns; -#[derive(Debug, Clone, PartialEq)] -pub enum Type { - Assisted, - Direct, - Proxy, - Tunnel, -} - -impl Default for Type { - fn default() -> Type { - Type::Direct - } -} - -impl FromStr for Type { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "assisted" => Type::Assisted, - "direct" => Type::Direct, - "proxy" => Type::Proxy, - "tunnel" => Type::Tunnel, - - _ => return Err(Error::ParseError("Invalid 'type' attribute in candidate element.")), - }) - } -} - -impl IntoAttributeValue for Type { - fn into_attribute_value(self) -> Option { - Some(match self { - Type::Assisted => String::from("assisted"), - Type::Direct => return None, - Type::Proxy => String::from("proxy"), - Type::Tunnel => String::from("tunnel"), - }) - } -} +generate_attribute!(Type, "type", { + Assisted => "assisted", + Direct => "direct", + Proxy => "proxy", + Tunnel => "tunnel", +}, Default = Direct); #[derive(Debug, Clone)] pub struct Candidate { @@ -77,39 +44,10 @@ impl Into for Candidate { } } -#[derive(Debug, Clone, PartialEq)] -pub enum Mode { - Tcp, - Udp, -} - -impl Default for Mode { - fn default() -> Mode { - Mode::Tcp - } -} - -impl FromStr for Mode { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "tcp" => Mode::Tcp, - "udp" => Mode::Udp, - - _ => return Err(Error::ParseError("Invalid 'mode' attribute.")), - }) - } -} - -impl IntoAttributeValue for Mode { - fn into_attribute_value(self) -> Option { - match self { - Mode::Tcp => None, - Mode::Udp => Some(String::from("udp")), - } - } -} +generate_attribute!(Mode, "mode", { + Tcp => "tcp", + Udp => "udp", +}, Default = Tcp); #[derive(Debug, Clone)] pub enum TransportPayload { diff --git a/src/lib.rs b/src/lib.rs index 46a2b8ef9c0c2cac2bc338c561eabc3178da839a..4fc420b82108ff51a0d759f799b8b6bf4275c183 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,6 +49,66 @@ macro_rules! get_attr { ); } +macro_rules! generate_attribute { + ($elem:ident, $name:tt, {$($a:ident => $b:tt),+,}) => ( + generate_attribute!($elem, $name, {$($a => $b),+}); + ); + ($elem:ident, $name:tt, {$($a:ident => $b:tt),+,}, Default = $default:ident) => ( + generate_attribute!($elem, $name, {$($a => $b),+}, Default = $default); + ); + ($elem:ident, $name:tt, {$($a:ident => $b:tt),+}) => ( + #[derive(Debug, Clone, PartialEq)] + pub enum $elem { + $($a),+ + } + impl FromStr for $elem { + type Err = Error; + fn from_str(s: &str) -> Result<$elem, Error> { + Ok(match s { + $($b => $elem::$a),+, + _ => return Err(Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + }) + } + } + impl IntoAttributeValue for $elem { + fn into_attribute_value(self) -> Option { + Some(String::from(match self { + $($elem::$a => $b),+ + })) + } + } + ); + ($elem:ident, $name:tt, {$($a:ident => $b:tt),+}, Default = $default:ident) => ( + #[derive(Debug, Clone, PartialEq)] + pub enum $elem { + $($a),+ + } + impl FromStr for $elem { + type Err = Error; + fn from_str(s: &str) -> Result<$elem, Error> { + Ok(match s { + $($b => $elem::$a),+, + _ => return Err(Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + }) + } + } + impl IntoAttributeValue for $elem { + #[allow(unreachable_patterns)] + fn into_attribute_value(self) -> Option { + Some(String::from(match self { + $elem::$default => return None, + $($elem::$a => $b),+ + })) + } + } + impl Default for $elem { + fn default() -> $elem { + $elem::$default + } + } + ); +} + /// Error type returned by every parser on failure. pub mod error; /// XML namespace definitions used through XMPP. diff --git a/src/mam.rs b/src/mam.rs index c8cd2ac81cdab8fedfc33f9b0fab9ac3b83810b5..920cf8d01a76ae44d95839ad658405e99372dcd6 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -39,36 +39,11 @@ pub struct Fin { pub set: Set, } -#[derive(Debug, Clone)] -pub enum DefaultPrefs { - Always, - Never, - Roster, -} - -impl FromStr for DefaultPrefs { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "always" => DefaultPrefs::Always, - "never" => DefaultPrefs::Never, - "roster" => DefaultPrefs::Roster, - - _ => return Err(Error::ParseError("Invalid 'default' attribute.")), - }) - } -} - -impl IntoAttributeValue for DefaultPrefs { - fn into_attribute_value(self) -> Option { - Some(String::from(match self { - DefaultPrefs::Always => "always", - DefaultPrefs::Never => "never", - DefaultPrefs::Roster => "roster", - })) - } -} +generate_attribute!(DefaultPrefs, "default", { + Always => "always", + Never => "never", + Roster => "roster", +}); #[derive(Debug, Clone)] pub struct Prefs { diff --git a/src/message.rs b/src/message.rs index 13caed356d623307feedd049e2c79a7ca46c2f5f..4efabbdba1c6ff71c8325bb768d68fdf0ec3349a 100644 --- a/src/message.rs +++ b/src/message.rs @@ -102,14 +102,13 @@ impl Into for MessagePayload { } } -#[derive(Debug, Clone, PartialEq)] -pub enum MessageType { - Chat, - Error, - Groupchat, - Headline, - Normal, -} +generate_attribute!(MessageType, "type", { + Chat => "chat", + Error => "error", + Groupchat => "groupchat", + Headline => "headline", + Normal => "normal", +}); impl Default for MessageType { fn default() -> MessageType { @@ -117,34 +116,6 @@ impl Default for MessageType { } } -impl FromStr for MessageType { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "chat" => MessageType::Chat, - "error" => MessageType::Error, - "groupchat" => MessageType::Groupchat, - "headline" => MessageType::Headline, - "normal" => MessageType::Normal, - - _ => return Err(Error::ParseError("Invalid 'type' attribute on message element.")), - }) - } -} - -impl IntoAttributeValue for MessageType { - fn into_attribute_value(self) -> Option { - Some(match self { - MessageType::Chat => "chat", - MessageType::Error => "error", - MessageType::Groupchat => "groupchat", - MessageType::Headline => "headline", - MessageType::Normal => "normal", - }.to_owned()) - } -} - type Lang = String; type Body = String; type Subject = String; diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 47d9a4699cd8033cb3a7ad979bbdc1460f295469..07e465c89a59dc3c7bdc3af520b1f787751dd588 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -43,45 +43,12 @@ impl IntoElements for Item { } } -#[derive(Debug, Clone, PartialEq)] -pub enum Subscription { - None, - Pending, - Subscribed, - Unconfigured, -} - -impl Default for Subscription { - fn default() -> Subscription { - Subscription::None - } -} - -impl FromStr for Subscription { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "none" => Subscription::None, - "pending" => Subscription::Pending, - "subscribed" => Subscription::Subscribed, - "unconfigured" => Subscription::Unconfigured, - - _ => return Err(Error::ParseError("Invalid 'subscription' attribute.")), - }) - } -} - -impl IntoAttributeValue for Subscription { - fn into_attribute_value(self) -> Option { - Some(String::from(match self { - Subscription::None => return None, - Subscription::Pending => "pending", - Subscription::Subscribed => "subscribed", - Subscription::Unconfigured => "unconfigured", - })) - } -} +generate_attribute!(Subscription, "subscription", { + None => "none", + Pending => "pending", + Subscribed => "subscribed", + Unconfigured => "unconfigured", +}, Default = None); #[derive(Debug, Clone)] pub enum PubSubEvent { diff --git a/src/roster.rs b/src/roster.rs index e8dd8ad5609299662148cbb3e78797b693b254df..f38817d0794290d833ed4dcd64cee1cd75d286d1 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -15,42 +15,13 @@ use ns; type Group = String; -#[derive(Debug, Clone, PartialEq)] -pub enum Subscription { - None, - From, - To, - Both, - Remove, -} - -impl FromStr for Subscription { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "none" => Subscription::None, - "from" => Subscription::From, - "to" => Subscription::To, - "both" => Subscription::Both, - "remove" => Subscription::Remove, - - _ => return Err(Error::ParseError("Unknown value for attribute 'subscription'.")), - }) - } -} - -impl IntoAttributeValue for Subscription { - fn into_attribute_value(self) -> Option { - Some(String::from(match self { - Subscription::None => "none", - Subscription::From => "from", - Subscription::To => "to", - Subscription::Both => "both", - Subscription::Remove => "remove", - })) - } -} +generate_attribute!(Subscription, "subscription", { + None => "none", + From => "from", + To => "to", + Both => "both", + Remove => "remove", +}); #[derive(Debug, Clone, PartialEq)] pub struct Item { diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 3841ad5b49fd6343f4bfefa99294576c4da57ebe..badd1efab7606026dde90871142add5083bb35a7 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -14,42 +14,13 @@ use error::Error; use jid::Jid; use ns; -#[derive(Debug, Clone, PartialEq)] -pub enum ErrorType { - Auth, - Cancel, - Continue, - Modify, - Wait, -} - -impl FromStr for ErrorType { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "auth" => ErrorType::Auth, - "cancel" => ErrorType::Cancel, - "continue" => ErrorType::Continue, - "modify" => ErrorType::Modify, - "wait" => ErrorType::Wait, - - _ => return Err(Error::ParseError("Unknown error type.")), - }) - } -} - -impl IntoAttributeValue for ErrorType { - fn into_attribute_value(self) -> Option { - Some(String::from(match self { - ErrorType::Auth => "auth", - ErrorType::Cancel => "cancel", - ErrorType::Continue => "continue", - ErrorType::Modify => "modify", - ErrorType::Wait => "wait", - })) - } -} +generate_attribute!(ErrorType, "type", { + Auth => "auth", + Cancel => "cancel", + Continue => "continue", + Modify => "modify", + Wait => "wait", +}); #[derive(Debug, Clone, PartialEq)] pub enum DefinedCondition { @@ -252,7 +223,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown error type."); + assert_eq!(message, "Unknown value for 'type' attribute."); } #[test] From ebc7582250d8a3e4a4a30576ad32c7bb3b47bbb2 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 14 Jun 2017 00:53:18 +0100 Subject: [PATCH 250/698] Move the Default implementation into more generate_attribute!. --- src/data_forms.rs | 8 +------- src/jingle.rs | 8 +------- src/message.rs | 8 +------- 3 files changed, 3 insertions(+), 21 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index 0899cc5ca88df9b01cab812eefe54bf9f3e7e0ad..9905f4b400ba4e0d6f33f36afdaee5a90cd07d76 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -25,13 +25,7 @@ generate_attribute!(FieldType, "type", { TextMulti => "text-multi", TextPrivate => "text-private", TextSingle => "text-single", -}); - -impl Default for FieldType { - fn default() -> FieldType { - FieldType::TextSingle - } -} +}, Default = TextSingle); #[derive(Debug, Clone)] pub struct Option_ { diff --git a/src/jingle.rs b/src/jingle.rs index e5fab352b53c491151c0045476fd4e534efda515..d189684e8efa31d6b4ac89083ad3df72acc8c94a 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -41,13 +41,7 @@ generate_attribute!(Senders, "senders", { Initiator => "initiator", None => "none", Responder => "responder", -}); - -impl Default for Senders { - fn default() -> Senders { - Senders::Both - } -} +}, Default = Both); #[derive(Debug, Clone)] pub struct Content { diff --git a/src/message.rs b/src/message.rs index 4efabbdba1c6ff71c8325bb768d68fdf0ec3349a..79038c1249b92736fee62a21123a732eb2b98a36 100644 --- a/src/message.rs +++ b/src/message.rs @@ -108,13 +108,7 @@ generate_attribute!(MessageType, "type", { Groupchat => "groupchat", Headline => "headline", Normal => "normal", -}); - -impl Default for MessageType { - fn default() -> MessageType { - MessageType::Normal - } -} +}, Default = Normal); type Lang = String; type Body = String; From 393402032c371f15333b095ea7526e20d3a6efc7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 14 Jun 2017 00:59:37 +0100 Subject: [PATCH 251/698] jingle: Add a comment. --- src/jingle.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jingle.rs b/src/jingle.rs index d189684e8efa31d6b4ac89083ad3df72acc8c94a..84600f43c7fd0dcd6bbc0ce5d83d136a3fba329d 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -46,7 +46,7 @@ generate_attribute!(Senders, "senders", { #[derive(Debug, Clone)] pub struct Content { pub creator: Creator, - pub disposition: String, + pub disposition: String, // TODO: the list of values is defined, use an enum! pub name: String, pub senders: Senders, pub description: Option, From 216f6f838987f27d06ec5a687da89b7d394fb1e4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 14 Jun 2017 01:57:02 +0100 Subject: [PATCH 252/698] Fix build and compilation. --- src/data_forms.rs | 4 ++-- src/ibb.rs | 2 +- src/jingle_ibb.rs | 2 +- src/message.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index 9905f4b400ba4e0d6f33f36afdaee5a90cd07d76..b5cd004b6e691e07c5670576ebbca3b420e5e736 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -86,7 +86,7 @@ impl IntoElements for Field { } } -generate_attribute!(Subscription, "subscription", { +generate_attribute!(DataFormType, "type", { Cancel => "cancel", Form => "form", Result_ => "result", @@ -272,7 +272,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown data form type."); + assert_eq!(message, "Unknown value for 'type' attribute."); } #[test] diff --git a/src/ibb.rs b/src/ibb.rs index 5463caae809975377e084151a4ac661a63f1a8ba..ad9873778afcfc4cb362fecb38fdd59ab0e9991d 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -181,6 +181,6 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Invalid 'stanza' attribute."); + assert_eq!(message, "Unknown value for 'stanza' attribute."); } } diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index afbf11c3308167ea02b4f0d8e589517434654894..25dbaa377ce1eb3966725a053479cbb3cc568eac 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -107,6 +107,6 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Invalid 'stanza' attribute."); + assert_eq!(message, "Unknown value for 'stanza' attribute."); } } diff --git a/src/message.rs b/src/message.rs index 79038c1249b92736fee62a21123a732eb2b98a36..8025a2a12ffca4d3085f7f01ea22bb21ffc7dad0 100644 --- a/src/message.rs +++ b/src/message.rs @@ -236,7 +236,7 @@ mod tests { #[test] fn test_serialise() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); let message = Message { from: None, to: None, From 48d340120c53a99f1d833491571a5d896b95bc10 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 14 Jun 2017 02:28:42 +0100 Subject: [PATCH 253/698] jingle: Specialise Sid to get an increased type safety. --- src/jingle.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/jingle.rs b/src/jingle.rs index 84600f43c7fd0dcd6bbc0ce5d83d136a3fba329d..ca9858ae85018c13bb4c3aafaa40719e5e464bfa 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -245,12 +245,29 @@ impl IntoElements for ReasonElement { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Sid(String); + +impl FromStr for Sid { + type Err = Error; + fn from_str(s: &str) -> Result { + // TODO: implement the NMTOKEN restrictions: https://www.w3.org/TR/2000/WD-xml-2e-20000814#NT-Nmtoken + Ok(Sid(String::from(s))) + } +} + +impl IntoAttributeValue for Sid { + fn into_attribute_value(self) -> Option { + return Some(self.0); + } +} + #[derive(Debug, Clone)] pub struct Jingle { pub action: Action, pub initiator: Option, pub responder: Option, - pub sid: String, + pub sid: Sid, pub contents: Vec, pub reason: Option, pub other: Vec, From e7f0c45da56ff2931bded25ef3ec8bbf427823bf Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 14 Jun 2017 09:19:06 +0100 Subject: [PATCH 254/698] jingle: Fix sid test. --- src/jingle.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jingle.rs b/src/jingle.rs index ca9858ae85018c13bb4c3aafaa40719e5e464bfa..fad59aa3df5ffb38a960ca0ff66e3bc9ba065e43 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -333,7 +333,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let jingle = Jingle::try_from(elem).unwrap(); assert_eq!(jingle.action, Action::SessionInitiate); - assert_eq!(jingle.sid, "coucou"); + assert_eq!(jingle.sid, Sid(String::from("coucou"))); } #[test] From 6f69f2d7d9be3a65f8c4e8f6e8faa7fbc23381a8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 16 Jun 2017 20:37:48 +0100 Subject: [PATCH 255/698] jingle_ft: Parse into a proper DateTime. --- src/jingle_ft.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 1d37b6a9dda3576e0f93ca3cfe4a75bf56baadac..a70b012b200b15dbec53db1bc0e44c2ca2d650a7 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -9,6 +9,7 @@ use std::convert::TryFrom; use hashes::Hash; use minidom::{Element, IntoElements, ElementEmitter}; +use chrono::{DateTime, FixedOffset}; use error::Error; use ns; @@ -36,7 +37,7 @@ impl IntoElements for Range { #[derive(Debug, Clone)] pub struct File { - pub date: Option, + pub date: Option>, pub media_type: Option, pub name: Option, pub desc: Option, @@ -110,7 +111,7 @@ impl TryFrom for Description { if date.is_some() { return Err(Error::ParseError("File must not have more than one date.")); } - date = Some(file_payload.text()); + date = Some(file_payload.text().parse()?); } else if file_payload.is("media-type", ns::JINGLE_FT) { if media_type.is_some() { return Err(Error::ParseError("File must not have more than one media-type.")); @@ -179,7 +180,7 @@ impl Into for File { if let Some(date) = self.date { root.append_child(Element::builder("date") .ns(ns::JINGLE_FT) - .append(date) + .append(date.to_rfc3339()) .build()); } if let Some(media_type) = self.media_type { @@ -242,7 +243,7 @@ mod tests { text/plain test.txt - 2015-07-26T21:46:00 + 2015-07-26T21:46:00+01:00 6144 w0mcJylzCn+AfvuGdqkty2+KP48= @@ -254,7 +255,7 @@ mod tests { assert_eq!(desc.file.media_type, Some(String::from("text/plain"))); assert_eq!(desc.file.name, Some(String::from("test.txt"))); assert_eq!(desc.file.desc, None); - assert_eq!(desc.file.date, Some(String::from("2015-07-26T21:46:00"))); + assert_eq!(desc.file.date, Some(DateTime::parse_from_rfc3339("2015-07-26T21:46:00+01:00").unwrap())); assert_eq!(desc.file.size, Some(6144u64)); assert_eq!(desc.file.range, None); assert_eq!(desc.file.hashes[0].algo, Algo::Sha_1); From 17798190cf6fd4fd57273a91f906d36666f6ee65 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 17 Jun 2017 03:36:12 +0100 Subject: [PATCH 256/698] error: Implement fmt::Display and error::Error. --- src/error.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/error.rs b/src/error.rs index 899e25f088612459d4bb276277e239e3b772622e..f8d7372b8710384c93b9a56162f693e600f34b01 100644 --- a/src/error.rs +++ b/src/error.rs @@ -8,6 +8,8 @@ use std::convert::From; use std::io; use std::num; use std::string; +use std::fmt; +use std::error; use base64; use minidom; @@ -26,6 +28,36 @@ pub enum Error { ChronoParseError(chrono::ParseError), } +impl fmt::Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match *self { + Error::ParseError(s) => write!(fmt, "{}", s), + Error::IoError(ref e) => write!(fmt, "{}", e), + Error::XMLError(ref e) => write!(fmt, "{}", e), + Error::Base64Error(ref e) => write!(fmt, "{}", e), + Error::ParseIntError(ref e) => write!(fmt, "{}", e), + Error::ParseStringError(ref e) => write!(fmt, "{}", e), + Error::JidParseError(_) => write!(fmt, "JID parse error"), + Error::ChronoParseError(ref e) => write!(fmt, "{}", e), + } + } +} + +impl error::Error for Error { + fn description(&self) -> &str { + match *self { + Error::ParseError(s) => s, + Error::IoError(ref e) => e.description(), + Error::XMLError(ref e) => e.description(), + Error::Base64Error(ref e) => e.description(), + Error::ParseIntError(ref e) => e.description(), + Error::ParseStringError(ref e) => e.description(), + Error::JidParseError(_) => "JID parse error", + Error::ChronoParseError(ref e) => e.description(), + } + } +} + impl From for Error { fn from(err: io::Error) -> Error { Error::IoError(err) From eca588ad6f89079a3af05b99b41b193188f03d0a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 22 Jun 2017 01:41:10 +0100 Subject: [PATCH 257/698] Cargo.toml: Update the chrono dependency to 0.4.0. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 68d32e5d16e530ced1da8127c69fd5f10304c230..224ace7dca438fdbb43ce747cac262917e9795c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,4 +21,4 @@ sha-1 = "0.3.0" sha2 = "0.5.0" sha3 = "0.5.0" blake2 = "0.5.0" -chrono = "0.3.1" +chrono = "0.4.0" From 13e85078487476a6732954248a94a69cda8fb29a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 25 Jun 2017 19:55:55 +0100 Subject: [PATCH 258/698] chatstates: Improve parsing. --- src/chatstates.rs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/chatstates.rs b/src/chatstates.rs index 54f0aefe7e93d343be9c46e8df6fc2d83e4f7f2e..47df01e0cd63330b65a1517c0c40fa3e40f97b33 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -25,25 +25,23 @@ impl TryFrom for ChatState { type Error = Error; fn try_from(elem: Element) -> Result { + if elem.ns() != Some(ns::CHATSTATES) { + return Err(Error::ParseError("This is not a chatstate element.")); + } for _ in elem.children() { return Err(Error::ParseError("Unknown child in chatstate element.")); } for _ in elem.attrs() { return Err(Error::ParseError("Unknown attribute in chatstate element.")); } - if elem.is("active", ns::CHATSTATES) { - Ok(ChatState::Active) - } else if elem.is("composing", ns::CHATSTATES) { - Ok(ChatState::Composing) - } else if elem.is("gone", ns::CHATSTATES) { - Ok(ChatState::Gone) - } else if elem.is("inactive", ns::CHATSTATES) { - Ok(ChatState::Inactive) - } else if elem.is("paused", ns::CHATSTATES) { - Ok(ChatState::Paused) - } else { - Err(Error::ParseError("This is not a chatstate element.")) - } + Ok(match elem.name() { + "active" => ChatState::Active, + "composing" => ChatState::Composing, + "gone" => ChatState::Gone, + "inactive" => ChatState::Inactive, + "paused" => ChatState::Paused, + _ => return Err(Error::ParseError("This is not a chatstate element.")), + }) } } From a48565e171ccb60aa6a6bac880b0096f1dd1349e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 25 Jun 2017 20:13:43 +0100 Subject: [PATCH 259/698] data_forms: Fix FORM_TYPE, and <instructions/> serialisation. --- src/data_forms.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index b5cd004b6e691e07c5670576ebbca3b420e5e736..7e187ec194b60a1269e1fa78d5d72c467386d1cb 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -229,9 +229,8 @@ impl From<DataForm> for Element { Element::builder("x") .ns(ns::DATA_FORMS) .attr("type", form.type_) - .append(form.form_type) - .append(form.title) - .append(form.instructions) + .append(if form.title.is_some() { Some(Element::builder("title").ns(ns::DATA_FORMS).append(form.title)) } else { None }) + .append(if form.instructions.is_some() { Some(Element::builder("instructions").ns(ns::DATA_FORMS).append(form.instructions)) } else { None }) .append(form.fields) .build() } From f428ee070d17bff0778f09fee7110efbac1ddd6d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> Date: Sun, 25 Jun 2017 21:03:48 +0100 Subject: [PATCH 260/698] Add an IBR parser. --- src/ibr.rs | 201 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 + src/ns.rs | 3 + 3 files changed, 207 insertions(+) create mode 100644 src/ibr.rs diff --git a/src/ibr.rs b/src/ibr.rs new file mode 100644 index 0000000000000000000000000000000000000000..183d371638f203b948f522411a690a85192005e1 --- /dev/null +++ b/src/ibr.rs @@ -0,0 +1,201 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::collections::HashMap; +use std::convert::TryFrom; + +use minidom::{Element, IntoElements, ElementEmitter}; + +use error::Error; + +use data_forms::DataForm; + +use ns; + +#[derive(Debug, Clone)] +pub struct Query { + pub fields: HashMap<String, String>, + pub registered: bool, + pub remove: bool, + pub form: Option<DataForm>, + // Not yet implemented. + //pub oob: Option<Oob>, +} + +impl TryFrom<Element> for Query { + type Error = Error; + + fn try_from(elem: Element) -> Result<Query, Error> { + if !elem.is("query", ns::REGISTER) { + return Err(Error::ParseError("This is not an ibr element.")); + } + let mut query = Query { + registered: false, + fields: HashMap::new(), + remove: false, + form: None, + }; + for child in elem.children() { + let namespace = child.ns().unwrap(); + if namespace == ns::REGISTER { + let name = child.name(); + let fields = vec!["address", "city", "date", "email", "first", "instructions", + "key", "last", "misc", "name", "nick", "password", "phone", + "state", "text", "url", "username", "zip"]; + if fields.binary_search(&name).is_ok() { + query.fields.insert(name.to_owned(), child.text()); + } else if name == "registered" { + query.registered = true; + } else if name == "remove" { + query.remove = true; + } else { + return Err(Error::ParseError("Wrong field in ibr element.")); + } + } else if child.is("x", ns::DATA_FORMS) { + query.form = Some(DataForm::try_from(child.clone())?); + } else { + return Err(Error::ParseError("Unknown child in ibr element.")); + } + } + Ok(query) + } +} + +impl Into<Element> for Query { + fn into(self) -> Element { + Element::builder("query") + .ns(ns::REGISTER) + .append(if self.registered { Some(Element::builder("registered").ns(ns::REGISTER)) } else { None }) + .append(self.fields.iter().map(|(name, value)| { + Element::builder(name.clone()).ns(ns::REGISTER).append(value.clone()) + }).collect::<Vec<_>>()) + .append(if self.remove { Some(Element::builder("remove").ns(ns::REGISTER)) } else { None }) + .append(self.form) + .build() + } +} + +impl IntoElements for Query { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + let elem: Element = "<query xmlns='jabber:iq:register'/>".parse().unwrap(); + Query::try_from(elem).unwrap(); + } + + #[test] + fn test_ex2() { + let elem: Element = r#" +<query xmlns='jabber:iq:register'> + <instructions> + Choose a username and password for use with this service. + Please also provide your email address. + </instructions> + <username/> + <password/> + <email/> +</query> +"#.parse().unwrap(); + let query = Query::try_from(elem).unwrap(); + assert_eq!(query.registered, false); + assert_eq!(query.fields["instructions"], "\n Choose a username and password for use with this service.\n Please also provide your email address.\n "); + assert_eq!(query.fields["username"], ""); + assert_eq!(query.fields["password"], ""); + assert_eq!(query.fields["email"], ""); + assert_eq!(query.fields.contains_key("name"), false); + + // FIXME: HashMap doesn’t keep the order right. + //let elem2 = query.into(); + //assert_eq!(elem, elem2); + } + + #[test] + fn test_ex9() { + let elem: Element = r#" +<query xmlns='jabber:iq:register'> + <instructions> + Use the enclosed form to register. If your Jabber client does not + support Data Forms, visit http://www.shakespeare.lit/contests.php + </instructions> + <x xmlns='jabber:x:data' type='form'> + <title>Contest Registration + + Please provide the following information + to sign up for our special contests! + + + jabber:iq:register + + + + + + + + + + + + + + + + +"#.parse().unwrap(); + let elem1 = elem.clone(); + let query = Query::try_from(elem).unwrap(); + assert_eq!(query.registered, false); + assert!(!query.fields["instructions"].is_empty()); + let form = query.form.clone().unwrap(); + assert!(!form.instructions.unwrap().is_empty()); + assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("first"))).is_ok()); + assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("x-gender"))).is_ok()); + assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("coucou"))).is_err()); + let elem2 = query.into(); + assert_eq!(elem1, elem2); + } + + #[test] + fn test_ex10() { + let elem: Element = r#" + + + + jabber:iq:register + + + Juliet + + + Capulet + + + juliet@capulet.com + + + F + + + +"#.parse().unwrap(); + let elem1 = elem.clone(); + let query = Query::try_from(elem).unwrap(); + assert_eq!(query.registered, false); + for _ in &query.fields { + panic!(); + } + let elem2 = query.into(); + assert_eq!(elem1, elem2); + } +} diff --git a/src/lib.rs b/src/lib.rs index 4fc420b82108ff51a0d759f799b8b6bf4275c183..00b2592ce44f13d0e17cbdad0f299ab740e158b2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -144,6 +144,9 @@ pub mod rsm; /// XEP-0060: Publish-Subscribe pub mod pubsub; +/// XEP-0077: In-Band Registration +pub mod ibr; + /// XEP-0085: Chat State Notifications pub mod chatstates; diff --git a/src/ns.rs b/src/ns.rs index af76dd9050c2e2c0a22fd9704ed6aab4cd2b52e5..fbc0048f952bb04d7691628a65692b818e845468 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -39,6 +39,9 @@ pub const PUBSUB_EVENT: &str = "http://jabber.org/protocol/pubsub#event"; /// XEP-0060: Publish-Subscribe pub const PUBSUB_OWNER: &str = "http://jabber.org/protocol/pubsub#owner"; +/// XEP-0077: In-Band Registration +pub const REGISTER: &str = "jabber:iq:register"; + /// XEP-0085: Chat State Notifications pub const CHATSTATES: &str = "http://jabber.org/protocol/chatstates"; From 3ea0c45337111b9664fe80ae4de760e157e9b3ac Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 25 Jun 2017 21:38:58 +0100 Subject: [PATCH 261/698] jingle_s5b: Strengthen type safety for ids. --- src/jingle_s5b.rs | 54 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 089d16056d4530195a344c992fdc4a6b39804362..b585cf19149b28fc340338f7ca6d47680c218767 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -20,9 +20,46 @@ generate_attribute!(Type, "type", { Tunnel => "tunnel", }, Default = Direct); +generate_attribute!(Mode, "mode", { + Tcp => "tcp", + Udp => "udp", +}, Default = Tcp); + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CandidateId(String); + +impl FromStr for CandidateId { + type Err = Error; + fn from_str(s: &str) -> Result { + Ok(CandidateId(String::from(s))) + } +} + +impl IntoAttributeValue for CandidateId { + fn into_attribute_value(self) -> Option { + return Some(self.0); + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct StreamId(String); + +impl FromStr for StreamId { + type Err = Error; + fn from_str(s: &str) -> Result { + Ok(StreamId(String::from(s))) + } +} + +impl IntoAttributeValue for StreamId { + fn into_attribute_value(self) -> Option { + return Some(self.0); + } +} + #[derive(Debug, Clone)] pub struct Candidate { - pub cid: String, + pub cid: CandidateId, pub host: String, pub jid: String, pub port: Option, @@ -44,11 +81,6 @@ impl Into for Candidate { } } -generate_attribute!(Mode, "mode", { - Tcp => "tcp", - Udp => "udp", -}, Default = Tcp); - #[derive(Debug, Clone)] pub enum TransportPayload { Activated(String), @@ -61,7 +93,7 @@ pub enum TransportPayload { #[derive(Debug, Clone)] pub struct Transport { - pub sid: String, + pub sid: StreamId, pub dstaddr: Option, pub mode: Mode, pub payload: TransportPayload, @@ -181,7 +213,7 @@ mod tests { fn test_simple() { let elem: Element = "".parse().unwrap(); let transport = Transport::try_from(elem).unwrap(); - assert_eq!(transport.sid, "coucou"); + assert_eq!(transport.sid, StreamId(String::from("coucou"))); assert_eq!(transport.dstaddr, None); assert_eq!(transport.mode, Mode::Tcp); match transport.payload { @@ -194,7 +226,7 @@ mod tests { fn test_serialise_activated() { let elem: Element = "".parse().unwrap(); let transport = Transport { - sid: String::from("coucou"), + sid: StreamId(String::from("coucou")), dstaddr: None, mode: Mode::Tcp, payload: TransportPayload::Activated(String::from("coucou")), @@ -207,11 +239,11 @@ mod tests { fn test_serialise_candidate() { let elem: Element = "".parse().unwrap(); let transport = Transport { - sid: String::from("coucou"), + sid: StreamId(String::from("coucou")), dstaddr: None, mode: Mode::Tcp, payload: TransportPayload::Candidates(vec!(Candidate { - cid: String::from("coucou"), + cid: CandidateId(String::from("coucou")), host: String::from("coucou"), jid: String::from("coucou@coucou"), port: None, From 95a19b4bb4ec648d58c82a9b17f68ee50409a0cb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 25 Jun 2017 22:14:21 +0100 Subject: [PATCH 262/698] lib: Implement a generate_id! macro. --- src/lib.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 00b2592ce44f13d0e17cbdad0f299ab740e158b2..cb8b9d4a42755afdb996f960be57b3697e27dbfb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -109,6 +109,25 @@ macro_rules! generate_attribute { ); } +macro_rules! generate_id { + ($elem:ident) => ( + #[derive(Debug, Clone, PartialEq, Eq, Hash)] + pub struct $elem(String); + impl FromStr for $elem { + type Err = Error; + fn from_str(s: &str) -> Result<$elem, Error> { + // TODO: add a way to parse that differently when needed. + Ok($elem(String::from(s))) + } + } + impl IntoAttributeValue for $elem { + fn into_attribute_value(self) -> Option { + Some(self.0) + } + } + ); +} + /// Error type returned by every parser on failure. pub mod error; /// XML namespace definitions used through XMPP. From a219501fed1c4aba544f1364eab866341802203e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 25 Jun 2017 22:14:51 +0100 Subject: [PATCH 263/698] jingle, jingle_ft: Use the new generate_id! macro to simplify Sid/Cid generation. --- src/jingle.rs | 21 +++------------------ src/jingle_s5b.rs | 32 ++------------------------------ 2 files changed, 5 insertions(+), 48 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index fad59aa3df5ffb38a960ca0ff66e3bc9ba065e43..224bb54332349cd299f4d057233434480576a84f 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -245,29 +245,14 @@ impl IntoElements for ReasonElement { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Sid(String); - -impl FromStr for Sid { - type Err = Error; - fn from_str(s: &str) -> Result { - // TODO: implement the NMTOKEN restrictions: https://www.w3.org/TR/2000/WD-xml-2e-20000814#NT-Nmtoken - Ok(Sid(String::from(s))) - } -} - -impl IntoAttributeValue for Sid { - fn into_attribute_value(self) -> Option { - return Some(self.0); - } -} +generate_id!(SessionId); #[derive(Debug, Clone)] pub struct Jingle { pub action: Action, pub initiator: Option, pub responder: Option, - pub sid: Sid, + pub sid: SessionId, pub contents: Vec, pub reason: Option, pub other: Vec, @@ -333,7 +318,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let jingle = Jingle::try_from(elem).unwrap(); assert_eq!(jingle.action, Action::SessionInitiate); - assert_eq!(jingle.sid, Sid(String::from("coucou"))); + assert_eq!(jingle.sid, SessionId(String::from("coucou"))); } #[test] diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index b585cf19149b28fc340338f7ca6d47680c218767..cc916a3ba99c71e3722440efd178f760e387a89e 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -25,37 +25,9 @@ generate_attribute!(Mode, "mode", { Udp => "udp", }, Default = Tcp); -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct CandidateId(String); +generate_id!(CandidateId); -impl FromStr for CandidateId { - type Err = Error; - fn from_str(s: &str) -> Result { - Ok(CandidateId(String::from(s))) - } -} - -impl IntoAttributeValue for CandidateId { - fn into_attribute_value(self) -> Option { - return Some(self.0); - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct StreamId(String); - -impl FromStr for StreamId { - type Err = Error; - fn from_str(s: &str) -> Result { - Ok(StreamId(String::from(s))) - } -} - -impl IntoAttributeValue for StreamId { - fn into_attribute_value(self) -> Option { - return Some(self.0); - } -} +generate_id!(StreamId); #[derive(Debug, Clone)] pub struct Candidate { From f6b222f49ae482ff0ef0a4367d49cec463d50969 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 25 Jun 2017 22:15:18 +0100 Subject: [PATCH 264/698] =?UTF-8?q?jingle=5Fibb:=20Strengthen=20sid?= =?UTF-8?q?=E2=80=99s=20type=20safety.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/jingle_ibb.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 25dbaa377ce1eb3966725a053479cbb3cc568eac..72ebba550ea5ae6e9ac6d02faadd8a7d210624fa 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -5,8 +5,9 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use std::convert::TryFrom; +use std::str::FromStr; -use minidom::Element; +use minidom::{Element, IntoAttributeValue}; use error::Error; @@ -14,10 +15,12 @@ use ns; use ibb::Stanza; +generate_id!(StreamId); + #[derive(Debug, Clone)] pub struct Transport { pub block_size: u16, - pub sid: String, + pub sid: StreamId, pub stanza: Stanza, } @@ -60,7 +63,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let transport = Transport::try_from(elem).unwrap(); assert_eq!(transport.block_size, 3); - assert_eq!(transport.sid, "coucou"); + assert_eq!(transport.sid, StreamId(String::from("coucou"))); assert_eq!(transport.stanza, Stanza::Iq); } From 4338d9d6384135f68d7201b9733175cf9d518c1d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 25 Jun 2017 22:20:24 +0100 Subject: [PATCH 265/698] jingle_s5b: Make jid a Jid and not a String. --- src/jingle_s5b.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index cc916a3ba99c71e3722440efd178f760e387a89e..57807be313ec817c1fbd9d7c8f44791c568d2e7f 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -8,6 +8,7 @@ use std::convert::TryFrom; use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; +use jid::Jid; use error::Error; @@ -33,7 +34,7 @@ generate_id!(StreamId); pub struct Candidate { pub cid: CandidateId, pub host: String, - pub jid: String, + pub jid: Jid, pub port: Option, pub priority: u32, pub type_: Type, @@ -45,7 +46,7 @@ impl Into for Candidate { .ns(ns::JINGLE_S5B) .attr("cid", self.cid) .attr("host", self.host) - .attr("jid", self.jid) + .attr("jid", String::from(self.jid)) .attr("port", self.port) .attr("priority", self.priority) .attr("type", self.type_) @@ -217,7 +218,7 @@ mod tests { payload: TransportPayload::Candidates(vec!(Candidate { cid: CandidateId(String::from("coucou")), host: String::from("coucou"), - jid: String::from("coucou@coucou"), + jid: Jid::from_str("coucou@coucou").unwrap(), port: None, priority: 0u32, type_: Type::Direct, From 96fe2d200eed4dc75264775e6f626062dd198b96 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 25 Jun 2017 23:27:38 +0100 Subject: [PATCH 266/698] roster: Fix group serialisation and add a test. --- src/roster.rs | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/roster.rs b/src/roster.rs index f38817d0794290d833ed4dcd64cee1cd75d286d1..db939ad8135ae537040af90125947eacdca36e4e 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -65,7 +65,7 @@ impl Into for Item { .attr("jid", String::from(self.jid)) .attr("name", self.name) .attr("subscription", self.subscription) - .append(self.groups) + .append(self.groups.iter().map(|group| Element::builder("group").ns(ns::ROSTER).append(group)).collect::>()) .build() } } @@ -172,6 +172,29 @@ mod tests { assert_eq!(roster.items[0].groups, vec!(String::from("Friends"))); } + #[test] + fn test_multiple_groups() { + let elem: Element = r#" + + + A + B + + +"#.parse().unwrap(); + let elem1 = elem.clone(); + let roster = Roster::try_from(elem).unwrap(); + assert!(roster.ver.is_none()); + assert_eq!(roster.items.len(), 1); + assert_eq!(roster.items[0].jid, Jid::from_str("test@example.org").unwrap()); + assert_eq!(roster.items[0].name, None); + assert_eq!(roster.items[0].groups.len(), 2); + assert_eq!(roster.items[0].groups[0], String::from("A")); + assert_eq!(roster.items[0].groups[1], String::from("B")); + let elem2 = roster.into(); + assert_eq!(elem1, elem2); + } + #[test] fn test_set() { let elem: Element = "".parse().unwrap(); @@ -186,7 +209,6 @@ mod tests { Servants - "#.parse().unwrap(); let roster = Roster::try_from(elem).unwrap(); assert!(roster.ver.is_none()); From ad1d4adcecc296cd40377b32d79f1937620e3bdb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 26 Jun 2017 19:47:26 +0100 Subject: [PATCH 267/698] message: Add a simpler way to create a Message. --- src/message.rs | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/src/message.rs b/src/message.rs index 8025a2a12ffca4d3085f7f01ea22bb21ffc7dad0..47c8db16eaebfbbb8256a7a45a2ce91a589bb4d0 100644 --- a/src/message.rs +++ b/src/message.rs @@ -127,6 +127,21 @@ pub struct Message { pub payloads: Vec, } +impl Message { + pub fn new(to: Option) -> Message { + Message { + from: None, + to: to, + id: None, + type_: MessageType::Chat, + bodies: BTreeMap::new(), + subjects: BTreeMap::new(), + thread: None, + payloads: vec!(), + } + } +} + impl TryFrom for Message { type Error = Error; @@ -237,16 +252,8 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let message = Message { - from: None, - to: None, - id: None, - type_: MessageType::Normal, - bodies: BTreeMap::new(), - subjects: BTreeMap::new(), - thread: None, - payloads: vec!(), - }; + let mut message = Message::new(None); + message.type_ = MessageType::Normal; let elem2 = message.into(); assert_eq!(elem, elem2); } @@ -265,18 +272,8 @@ mod tests { #[test] fn test_serialise_body() { let elem: Element = "Hello world!".parse().unwrap(); - let mut bodies = BTreeMap::new(); - bodies.insert(String::from(""), String::from("Hello world!")); - let message = Message { - from: None, - to: Some(Jid::from_str("coucou@example.org").unwrap()), - id: None, - type_: MessageType::Chat, - bodies: bodies, - subjects: BTreeMap::new(), - thread: None, - payloads: vec!(), - }; + let mut message = Message::new(Some(Jid::from_str("coucou@example.org").unwrap())); + message.bodies.insert(String::from(""), String::from("Hello world!")); let elem2 = message.into(); assert_eq!(elem, elem2); } From 8ac460224060a888778e7f84671c448d1cc747b9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 26 Jun 2017 19:49:16 +0100 Subject: [PATCH 268/698] presence: Add a simpler way to create a Presence. --- src/presence.rs | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index d04a3d2e3a227b956adc440e0cfde59a1e73b83e..c85f25bec450a255fa2ce6adb2cec30ba29c1d36 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -198,6 +198,21 @@ pub struct Presence { pub payloads: Vec, } +impl Presence { + pub fn new(type_: Type) -> Presence { + Presence { + from: None, + to: None, + id: None, + type_: type_, + show: Show::None, + statuses: BTreeMap::new(), + priority: 0i8, + payloads: vec!(), + } + } +} + impl TryFrom for Presence { type Error = Error; @@ -310,16 +325,7 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let presence = Presence { - from: None, - to: None, - id: None, - type_: Type::Unavailable, - show: Show::None, - statuses: BTreeMap::new(), - priority: 0i8, - payloads: vec!(), - }; + let presence = Presence::new(Type::Unavailable); let elem2 = presence.into(); assert_eq!(elem, elem2); } @@ -446,18 +452,8 @@ mod tests { #[test] fn test_serialise_status() { let status = Status::from("Hello world!"); - let mut statuses = BTreeMap::new(); - statuses.insert(String::from(""), status); - let presence = Presence { - from: None, - to: None, - id: None, - type_: Type::Unavailable, - show: Show::None, - statuses: statuses, - priority: 0i8, - payloads: vec!(), - }; + let mut presence = Presence::new(Type::Unavailable); + presence.statuses.insert(String::from(""), status); let elem: Element = presence.into(); assert!(elem.is("presence", ns::JABBER_CLIENT)); assert!(elem.children().collect::>()[0].is("status", ns::JABBER_CLIENT)); From 27cf9a6298bbb6060f7926aac345a9671c13e6fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Mon, 26 Jun 2017 23:07:12 +0100 Subject: [PATCH 269/698] muc::Muc and muc::MucUser --- src/muc.rs | 346 -------------------- src/muc/mod.rs | 11 + src/muc/muc.rs | 108 +++++++ src/muc/user.rs | 830 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 949 insertions(+), 346 deletions(-) delete mode 100644 src/muc.rs create mode 100644 src/muc/mod.rs create mode 100644 src/muc/muc.rs create mode 100644 src/muc/user.rs diff --git a/src/muc.rs b/src/muc.rs deleted file mode 100644 index c8248b7500df034a926f847a7c83bdc2572d82b2..0000000000000000000000000000000000000000 --- a/src/muc.rs +++ /dev/null @@ -1,346 +0,0 @@ -// Copyright (c) 2017 Maxime “pep” Buquet -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -use std::convert::TryFrom; - -use minidom::{Element, IntoElements, ElementEmitter}; - -use error::Error; - -use ns; - -#[derive(Debug, Clone)] -pub struct Muc; - -impl TryFrom for Muc { - type Error = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("x", ns::MUC) { - return Err(Error::ParseError("This is not an x element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in x element.")); - } - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown attribute in x element.")); - } - Ok(Muc) - } -} - -impl Into for Muc { - fn into(self) -> Element { - Element::builder("x") - .ns(ns::MUC) - .build() - } -} - -#[derive(Debug, Clone, PartialEq)] -pub enum Status { - // 100 - NonAnonymousRoom, - - // 101 - AffiliationChange, - - // 102 - ConfigShowsUnavailableMembers, - - // 103 - ConfigHidesUnavailableMembers, - - // 104 - ConfigNonPrivacyRelated, - - // 110 - SelfPresence, - - // 170 - ConfigRoomLoggingEnabled, - - // 171 - ConfigRoomLoggingDisabled, - - // 172 - ConfigRoomNonAnonymous, - - // 173 - ConfigRoomSemiAnonymous, - - // 201 - RoomHasBeenCreated, - - // 210 - AssignedNick, - - // 301 - Banned, - - // 303 - NewNick, - - // 307 - Kicked, - - // 321 - RemovalFromRoom, - - // 322 - ConfigMembersOnly, - - // 332 - ServiceShutdown, -} - -impl TryFrom for Status { - type Error = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("status", ns::MUC_USER) { - return Err(Error::ParseError("This is not a status element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in status element.")); - } - for (attr, _) in elem.attrs() { - if attr != "code" { - return Err(Error::ParseError("Unknown attribute in status element.")); - } - } - let code = get_attr!(elem, "code", required); - - Ok(match code { - 100 => Status::NonAnonymousRoom, - 101 => Status::AffiliationChange, - 102 => Status::ConfigShowsUnavailableMembers, - 103 => Status::ConfigHidesUnavailableMembers, - 104 => Status::ConfigNonPrivacyRelated, - 110 => Status::SelfPresence, - 170 => Status::ConfigRoomLoggingEnabled, - 171 => Status::ConfigRoomLoggingDisabled, - 172 => Status::ConfigRoomNonAnonymous, - 173 => Status::ConfigRoomSemiAnonymous, - 201 => Status::RoomHasBeenCreated, - 210 => Status::AssignedNick, - 301 => Status::Banned, - 303 => Status::NewNick, - 307 => Status::Kicked, - 321 => Status::RemovalFromRoom, - 322 => Status::ConfigMembersOnly, - 332 => Status::ServiceShutdown, - _ => return Err(Error::ParseError("Invalid status code.")), - }) - } -} - -impl Into for Status { - fn into(self) -> Element { - Element::builder("status") - .ns(ns::MUC_USER) - .attr("code", match self { - Status::NonAnonymousRoom => 100, - Status::AffiliationChange => 101, - Status::ConfigShowsUnavailableMembers => 102, - Status::ConfigHidesUnavailableMembers => 103, - Status::ConfigNonPrivacyRelated => 104, - Status::SelfPresence => 110, - Status::ConfigRoomLoggingEnabled => 170, - Status::ConfigRoomLoggingDisabled => 171, - Status::ConfigRoomNonAnonymous => 172, - Status::ConfigRoomSemiAnonymous => 173, - Status::RoomHasBeenCreated => 201, - Status::AssignedNick => 210, - Status::Banned => 301, - Status::NewNick => 303, - Status::Kicked => 307, - Status::RemovalFromRoom => 321, - Status::ConfigMembersOnly => 322, - Status::ServiceShutdown => 332, - }) - .build() - } -} - -impl IntoElements for Status { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - -#[derive(Debug, Clone)] -pub struct MucUser { - status: Vec, -} - -impl TryFrom for MucUser { - type Error = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("x", ns::MUC_USER) { - return Err(Error::ParseError("This is not an x element.")); - } - let mut status = vec!(); - for child in elem.children() { - if child.is("status", ns::MUC_USER) { - status.push(Status::try_from(child.clone())?); - } else { - return Err(Error::ParseError("Unknown child in x element.")); - } - } - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown attribute in x element.")); - } - Ok(MucUser { - status: status, - }) - } -} - -impl Into for MucUser { - fn into(self) -> Element { - Element::builder("x") - .ns(ns::MUC_USER) - .append(self.status) - .build() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::error::Error as StdError; - - #[test] - fn test_muc_simple() { - let elem: Element = "".parse().unwrap(); - Muc::try_from(elem).unwrap(); - } - - #[test] - fn test_muc_invalid_child() { - let elem: Element = "".parse().unwrap(); - let error = Muc::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown child in x element."); - } - - #[test] - fn test_muc_serialise() { - let elem: Element = "".parse().unwrap(); - let muc = Muc; - let elem2 = muc.into(); - assert_eq!(elem, elem2); - } - - #[test] - fn test_muc_invalid_attribute() { - let elem: Element = "".parse().unwrap(); - let error = Muc::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown attribute in x element."); - } - - #[test] - fn test_muc_user_simple() { - let elem: Element = "".parse().unwrap(); - MucUser::try_from(elem).unwrap(); - } - - #[test] - fn test_muc_user_invalid_child() { - let elem: Element = "".parse().unwrap(); - let error = MucUser::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown child in x element."); - } - - #[test] - fn test_muc_user_serialise() { - let elem: Element = "".parse().unwrap(); - let muc = MucUser { status: vec!() }; - let elem2 = muc.into(); - assert_eq!(elem, elem2); - } - - #[test] - fn test_muc_user_invalid_attribute() { - let elem: Element = "".parse().unwrap(); - let error = MucUser::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown attribute in x element."); - } - - #[test] - fn test_status_simple() { - let elem: Element = "".parse().unwrap(); - Status::try_from(elem).unwrap(); - } - - #[test] - fn test_status_invalid() { - let elem: Element = "".parse().unwrap(); - let error = Status::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Required attribute 'code' missing."); - } - - #[test] - fn test_status_invalid_child() { - let elem: Element = "".parse().unwrap(); - let error = Status::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown child in status element."); - } - - #[test] - fn test_status_simple_code() { - let elem: Element = "".parse().unwrap(); - let status = Status::try_from(elem).unwrap(); - assert_eq!(status, Status::Kicked); - } - - #[test] - fn test_status_invalid_code() { - let elem: Element = "".parse().unwrap(); - let error = Status::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Invalid status code."); - } - - #[test] - fn test_status_invalid_code2() { - let elem: Element = "".parse().unwrap(); - let error = Status::try_from(elem).unwrap_err(); - let error = match error { - Error::ParseIntError(error) => error, - _ => panic!(), - }; - assert_eq!(error.description(), "invalid digit found in string"); - } -} diff --git a/src/muc/mod.rs b/src/muc/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..80d61d88b2066769c909c56d899683a2058c682f --- /dev/null +++ b/src/muc/mod.rs @@ -0,0 +1,11 @@ +// Copyright (c) 2017 Maxime “pep” Buquet +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +pub mod muc; +pub mod user; + +pub use self::muc::Muc; +pub use self::user::MucUser; diff --git a/src/muc/muc.rs b/src/muc/muc.rs new file mode 100644 index 0000000000000000000000000000000000000000..75667d877352ccd6e38404a1a16ef4e1431cf7db --- /dev/null +++ b/src/muc/muc.rs @@ -0,0 +1,108 @@ +// Copyright (c) 2017 Maxime “pep” Buquet +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::convert::TryFrom; + +use minidom::Element; + +use error::Error; + +use ns; + +#[derive(Debug, Clone)] +pub struct Muc { + pub password: Option, +} + +impl TryFrom for Muc { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("x", ns::MUC) { + return Err(Error::ParseError("This is not an x element.")); + } + + let mut password = None; + for child in elem.children() { + if child.is("password", ns::MUC) { + password = Some(child.text()); + } else { + return Err(Error::ParseError("Unknown child in x element.")); + } + } + + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown attribute in x element.")); + } + + Ok(Muc { + password: password, + }) + } +} + +impl Into for Muc { + fn into(self) -> Element { + Element::builder("x") + .ns(ns::MUC) + .append(self.password) + .build() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_muc_simple() { + let elem: Element = "".parse().unwrap(); + Muc::try_from(elem).unwrap(); + } + + #[test] + fn test_muc_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = Muc::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in x element."); + } + + #[test] + fn test_muc_serialise() { + let elem: Element = "".parse().unwrap(); + let muc = Muc { + password: None, + }; + let elem2 = muc.into(); + assert_eq!(elem, elem2); + } + + #[test] + fn test_muc_invalid_attribute() { + let elem: Element = "".parse().unwrap(); + let error = Muc::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in x element."); + } + + #[test] + fn test_muc_simple_password() { + let elem: Element = " + + coucou + " + .parse().unwrap(); + let muc = Muc::try_from(elem).unwrap(); + assert_eq!(muc.password, Some("coucou".to_owned())); + } +} diff --git a/src/muc/user.rs b/src/muc/user.rs new file mode 100644 index 0000000000000000000000000000000000000000..8dae704a0a05e0eae428081f81396e747ef1b9c8 --- /dev/null +++ b/src/muc/user.rs @@ -0,0 +1,830 @@ +// Copyright (c) 2017 Maxime “pep” Buquet +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::convert::TryFrom; +use std::convert::TryInto; +use std::str::FromStr; + +use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; + +use jid::Jid; + +use error::Error; + +use ns; + +#[derive(Debug, Clone, PartialEq)] +pub enum Status { + /// Status: 100 + NonAnonymousRoom, + + /// Status: 101 + AffiliationChange, + + /// Status: 102 + ConfigShowsUnavailableMembers, + + /// Status: 103 + ConfigHidesUnavailableMembers, + + /// Status: 104 + ConfigNonPrivacyRelated, + + /// Status: 110 + SelfPresence, + + /// Status: 170 + ConfigRoomLoggingEnabled, + + /// Status: 171 + ConfigRoomLoggingDisabled, + + /// Status: 172 + ConfigRoomNonAnonymous, + + /// Status: 173 + ConfigRoomSemiAnonymous, + + /// Status: 201 + RoomHasBeenCreated, + + /// Status: 210 + AssignedNick, + + /// Status: 301 + Banned, + + /// Status: 303 + NewNick, + + /// Status: 307 + Kicked, + + /// Status: 321 + RemovalFromRoom, + + /// Status: 322 + ConfigMembersOnly, + + /// Status: 332 + ServiceShutdown, +} + +impl TryFrom for Status { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("status", ns::MUC_USER) { + return Err(Error::ParseError("This is not a status element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in status element.")); + } + for (attr, _) in elem.attrs() { + if attr != "code" { + return Err(Error::ParseError("Unknown attribute in status element.")); + } + } + let code = get_attr!(elem, "code", required); + + Ok(match code { + 100 => Status::NonAnonymousRoom, + 101 => Status::AffiliationChange, + 102 => Status::ConfigShowsUnavailableMembers, + 103 => Status::ConfigHidesUnavailableMembers, + 104 => Status::ConfigNonPrivacyRelated, + 110 => Status::SelfPresence, + 170 => Status::ConfigRoomLoggingEnabled, + 171 => Status::ConfigRoomLoggingDisabled, + 172 => Status::ConfigRoomNonAnonymous, + 173 => Status::ConfigRoomSemiAnonymous, + 201 => Status::RoomHasBeenCreated, + 210 => Status::AssignedNick, + 301 => Status::Banned, + 303 => Status::NewNick, + 307 => Status::Kicked, + 321 => Status::RemovalFromRoom, + 322 => Status::ConfigMembersOnly, + 332 => Status::ServiceShutdown, + _ => return Err(Error::ParseError("Invalid status code.")), + }) + } +} + +impl Into for Status { + fn into(self) -> Element { + Element::builder("status") + .ns(ns::MUC_USER) + .attr("code", match self { + Status::NonAnonymousRoom => 100, + Status::AffiliationChange => 101, + Status::ConfigShowsUnavailableMembers => 102, + Status::ConfigHidesUnavailableMembers => 103, + Status::ConfigNonPrivacyRelated => 104, + Status::SelfPresence => 110, + Status::ConfigRoomLoggingEnabled => 170, + Status::ConfigRoomLoggingDisabled => 171, + Status::ConfigRoomNonAnonymous => 172, + Status::ConfigRoomSemiAnonymous => 173, + Status::RoomHasBeenCreated => 201, + Status::AssignedNick => 210, + Status::Banned => 301, + Status::NewNick => 303, + Status::Kicked => 307, + Status::RemovalFromRoom => 321, + Status::ConfigMembersOnly => 322, + Status::ServiceShutdown => 332, + }) + .build() + } +} + +impl IntoElements for Status { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + +/// Optional element used in elements inside presence stanzas of type +/// "unavailable" that are sent to users who are kick or banned, as well as within IQs for tracking +/// purposes. -- CHANGELOG 0.17 (2002-10-23) +/// Possesses a 'jid' and a 'nick' attribute, so that an action can be attributed either to a real +/// JID or to a roomnick. -- CHANGELOG 1.25 (2012-02-08) +#[derive(Debug, Clone, PartialEq)] +pub enum Actor { + Jid(Jid), + Nick(String), +} + +impl TryFrom for Actor { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("actor", ns::MUC_USER) { + return Err(Error::ParseError("This is not a actor element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in actor element.")); + } + for (attr, _) in elem.attrs() { + if attr != "jid" && attr != "nick" { + return Err(Error::ParseError("Unknown attribute in actor element.")); + } + } + let jid: Option = get_attr!(elem, "jid", optional); + let nick = get_attr!(elem, "nick", optional); + + match (jid, nick) { + (Some(_), Some(_)) + | (None, None) => + return Err(Error::ParseError("Either 'jid' or 'nick' attribute is required.")), + (Some(jid), _) => Ok(Actor::Jid(jid)), + (_, Some(nick)) => Ok(Actor::Nick(nick)), + } + } +} + +impl Into for Actor { + fn into(self) -> Element { + let elem = Element::builder("actor").ns(ns::MUC_USER); + + (match self { + Actor::Jid(jid) => elem.attr("jid", String::from(jid)), + Actor::Nick(nick) => elem.attr("nick", nick), + }).build() + } +} + +impl IntoElements for Actor { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct Continue { + thread: Option, +} + +impl TryFrom for Continue { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("continue", ns::MUC_USER) { + return Err(Error::ParseError("This is not a continue element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in continue element.")); + } + for (attr, _) in elem.attrs() { + if attr != "thread" { + return Err(Error::ParseError("Unknown attribute in continue element.")); + } + } + Ok(Continue { thread: get_attr!(elem, "thread", optional) }) + } +} + +impl Into for Continue { + fn into(self) -> Element { + Element::builder("continue") + .ns(ns::MUC_USER) + .attr("thread", self.thread) + .build() + } +} + +impl IntoElements for Continue { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct Reason(String); + +impl TryFrom for Reason { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("reason", ns::MUC_USER) { + return Err(Error::ParseError("This is not a reason element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in reason element.")); + } + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown attribute in reason element.")); + } + Ok(Reason(elem.text())) + } +} + +impl Into for Reason { + fn into(self) -> Element { + Element::builder("reason") + .ns(ns::MUC_USER) + .append(self.0) + .build() + } +} + +impl IntoElements for Reason { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum Affiliation { + Owner, + Admin, + Member, + Outcast, + None, +} + +impl FromStr for Affiliation { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "owner" => Affiliation::Owner, + "admin" => Affiliation::Admin, + "member" => Affiliation::Member, + "outcast" => Affiliation::Outcast, + "none" => Affiliation::None, + + _ => return Err(Error::ParseError("Unknown affiliation.")), + }) + } +} + +impl IntoAttributeValue for Affiliation { + fn into_attribute_value(self) -> Option { + Some(String::from(match self { + Affiliation::Owner => "owner", + Affiliation::Admin => "admin", + Affiliation::Member => "member", + Affiliation::Outcast => "outcast", + Affiliation::None => "none", + })) + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum Role { + Moderator, + Participant, + Visitor, + None, +} + +impl FromStr for Role { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "moderator" => Role::Moderator, + "participant" => Role::Participant, + "visitor" => Role::Visitor, + "none" => Role::None, + + _ => return Err(Error::ParseError("Unknown role.")), + }) + } +} + +impl IntoAttributeValue for Role { + fn into_attribute_value(self) -> Option { + Some(String::from(match self { + Role::Moderator => "moderator", + Role::Participant => "participant", + Role::Visitor => "visitor", + Role::None => "none", + })) + } +} + +#[derive(Debug, Clone)] +pub struct Item { + pub affiliation: Affiliation, + pub jid: Option, + pub nick: Option, + pub role: Role, + pub actor: Option, + pub continue_: Option, + pub reason: Option, +} + +impl TryFrom for Item { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("item", ns::MUC_USER) { + return Err(Error::ParseError("This is not a item element.")); + } + let mut actor: Option = None; + let mut continue_: Option = None; + let mut reason: Option = None; + for child in elem.children() { + if child.is("actor", ns::MUC_USER) { + actor = Some(child.clone().try_into()?); + } else if child.is("continue", ns::MUC_USER) { + continue_ = Some(child.clone().try_into()?); + } else if child.is("reason", ns::MUC_USER) { + reason = Some(child.clone().try_into()?); + } else { + return Err(Error::ParseError("Unknown child in item element.")); + } + } + for (attr, _) in elem.attrs() { + if attr != "affiliation" && attr != "jid" && + attr != "nick" && attr != "role" { + return Err(Error::ParseError("Unknown attribute in item element.")); + } + } + + let affiliation: Affiliation = get_attr!(elem, "affiliation", required); + let jid: Option = get_attr!(elem, "jid", optional); + let nick: Option = get_attr!(elem, "nick", optional); + let role: Role = get_attr!(elem, "role", required); + + Ok(Item{ + affiliation: affiliation, + jid: jid, + nick: nick, + role: role, + actor: actor, + continue_: continue_, + reason: reason, + }) + } +} + +impl Into for Item { + fn into(self) -> Element { + Element::builder("item") + .ns(ns::MUC_USER) + .attr("affiliation", self.affiliation) + .attr("jid", match self.jid { + Some(jid) => Some(String::from(jid)), + None => None, + }) + .attr("nick", self.nick) + .attr("role", self.role) + .append(self.actor) + .append(self.continue_) + .append(self.reason) + .build() + } +} + +#[derive(Debug, Clone)] +pub struct MucUser { + pub status: Vec, + pub items: Vec, +} + +impl TryFrom for MucUser { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("x", ns::MUC_USER) { + return Err(Error::ParseError("This is not an x element.")); + } + let mut status = vec!(); + let mut items = vec!(); + for child in elem.children() { + if child.is("status", ns::MUC_USER) { + status.push(Status::try_from(child.clone())?); + } else if child.is("item", ns::MUC_USER) { + items.push(Item::try_from(child.clone())?); + } else { + return Err(Error::ParseError("Unknown child in x element.")); + } + } + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown attribute in x element.")); + } + Ok(MucUser { + status, + items, + }) + } +} + +impl Into for MucUser { + fn into(self) -> Element { + Element::builder("x") + .ns(ns::MUC_USER) + .append(self.status) + .build() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::error::Error as StdError; + + #[test] + fn test_simple() { + let elem: Element = " + + ".parse().unwrap(); + MucUser::try_from(elem).unwrap(); + } + + #[test] + fn test_invalid_child() { + let elem: Element = " + + + + ".parse().unwrap(); + let error = MucUser::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in x element."); + } + + #[test] + fn test_serialise() { + let elem: Element = " + + ".parse().unwrap(); + let muc = MucUser { status: vec!(), items: vec!() }; + let elem2 = muc.into(); + assert_eq!(elem, elem2); + } + + #[test] + fn test_invalid_attribute() { + let elem: Element = " + + ".parse().unwrap(); + let error = MucUser::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in x element."); + } + + #[test] + fn test_status_simple() { + let elem: Element = " + + ".parse().unwrap(); + Status::try_from(elem).unwrap(); + } + + #[test] + fn test_status_invalid() { + let elem: Element = " + + ".parse().unwrap(); + let error = Status::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'code' missing."); + } + + #[test] + fn test_status_invalid_child() { + let elem: Element = " + + + + ".parse().unwrap(); + let error = Status::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in status element."); + } + + #[test] + fn test_status_simple_code() { + let elem: Element = " + + ".parse().unwrap(); + let status = Status::try_from(elem).unwrap(); + assert_eq!(status, Status::Kicked); + } + + #[test] + fn test_status_invalid_code() { + let elem: Element = " + + ".parse().unwrap(); + let error = Status::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Invalid status code."); + } + + #[test] + fn test_status_invalid_code2() { + let elem: Element = " + + ".parse().unwrap(); + let error = Status::try_from(elem).unwrap_err(); + let error = match error { + Error::ParseIntError(error) => error, + _ => panic!(), + }; + assert_eq!(error.description(), "invalid digit found in string"); + } + + #[test] + fn test_actor_required_attributes() { + let elem: Element = " + + ".parse().unwrap(); + let error = Actor::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Either 'jid' or 'nick' attribute is required."); + } + + #[test] + fn test_actor_required_attributes2() { + let elem: Element = " + + ".parse().unwrap(); + let error = Actor::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Either 'jid' or 'nick' attribute is required."); + } + + #[test] + fn test_actor_jid() { + let elem: Element = " + + ".parse().unwrap(); + let actor = Actor::try_from(elem).unwrap(); + let jid = match actor { + Actor::Jid(jid) => jid, + _ => panic!(), + }; + assert_eq!(jid, "foo@bar/baz".parse::().unwrap()); + } + + #[test] + fn test_actor_nick() { + let elem: Element = " + + ".parse().unwrap(); + let actor = Actor::try_from(elem).unwrap(); + let nick = match actor { + Actor::Nick(nick) => nick, + _ => panic!(), + }; + assert_eq!(nick, "baz".to_owned()); + } + + #[test] + fn test_continue_simple() { + let elem: Element = " + + ".parse().unwrap(); + Continue::try_from(elem).unwrap(); + } + + #[test] + fn test_continue_thread_attribute() { + let elem: Element = " + + ".parse().unwrap(); + let continue_ = Continue::try_from(elem).unwrap(); + assert_eq!(continue_, Continue { thread: Some("foo".to_owned()) }); + } + + #[test] + fn test_continue_invalid() { + let elem: Element = " + + + + ".parse().unwrap(); + let continue_ = Continue::try_from(elem).unwrap_err(); + let message = match continue_ { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in continue element.".to_owned()); + } + + #[test] + fn test_reason_simple() { + let elem: Element = " + Reason" + .parse().unwrap(); + let reason = Reason::try_from(elem).unwrap(); + assert_eq!(reason.0, "Reason".to_owned()); + } + + #[test] + fn test_reason_invalid_attribute() { + let elem: Element = " + + ".parse().unwrap(); + let error = Reason::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in reason element.".to_owned()); + } + + #[test] + fn test_reason_invalid() { + let elem: Element = " + + + + ".parse().unwrap(); + let error = Reason::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in reason element.".to_owned()); + } + + #[test] + fn test_item_invalid_attr(){ + let elem: Element = " + + ".parse().unwrap(); + let error = Item::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in item element.".to_owned()); + } + + #[test] + fn test_item_affiliation_role_attr(){ + let elem: Element = " + + ".parse().unwrap(); + Item::try_from(elem).unwrap(); + } + + #[test] + fn test_item_affiliation_role_invalid_attr(){ + let elem: Element = " + + ".parse().unwrap(); + let error = Item::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'role' missing.".to_owned()); + } + + #[test] + fn test_item_nick_attr(){ + let elem: Element = " + + ".parse().unwrap(); + let item = Item::try_from(elem).unwrap(); + match item { + Item { nick, .. } => assert_eq!(nick, Some("foobar".to_owned())), + } + } + + #[test] + fn test_item_affiliation_role_invalid_attr2(){ + let elem: Element = " + + ".parse().unwrap(); + let error = Item::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'affiliation' missing.".to_owned()); + } + + #[test] + fn test_item_role_actor_child(){ + let elem: Element = " + + + + ".parse().unwrap(); + let item = Item::try_from(elem).unwrap(); + match item { + Item { actor, .. } => + assert_eq!(actor, Some(Actor::Nick("foobar".to_owned()))), + } + } + + #[test] + fn test_item_role_continue_child(){ + let elem: Element = " + + + + ".parse().unwrap(); + let item = Item::try_from(elem).unwrap(); + let continue_1 = Continue { thread: Some("foobar".to_owned()) }; + match item { + Item { continue_: Some(continue_2), .. } => assert_eq!(continue_2, continue_1), + _ => panic!(), + } + } + + #[test] + fn test_item_role_reason_child(){ + let elem: Element = " + + foobar + + ".parse().unwrap(); + let item = Item::try_from(elem).unwrap(); + match item { + Item { reason, .. } => + assert_eq!(reason, Some(Reason("foobar".to_owned()))), + } + } +} From 98878042dd4ba1a630f85b329645527f59093b9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Tue, 27 Jun 2017 00:14:40 +0100 Subject: [PATCH 270/698] Use the generate_attribute macro in muc::user --- src/muc/user.rs | 84 +++++++++---------------------------------------- 1 file changed, 14 insertions(+), 70 deletions(-) diff --git a/src/muc/user.rs b/src/muc/user.rs index 8dae704a0a05e0eae428081f81396e747ef1b9c8..833b5604a959b43f9455f0003331b001f1646574 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -278,76 +278,20 @@ impl IntoElements for Reason { } } -#[derive(Debug, Clone, PartialEq)] -pub enum Affiliation { - Owner, - Admin, - Member, - Outcast, - None, -} - -impl FromStr for Affiliation { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "owner" => Affiliation::Owner, - "admin" => Affiliation::Admin, - "member" => Affiliation::Member, - "outcast" => Affiliation::Outcast, - "none" => Affiliation::None, - - _ => return Err(Error::ParseError("Unknown affiliation.")), - }) - } -} - -impl IntoAttributeValue for Affiliation { - fn into_attribute_value(self) -> Option { - Some(String::from(match self { - Affiliation::Owner => "owner", - Affiliation::Admin => "admin", - Affiliation::Member => "member", - Affiliation::Outcast => "outcast", - Affiliation::None => "none", - })) - } -} - -#[derive(Debug, Clone, PartialEq)] -pub enum Role { - Moderator, - Participant, - Visitor, - None, -} - -impl FromStr for Role { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "moderator" => Role::Moderator, - "participant" => Role::Participant, - "visitor" => Role::Visitor, - "none" => Role::None, - - _ => return Err(Error::ParseError("Unknown role.")), - }) - } -} - -impl IntoAttributeValue for Role { - fn into_attribute_value(self) -> Option { - Some(String::from(match self { - Role::Moderator => "moderator", - Role::Participant => "participant", - Role::Visitor => "visitor", - Role::None => "none", - })) - } -} +generate_attribute!(Affiliation, "affiliation", { + Owner => "owner", + Admin => "admin", + Member => "member", + Outcast => "outcast", + None => "none", +}, Default = None); + +generate_attribute!(Role, "role", { + Moderator => "moderator", + Participant => "participant", + Visitor => "visitor", + None => "none", +}, Default = None); #[derive(Debug, Clone)] pub struct Item { From 244c9b2710ce08e3886a9cd2a25ae0d938e60fe5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 27 Jun 2017 22:21:35 +0100 Subject: [PATCH 271/698] presence: Remove unused import in tests. --- src/presence.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/presence.rs b/src/presence.rs index c85f25bec450a255fa2ce6adb2cec30ba29c1d36..74458797a457e320a0e158790cca120f5f8b90c4 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -308,7 +308,6 @@ impl Into for Presence { #[cfg(test)] mod tests { - use std::collections::BTreeMap; use super::*; #[test] From 6ea175ad2ed16596e13a699826dffb49f7633f83 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 27 Jun 2017 22:25:13 +0100 Subject: [PATCH 272/698] Cargo.toml: Update the hashing libraries. --- Cargo.toml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 224ace7dca438fdbb43ce747cac262917e9795c3..ac9eae51923a2300b002672b78cf9ceca3e8157e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,9 +16,9 @@ license = "MPL-2.0" minidom = "0.4.3" jid = "0.2.0" base64 = "0.6.0" -digest = "0.5.0" -sha-1 = "0.3.0" -sha2 = "0.5.0" -sha3 = "0.5.0" -blake2 = "0.5.0" +digest = "0.6.0" +sha-1 = "0.4.0" +sha2 = "0.6.0" +sha3 = "0.6.0" +blake2 = "0.6.0" chrono = "0.4.0" From 7ddb1b8301175c00c974842877ba57a833d4a151 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 27 Jun 2017 22:19:06 +0100 Subject: [PATCH 273/698] ChangeLog: Add 0.6.0 release notes. --- ChangeLog | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ChangeLog b/ChangeLog index d219bf0bcc742eebae08be95e8a41ecf8a3efee7..5edf6648790440e927d0026eb6ffbf044a801d6e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,23 @@ +Version 0.6.0: +2017-06-27 Emmanuel Gil Peyrot + * New parsers/serialisers: + - In-Band Registration (XEP-0077) was added. + - Multi-User Chat (XEP-0045) got expanded a lot, thanks pep.! + * Breaking changes: + - Added wrappers for Strings used as identifiers, to add type + safety. + - Use chrono’s DateTime for JingleFT’s date element. + - Use Jid for JingleS5B’s jid attribute. + * Improvements: + - Use more macros for common tasks. + - Add a constructor for Message and Presence. + - Implement std::fmt::Display and std::error::Error on our + error type. + - Fix DataForms serialisation. + - Fix roster group serialisation. + - Update libraries, notably chrono whose version 0.3.1 got + yanked. + Version 0.5.0: 2017-06-11 Emmanuel Gil Peyrot * New parsers/serialisers: From a2855112bac37ae4fd4f561d817345bbb317fcea Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 27 Jun 2017 22:25:33 +0100 Subject: [PATCH 274/698] Release version 0.6.0. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ac9eae51923a2300b002672b78cf9ceca3e8157e..608f2ff536685715ccc4a39e5d221499772a85ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.5.0" +version = "0.6.0" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", From 161e5dcc9bd63fcd1c25084e8475c711bda2918c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 15 Jul 2017 11:37:29 +0100 Subject: [PATCH 276/698] jingle: Type Content::name better. --- src/jingle.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index 224bb54332349cd299f4d057233434480576a84f..ece7fa023a89c934ad4400dd93047c8368e731f8 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -43,11 +43,13 @@ generate_attribute!(Senders, "senders", { Responder => "responder", }, Default = Both); +generate_id!(ContentId); + #[derive(Debug, Clone)] pub struct Content { pub creator: Creator, pub disposition: String, // TODO: the list of values is defined, use an enum! - pub name: String, + pub name: ContentId, pub senders: Senders, pub description: Option, pub transport: Option, @@ -353,7 +355,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let jingle = Jingle::try_from(elem).unwrap(); assert_eq!(jingle.contents[0].creator, Creator::Initiator); - assert_eq!(jingle.contents[0].name, "coucou"); + assert_eq!(jingle.contents[0].name, ContentId(String::from("coucou"))); assert_eq!(jingle.contents[0].senders, Senders::Both); assert_eq!(jingle.contents[0].disposition, "session"); From 945ae350ac650e503352f6b095c9286f82e7921b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 15 Jul 2017 11:38:44 +0100 Subject: [PATCH 277/698] Add a jingle message parser and serialiser. --- src/jingle_message.rs | 132 ++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 5 +- src/ns.rs | 3 + 3 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 src/jingle_message.rs diff --git a/src/jingle_message.rs b/src/jingle_message.rs new file mode 100644 index 0000000000000000000000000000000000000000..d61133427655ebeb9037c259279bdc37dd91341b --- /dev/null +++ b/src/jingle_message.rs @@ -0,0 +1,132 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::convert::TryFrom; + +use minidom::Element; + +use error::Error; + +use jingle::SessionId; + +use ns; + +#[derive(Debug, Clone)] +pub enum JingleMI { + Propose { + sid: SessionId, + // TODO: Use a more specialised type here. + description: Element, + }, + Retract(SessionId), + Accept(SessionId), + Proceed(SessionId), + Reject(SessionId), +} + +fn get_sid(elem: Element) -> Result { + for (attr, _) in elem.attrs() { + if attr != "id" { + return Err(Error::ParseError("Unknown attribute in Jingle message element.")); + } + } + Ok(SessionId(get_attr!(elem, "id", required))) +} + +fn check_empty_and_get_sid(elem: Element) -> Result { + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in Jingle message element.")); + } + get_sid(elem) +} + +impl TryFrom for JingleMI { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if elem.ns() != Some(ns::JINGLE_MESSAGE) { + return Err(Error::ParseError("This is not a Jingle message element.")); + } + Ok(match elem.name() { + "propose" => { + let mut description = None; + for child in elem.children() { + if child.name() != "description" { + return Err(Error::ParseError("Unknown child in propose element.")); + } + if description.is_some() { + return Err(Error::ParseError("Too many children in propose element.")); + } + description = Some(child.clone()); + } + JingleMI::Propose { + sid: get_sid(elem)?, + description: description.ok_or(Error::ParseError("Propose element doesn’t contain a description."))?, + } + }, + "retract" => JingleMI::Retract(check_empty_and_get_sid(elem)?), + "accept" => JingleMI::Accept(check_empty_and_get_sid(elem)?), + "proceed" => JingleMI::Proceed(check_empty_and_get_sid(elem)?), + "reject" => JingleMI::Reject(check_empty_and_get_sid(elem)?), + _ => return Err(Error::ParseError("This is not a Jingle message element.")), + }) + } +} + +impl From for Element { + fn from(jingle_mi: JingleMI) -> Element { + match jingle_mi { + JingleMI::Propose { sid, description } => { + Element::builder("propose") + .ns(ns::JINGLE_MESSAGE) + .attr("id", sid) + .append(description) + }, + JingleMI::Retract(sid) => { + Element::builder("retract") + .ns(ns::JINGLE_MESSAGE) + .attr("id", sid) + } + JingleMI::Accept(sid) => { + Element::builder("accept") + .ns(ns::JINGLE_MESSAGE) + .attr("id", sid) + } + JingleMI::Proceed(sid) => { + Element::builder("proceed") + .ns(ns::JINGLE_MESSAGE) + .attr("id", sid) + } + JingleMI::Reject(sid) => { + Element::builder("reject") + .ns(ns::JINGLE_MESSAGE) + .attr("id", sid) + } + }.build() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + JingleMI::try_from(elem).unwrap(); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = JingleMI::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in propose element."); + } +} diff --git a/src/lib.rs b/src/lib.rs index cb8b9d4a42755afdb996f960be57b3697e27dbfb..f82e6df349d6d63df7f87a9fb64fb7e2261f8e51 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,7 +112,7 @@ macro_rules! generate_attribute { macro_rules! generate_id { ($elem:ident) => ( #[derive(Debug, Clone, PartialEq, Eq, Hash)] - pub struct $elem(String); + pub struct $elem(pub String); impl FromStr for $elem { type Err = Error; fn from_str(s: &str) -> Result<$elem, Error> { @@ -214,6 +214,9 @@ pub mod mam; /// XEP-0319: Last User Interaction in Presence pub mod idle; +/// XEP-0353: Jingle Message Initiation +pub mod jingle_message; + /// XEP-0359: Unique and Stable Stanza IDs pub mod stanza_id; diff --git a/src/ns.rs b/src/ns.rs index fbc0048f952bb04d7691628a65692b818e845468..7edee1e4aec9af43771c630cdd6380871c075971 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -104,6 +104,9 @@ pub const MAM: &str = "urn:xmpp:mam:2"; /// XEP-0319: Last User Interaction in Presence pub const IDLE: &str = "urn:xmpp:idle:1"; +/// XEP-0353: Jingle Message Initiation +pub const JINGLE_MESSAGE: &str = "urn:xmpp:jingle-message:0"; + /// XEP-0359: Unique and Stable Stanza IDs pub const SID: &str = "urn:xmpp:sid:0"; From 76a46559b81f7ee56df4533e4381df9e325423f5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Jul 2017 17:39:59 +0100 Subject: [PATCH 278/698] disco: Split query and result. --- src/caps.rs | 17 ++++++------- src/disco.rs | 69 +++++++++++++++++++++++++++++++++++++-------------- src/ecaps2.rs | 17 ++++++------- src/iq.rs | 19 ++++++++++---- 4 files changed, 78 insertions(+), 44 deletions(-) diff --git a/src/caps.rs b/src/caps.rs index b7753a195ed15fad27bd54ea4c60ae425e79da09..e883a64a7863f73979b902e9207e3622890146f2 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -6,7 +6,7 @@ use std::convert::TryFrom; -use disco::{Feature, Identity, Disco}; +use disco::{Feature, Identity, DiscoInfoResult, DiscoInfoQuery}; use data_forms::DataForm; use hashes::{Hash, Algo}; @@ -122,7 +122,7 @@ fn compute_extensions(extensions: &[DataForm]) -> Vec { }) } -pub fn compute_disco(disco: &Disco) -> Vec { +pub fn compute_disco(disco: &DiscoInfoResult) -> Vec { let identities_string = compute_identities(&disco.identities); let features_string = compute_features(&disco.features); let extensions_string = compute_extensions(&disco.extensions); @@ -193,12 +193,9 @@ pub fn hash_caps(data: &[u8], algo: Algo) -> Result { }) } -pub fn query_caps(caps: Caps) -> Disco { - Disco { +pub fn query_caps(caps: Caps) -> DiscoInfoQuery { + DiscoInfoQuery { node: Some(format!("{}#{}", caps.node, base64::encode(&caps.hash.hash))), - identities: vec!(), - features: vec!(), - extensions: vec!(), } } @@ -231,7 +228,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let disco = Disco::try_from(elem).unwrap(); + let disco = DiscoInfoResult::try_from(elem).unwrap(); let caps = caps::compute_disco(&disco); assert_eq!(caps.len(), 50); } @@ -252,7 +249,7 @@ mod tests { let data = b"client/pc//Exodus 0.9.1, +} + +impl TryFrom for DiscoInfoQuery { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("query", ns::DISCO_INFO) { + return Err(Error::ParseError("This is not a disco#info element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in disco#info.")); + } + for (attr, _) in elem.attrs() { + if attr != "node" { + return Err(Error::ParseError("Unknown attribute in disco#info.")); + } + } + Ok(DiscoInfoQuery { + node: get_attr!(elem, "node", optional), + }) + } +} + +impl From for Element { + fn from(disco: DiscoInfoQuery) -> Element { + Element::builder("query") + .ns(ns::DISCO_INFO) + .attr("node", disco.node) + .build() + } +} + #[derive(Debug, Clone, PartialEq)] pub struct Feature { pub var: String, @@ -60,17 +95,17 @@ impl IntoElements for Identity { } #[derive(Debug, Clone)] -pub struct Disco { +pub struct DiscoInfoResult { pub node: Option, pub identities: Vec, pub features: Vec, pub extensions: Vec, } -impl TryFrom for Disco { +impl TryFrom for DiscoInfoResult { type Error = Error; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("query", ns::DISCO_INFO) { return Err(Error::ParseError("This is not a disco#info element.")); } @@ -120,8 +155,6 @@ impl TryFrom for Disco { } } - /* - // TODO: encode these restrictions only for result disco#info, not get ones. if identities.is_empty() { return Err(Error::ParseError("There must be at least one identity in disco#info.")); } @@ -131,9 +164,8 @@ impl TryFrom for Disco { if !features.contains(&Feature { var: ns::DISCO_INFO.to_owned() }) { return Err(Error::ParseError("disco#info feature not present in disco#info.")); } - */ - Ok(Disco { + Ok(DiscoInfoResult { node: node, identities: identities, features: features, @@ -142,7 +174,7 @@ impl TryFrom for Disco { } } -impl Into for Disco { +impl Into for DiscoInfoResult { fn into(self) -> Element { for _ in self.extensions { panic!("Not yet implemented!"); @@ -163,7 +195,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let query = Disco::try_from(elem).unwrap(); + let query = DiscoInfoResult::try_from(elem).unwrap(); assert!(query.node.is_none()); assert_eq!(query.identities.len(), 1); assert_eq!(query.features.len(), 1); @@ -173,7 +205,7 @@ mod tests { #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(elem).unwrap_err(); + let error = DiscoInfoResult::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -184,7 +216,7 @@ mod tests { #[test] fn test_invalid_identity() { let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(elem).unwrap_err(); + let error = DiscoInfoResult::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -192,7 +224,7 @@ mod tests { assert_eq!(message, "Required attribute 'category' missing."); let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(elem).unwrap_err(); + let error = DiscoInfoResult::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -200,7 +232,7 @@ mod tests { assert_eq!(message, "Identity must have a non-empty 'category' attribute."); let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(elem).unwrap_err(); + let error = DiscoInfoResult::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -208,7 +240,7 @@ mod tests { assert_eq!(message, "Required attribute 'type' missing."); let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(elem).unwrap_err(); + let error = DiscoInfoResult::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -219,7 +251,7 @@ mod tests { #[test] fn test_invalid_feature() { let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(elem).unwrap_err(); + let error = DiscoInfoResult::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -228,10 +260,9 @@ mod tests { } #[test] - #[ignore] fn test_invalid_result() { let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(elem).unwrap_err(); + let error = DiscoInfoResult::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -239,7 +270,7 @@ mod tests { assert_eq!(message, "There must be at least one identity in disco#info."); let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(elem).unwrap_err(); + let error = DiscoInfoResult::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -247,7 +278,7 @@ mod tests { assert_eq!(message, "There must be at least one feature in disco#info."); let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(elem).unwrap_err(); + let error = DiscoInfoResult::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 7d9279bb2d73d1ae88a04b1e7add7559b8398009..da74533d8e366f8135d9f2f7699201423fb7b059 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -6,7 +6,7 @@ use std::convert::TryFrom; -use disco::{Feature, Identity, Disco}; +use disco::{Feature, Identity, DiscoInfoResult, DiscoInfoQuery}; use data_forms::DataForm; use hashes::{Hash, Algo}; @@ -106,7 +106,7 @@ fn compute_extensions(extensions: &[DataForm]) -> Vec { }) } -pub fn compute_disco(disco: &Disco) -> Vec { +pub fn compute_disco(disco: &DiscoInfoResult) -> Vec { let features_string = compute_features(&disco.features); let identities_string = compute_identities(&disco.identities); let extensions_string = compute_extensions(&disco.extensions); @@ -172,12 +172,9 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { }) } -pub fn query_ecaps2(hash: Hash) -> Disco { - Disco { +pub fn query_ecaps2(hash: Hash) -> DiscoInfoQuery { + DiscoInfoQuery { node: Some(format!("{}#{}.{}", ns::ECAPS2, String::from(hash.algo), base64::encode(&hash.hash))), - identities: vec!(), - features: vec!(), - extensions: vec!(), } } @@ -211,7 +208,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let disco = Disco::try_from(elem).unwrap(); + let disco = DiscoInfoResult::try_from(elem).unwrap(); let ecaps2 = ecaps2::compute_disco(&disco); assert_eq!(ecaps2.len(), 54); } @@ -274,7 +271,7 @@ mod tests { 105, 109, 101, 31, 28, 99, 108, 105, 101, 110, 116, 31, 109, 111, 98, 105, 108, 101, 31, 31, 66, 111, 109, 98, 117, 115, 77, 111, 100, 31, 30, 28, 28]; - let disco = Disco::try_from(elem).unwrap(); + let disco = DiscoInfoResult::try_from(elem).unwrap(); let ecaps2 = ecaps2::compute_disco(&disco); assert_eq!(ecaps2.len(), 0x1d9); assert_eq!(ecaps2, expected); @@ -446,7 +443,7 @@ mod tests { 111, 110, 31, 48, 46, 49, 49, 46, 49, 45, 115, 118, 110, 45, 50, 48, 49, 49, 49, 50, 49, 54, 45, 109, 111, 100, 32, 40, 84, 99, 108, 47, 84, 107, 32, 56, 46,54, 98, 50, 41, 31, 30, 29, 28]; - let disco = Disco::try_from(elem).unwrap(); + let disco = DiscoInfoResult::try_from(elem).unwrap(); let ecaps2 = ecaps2::compute_disco(&disco); assert_eq!(ecaps2.len(), 0x543); assert_eq!(ecaps2, expected); diff --git a/src/iq.rs b/src/iq.rs index 76a97b1f217408f2973030d93241ed3120b62d2e..9420b44ccddc2c7227b14016205fe98edfecf3f5 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -18,7 +18,7 @@ use ns; use stanza_error::StanzaError; use roster::Roster; -use disco::Disco; +use disco::{DiscoInfoResult, DiscoInfoQuery}; use ibb::IBB; use jingle::Jingle; use ping::Ping; @@ -28,7 +28,8 @@ use mam::{Query as MamQuery, Fin as MamFin, Prefs as MamPrefs}; #[derive(Debug, Clone)] pub enum IqPayload { Roster(Roster), - Disco(Disco), + DiscoInfoResult(DiscoInfoResult), + DiscoInfoQuery(DiscoInfoQuery), IBB(IBB), Jingle(Jingle), Ping(Ping), @@ -48,7 +49,14 @@ impl TryFrom for IqPayload { ("query", ns::ROSTER) => IqPayload::Roster(Roster::try_from(elem)?), // XEP-0030 - ("query", ns::DISCO_INFO) => IqPayload::Disco(Disco::try_from(elem)?), + ("query", ns::DISCO_INFO) => { + // TODO: separate all three types of payloads. + match DiscoInfoQuery::try_from(elem.clone()) { + Ok(payload) => IqPayload::DiscoInfoQuery(payload), + // TODO: put that error somewhere too? + Err(_) => IqPayload::DiscoInfoResult(DiscoInfoResult::try_from(elem)?), + } + }, // XEP-0047 ("open", ns::IBB) @@ -171,7 +179,8 @@ impl Into for IqPayload { fn into(self) -> Element { match self { IqPayload::Roster(roster) => roster.into(), - IqPayload::Disco(disco) => disco.into(), + IqPayload::DiscoInfoResult(disco) => disco.into(), + IqPayload::DiscoInfoQuery(disco) => disco.into(), IqPayload::IBB(ibb) => ibb.into(), IqPayload::Jingle(jingle) => jingle.into(), IqPayload::Ping(ping) => ping.into(), @@ -339,7 +348,7 @@ mod tests { _ => panic!(), }; assert!(match payload { - IqPayload::Disco(Disco { .. }) => true, + IqPayload::DiscoInfoQuery(DiscoInfoQuery { .. }) => true, _ => false, }); } From 0fcbf11644b2efe25527d6db87268cff9b3cea6a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Jul 2017 18:31:17 +0100 Subject: [PATCH 279/698] iq: Split IqPayload into all three possible types. --- src/iq.rs | 162 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 118 insertions(+), 44 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index 9420b44ccddc2c7227b14016205fe98edfecf3f5..976d79f8f7656b1ba1a61b7fa5c509b5b1057308 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -24,15 +24,35 @@ use jingle::Jingle; use ping::Ping; use mam::{Query as MamQuery, Fin as MamFin, Prefs as MamPrefs}; -/// Lists every known payload of a ``. +/// Lists every known payload of an ``. #[derive(Debug, Clone)] -pub enum IqPayload { +pub enum IqGetPayload { + Roster(Roster), + DiscoInfo(DiscoInfoQuery), + Ping(Ping), + MamQuery(MamQuery), + MamPrefs(MamPrefs), + + Unknown(Element), +} + +/// Lists every known payload of an ``. +#[derive(Debug, Clone)] +pub enum IqSetPayload { Roster(Roster), - DiscoInfoResult(DiscoInfoResult), - DiscoInfoQuery(DiscoInfoQuery), IBB(IBB), Jingle(Jingle), - Ping(Ping), + MamQuery(MamQuery), + MamPrefs(MamPrefs), + + Unknown(Element), +} + +/// Lists every known payload of an ``. +#[derive(Debug, Clone)] +pub enum IqResultPayload { + Roster(Roster), + DiscoInfo(DiscoInfoResult), MamQuery(MamQuery), MamFin(MamFin), MamPrefs(MamPrefs), @@ -40,45 +60,117 @@ pub enum IqPayload { Unknown(Element), } -impl TryFrom for IqPayload { +impl TryFrom for IqGetPayload { type Error = Error; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { // RFC-6121 - ("query", ns::ROSTER) => IqPayload::Roster(Roster::try_from(elem)?), + ("query", ns::ROSTER) => IqGetPayload::Roster(Roster::try_from(elem)?), // XEP-0030 - ("query", ns::DISCO_INFO) => { - // TODO: separate all three types of payloads. - match DiscoInfoQuery::try_from(elem.clone()) { - Ok(payload) => IqPayload::DiscoInfoQuery(payload), - // TODO: put that error somewhere too? - Err(_) => IqPayload::DiscoInfoResult(DiscoInfoResult::try_from(elem)?), - } - }, + ("query", ns::DISCO_INFO) => IqGetPayload::DiscoInfo(DiscoInfoQuery::try_from(elem)?), + + // XEP-0199 + ("ping", ns::PING) => IqGetPayload::Ping(Ping::try_from(elem)?), + + // XEP-0313 + ("query", ns::MAM) => IqGetPayload::MamQuery(MamQuery::try_from(elem)?), + ("prefs", ns::MAM) => IqGetPayload::MamPrefs(MamPrefs::try_from(elem)?), + + _ => IqGetPayload::Unknown(elem), + }) + } +} + +impl Into for IqGetPayload { + fn into(self) -> Element { + match self { + IqGetPayload::Roster(roster) => roster.into(), + IqGetPayload::DiscoInfo(disco) => disco.into(), + IqGetPayload::Ping(ping) => ping.into(), + IqGetPayload::MamQuery(query) => query.into(), + IqGetPayload::MamPrefs(prefs) => prefs.into(), + + IqGetPayload::Unknown(elem) => elem, + } + } +} + +impl TryFrom for IqSetPayload { + type Error = Error; + + fn try_from(elem: Element) -> Result { + Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { + // RFC-6121 + ("query", ns::ROSTER) => IqSetPayload::Roster(Roster::try_from(elem)?), // XEP-0047 ("open", ns::IBB) | ("data", ns::IBB) - | ("close", ns::IBB) => IqPayload::IBB(IBB::try_from(elem)?), + | ("close", ns::IBB) => IqSetPayload::IBB(IBB::try_from(elem)?), // XEP-0166 - ("jingle", ns::JINGLE) => IqPayload::Jingle(Jingle::try_from(elem)?), + ("jingle", ns::JINGLE) => IqSetPayload::Jingle(Jingle::try_from(elem)?), - // XEP-0199 - ("ping", ns::PING) => IqPayload::Ping(Ping::try_from(elem)?), + // XEP-0313 + ("query", ns::MAM) => IqSetPayload::MamQuery(MamQuery::try_from(elem)?), + ("prefs", ns::MAM) => IqSetPayload::MamPrefs(MamPrefs::try_from(elem)?), + + _ => IqSetPayload::Unknown(elem), + }) + } +} + +impl Into for IqSetPayload { + fn into(self) -> Element { + match self { + IqSetPayload::Roster(roster) => roster.into(), + IqSetPayload::IBB(ibb) => ibb.into(), + IqSetPayload::Jingle(jingle) => jingle.into(), + IqSetPayload::MamQuery(query) => query.into(), + IqSetPayload::MamPrefs(prefs) => prefs.into(), + + IqSetPayload::Unknown(elem) => elem, + } + } +} + +impl TryFrom for IqResultPayload { + type Error = Error; + + fn try_from(elem: Element) -> Result { + Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { + // RFC-6121 + ("query", ns::ROSTER) => IqResultPayload::Roster(Roster::try_from(elem)?), + + // XEP-0030 + ("query", ns::DISCO_INFO) => IqResultPayload::DiscoInfo(DiscoInfoResult::try_from(elem)?), // XEP-0313 - ("query", ns::MAM) => IqPayload::MamQuery(MamQuery::try_from(elem)?), - ("fin", ns::MAM) => IqPayload::MamFin(MamFin::try_from(elem)?), - ("prefs", ns::MAM) => IqPayload::MamPrefs(MamPrefs::try_from(elem)?), + ("query", ns::MAM) => IqResultPayload::MamQuery(MamQuery::try_from(elem)?), + ("fin", ns::MAM) => IqResultPayload::MamFin(MamFin::try_from(elem)?), + ("prefs", ns::MAM) => IqResultPayload::MamPrefs(MamPrefs::try_from(elem)?), - _ => IqPayload::Unknown(elem), + _ => IqResultPayload::Unknown(elem), }) } } +impl Into for IqResultPayload { + fn into(self) -> Element { + match self { + IqResultPayload::Roster(roster) => roster.into(), + IqResultPayload::DiscoInfo(disco) => disco.into(), + IqResultPayload::MamQuery(query) => query.into(), + IqResultPayload::MamFin(fin) => fin.into(), + IqResultPayload::MamPrefs(prefs) => prefs.into(), + + IqResultPayload::Unknown(elem) => elem, + } + } +} + #[derive(Debug, Clone)] pub enum IqType { Get(Element), @@ -175,24 +267,6 @@ impl TryFrom for Iq { } } -impl Into for IqPayload { - fn into(self) -> Element { - match self { - IqPayload::Roster(roster) => roster.into(), - IqPayload::DiscoInfoResult(disco) => disco.into(), - IqPayload::DiscoInfoQuery(disco) => disco.into(), - IqPayload::IBB(ibb) => ibb.into(), - IqPayload::Jingle(jingle) => jingle.into(), - IqPayload::Ping(ping) => ping.into(), - IqPayload::MamQuery(query) => query.into(), - IqPayload::MamFin(fin) => fin.into(), - IqPayload::MamPrefs(prefs) => prefs.into(), - - IqPayload::Unknown(elem) => elem, - } - } -} - impl Into for Iq { fn into(self) -> Element { let mut stanza = Element::builder("iq") @@ -344,11 +418,11 @@ mod tests { let elem: Element = "".parse().unwrap(); let iq = Iq::try_from(elem).unwrap(); let payload = match iq.payload { - IqType::Get(payload) => IqPayload::try_from(payload).unwrap(), + IqType::Get(payload) => IqGetPayload::try_from(payload).unwrap(), _ => panic!(), }; assert!(match payload { - IqPayload::DiscoInfoQuery(DiscoInfoQuery { .. }) => true, + IqGetPayload::DiscoInfo(DiscoInfoQuery { .. }) => true, _ => false, }); } From 1f43cd934b56bda603deb49efa22f6f07f0ed8b1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Jul 2017 20:03:15 +0100 Subject: [PATCH 280/698] Use TryFrom from the try_from crate, instead of the unstable feature. This makes xmpp-parsers usable on stable. --- Cargo.toml | 1 + src/attention.rs | 6 +++--- src/caps.rs | 4 ++-- src/chatstates.rs | 4 ++-- src/data_forms.rs | 4 ++-- src/delay.rs | 4 ++-- src/disco.rs | 6 +++--- src/ecaps2.rs | 4 ++-- src/eme.rs | 4 ++-- src/forwarding.rs | 4 ++-- src/hashes.rs | 4 ++-- src/ibb.rs | 4 ++-- src/ibr.rs | 4 ++-- src/idle.rs | 4 ++-- src/iq.rs | 10 +++++----- src/jingle.rs | 8 ++++---- src/jingle_ft.rs | 4 ++-- src/jingle_ibb.rs | 4 ++-- src/jingle_message.rs | 4 ++-- src/jingle_s5b.rs | 4 ++-- src/lib.rs | 3 +-- src/mam.rs | 10 +++++----- src/media_element.rs | 4 ++-- src/message.rs | 6 +++--- src/message_correct.rs | 4 ++-- src/muc/muc.rs | 4 ++-- src/muc/user.rs | 15 +++++++-------- src/ping.rs | 4 ++-- src/presence.rs | 6 +++--- src/pubsub/event.rs | 4 ++-- src/receipts.rs | 4 ++-- src/roster.rs | 6 +++--- src/rsm.rs | 4 ++-- src/stanza_error.rs | 4 ++-- src/stanza_id.rs | 4 ++-- 35 files changed, 86 insertions(+), 87 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 608f2ff536685715ccc4a39e5d221499772a85ce..b70538110dc033d592a6b06bab35c010d605c298 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,3 +22,4 @@ sha2 = "0.6.0" sha3 = "0.6.0" blake2 = "0.6.0" chrono = "0.4.0" +try_from = "0.2.2" diff --git a/src/attention.rs b/src/attention.rs index b28b5cc300967f03c455814f1008cd06c93c264b..c53040c4876bc0046e7f0b5974b13f5a5d87b9eb 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::Element; @@ -16,7 +16,7 @@ use ns; pub struct Attention; impl TryFrom for Attention { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("attention", ns::ATTENTION) { @@ -39,7 +39,7 @@ impl Into for Attention { #[cfg(test)] mod tests { - use std::convert::TryFrom; + use try_from::TryFrom; use minidom::Element; use error::Error; use super::Attention; diff --git a/src/caps.rs b/src/caps.rs index e883a64a7863f73979b902e9207e3622890146f2..aad50eb2d644807a73f50922ce8fe7058f0f7ec5 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use disco::{Feature, Identity, DiscoInfoResult, DiscoInfoQuery}; use data_forms::DataForm; @@ -29,7 +29,7 @@ pub struct Caps { } impl TryFrom for Caps { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("c", ns::CAPS) { diff --git a/src/chatstates.rs b/src/chatstates.rs index 47df01e0cd63330b65a1517c0c40fa3e40f97b33..ff1baa9563bf82dad351fcfe15aeb5002b0e9b5e 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::Element; @@ -22,7 +22,7 @@ pub enum ChatState { } impl TryFrom for ChatState { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if elem.ns() != Some(ns::CHATSTATES) { diff --git a/src/data_forms.rs b/src/data_forms.rs index 7e187ec194b60a1269e1fa78d5d72c467386d1cb..d4678c097573e6795345449330c3f67b248a87d5 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; @@ -103,7 +103,7 @@ pub struct DataForm { } impl TryFrom for DataForm { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("x", ns::DATA_FORMS) { diff --git a/src/delay.rs b/src/delay.rs index d744751da9ec01e743ae9358d0a3bf1319a6666b..7b56ab7eeeff733bb0f587ea4b2e280464b804bc 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::Element; use chrono::{DateTime, FixedOffset}; @@ -22,7 +22,7 @@ pub struct Delay { } impl TryFrom for Delay { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("delay", ns::DELAY) { diff --git a/src/disco.rs b/src/disco.rs index a36f997e6c89e2d7bd475bf404f94ef16e53b6b0..43727d51d82ba3ebf1d4ce1a6a04e200bee7dfb5 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::{Element, IntoElements, ElementEmitter}; @@ -19,7 +19,7 @@ pub struct DiscoInfoQuery { } impl TryFrom for DiscoInfoQuery { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("query", ns::DISCO_INFO) { @@ -103,7 +103,7 @@ pub struct DiscoInfoResult { } impl TryFrom for DiscoInfoResult { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("query", ns::DISCO_INFO) { diff --git a/src/ecaps2.rs b/src/ecaps2.rs index da74533d8e366f8135d9f2f7699201423fb7b059..0e92796de154b539ffd0323840d60fcba3599916 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use disco::{Feature, Identity, DiscoInfoResult, DiscoInfoQuery}; use data_forms::DataForm; @@ -26,7 +26,7 @@ pub struct ECaps2 { } impl TryFrom for ECaps2 { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("c", ns::ECAPS2) { diff --git a/src/eme.rs b/src/eme.rs index fe9738613ecafe0d5b7e7e13b7a280643bf3955e..ee71e21c75cd96c3a888654a6602c3955a9eb769 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::Element; @@ -19,7 +19,7 @@ pub struct ExplicitMessageEncryption { } impl TryFrom for ExplicitMessageEncryption { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("encryption", ns::EME) { diff --git a/src/forwarding.rs b/src/forwarding.rs index 9ad416b8fcf5ea23775bcac763b37235763c46c6..d10860ff30a9cee175461c38b6b26eca99ff7077 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::Element; @@ -23,7 +23,7 @@ pub struct Forwarded { } impl TryFrom for Forwarded { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("forwarded", ns::FORWARD) { diff --git a/src/hashes.rs b/src/hashes.rs index 07f2822c479fbe9feb8e06ec93308caaad00920f..6520fede0a90e880aa2ccee3df8276b24ed615aa 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; @@ -75,7 +75,7 @@ pub struct Hash { } impl TryFrom for Hash { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("hash", ns::HASHES) { diff --git a/src/ibb.rs b/src/ibb.rs index ad9873778afcfc4cb362fecb38fdd59ab0e9991d..969f34194fd25fe8836689b6b90d4aee4577976b 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; @@ -37,7 +37,7 @@ pub enum IBB { } impl TryFrom for IBB { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if elem.is("open", ns::IBB) { diff --git a/src/ibr.rs b/src/ibr.rs index 183d371638f203b948f522411a690a85192005e1..a0c7cc723b7f0e1947a618a054e8f012b1f532fc 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -5,7 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use std::collections::HashMap; -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::{Element, IntoElements, ElementEmitter}; @@ -26,7 +26,7 @@ pub struct Query { } impl TryFrom for Query { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("query", ns::REGISTER) { diff --git a/src/idle.rs b/src/idle.rs index 82009668bfbeed21044f8a7ef0b61f47222d2fd3..3809ff8b0e05b208291d504bf2d761caa5cd5067 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::Element; use chrono::{DateTime, FixedOffset}; @@ -19,7 +19,7 @@ pub struct Idle { } impl TryFrom for Idle { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("idle", ns::IDLE) { diff --git a/src/iq.rs b/src/iq.rs index 976d79f8f7656b1ba1a61b7fa5c509b5b1057308..1016102194fd1bf6846be50b96d8c78afb61e546 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -5,7 +5,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::Element; use minidom::IntoAttributeValue; @@ -61,7 +61,7 @@ pub enum IqResultPayload { } impl TryFrom for IqGetPayload { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { @@ -98,7 +98,7 @@ impl Into for IqGetPayload { } impl TryFrom for IqSetPayload { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { @@ -137,7 +137,7 @@ impl Into for IqSetPayload { } impl TryFrom for IqResultPayload { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { @@ -199,7 +199,7 @@ pub struct Iq { } impl TryFrom for Iq { - type Error = Error; + type Err = Error; fn try_from(root: Element) -> Result { if !root.is("iq", ns::JABBER_CLIENT) { diff --git a/src/jingle.rs b/src/jingle.rs index ece7fa023a89c934ad4400dd93047c8368e731f8..c9470326f32803c1f85c4714dd8be12f85c6993a 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; @@ -57,7 +57,7 @@ pub struct Content { } impl TryFrom for Content { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("content", ns::JINGLE) { @@ -196,7 +196,7 @@ pub struct ReasonElement { } impl TryFrom for ReasonElement { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("reason", ns::JINGLE) { @@ -261,7 +261,7 @@ pub struct Jingle { } impl TryFrom for Jingle { - type Error = Error; + type Err = Error; fn try_from(root: Element) -> Result { if !root.is("jingle", ns::JINGLE) { diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index a70b012b200b15dbec53db1bc0e44c2ca2d650a7..5e070cd5e6d8f4291ebf87da8c1e31749049914c 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use hashes::Hash; @@ -85,7 +85,7 @@ impl IntoElements for Received { } impl TryFrom for Description { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("description", ns::JINGLE_FT) { diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 72ebba550ea5ae6e9ac6d02faadd8a7d210624fa..08a313297e88c1b16a5b3d9a83229679c54f0b3a 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; @@ -25,7 +25,7 @@ pub struct Transport { } impl TryFrom for Transport { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("transport", ns::JINGLE_IBB) { diff --git a/src/jingle_message.rs b/src/jingle_message.rs index d61133427655ebeb9037c259279bdc37dd91341b..216e1877386fa185894a9bb0936c111b833693b9 100644 --- a/src/jingle_message.rs +++ b/src/jingle_message.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::Element; @@ -44,7 +44,7 @@ fn check_empty_and_get_sid(elem: Element) -> Result { } impl TryFrom for JingleMI { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if elem.ns() != Some(ns::JINGLE_MESSAGE) { diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 57807be313ec817c1fbd9d7c8f44791c568d2e7f..603dbb242f7246e7ed4e388e3d895f90bb31020d 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; @@ -73,7 +73,7 @@ pub struct Transport { } impl TryFrom for Transport { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if elem.is("transport", ns::JINGLE_S5B) { diff --git a/src/lib.rs b/src/lib.rs index f82e6df349d6d63df7f87a9fb64fb7e2261f8e51..35dacabd7cf1c705514a6ce56cf46383b2d6ba58 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,8 +13,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -#![feature(try_from)] - extern crate minidom; extern crate jid; extern crate base64; @@ -24,6 +22,7 @@ extern crate sha2; extern crate sha3; extern crate blake2; extern crate chrono; +extern crate try_from; macro_rules! get_attr { ($elem:ident, $attr:tt, $type:tt) => ( diff --git a/src/mam.rs b/src/mam.rs index 920cf8d01a76ae44d95839ad658405e99372dcd6..62b035e1691487c1932bdf1dd54de955c3b92db6 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; @@ -53,7 +53,7 @@ pub struct Prefs { } impl TryFrom for Query { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("query", ns::MAM) { @@ -77,7 +77,7 @@ impl TryFrom for Query { } impl TryFrom for Result_ { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("result", ns::MAM) { @@ -103,7 +103,7 @@ impl TryFrom for Result_ { } impl TryFrom for Fin { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("fin", ns::MAM) { @@ -129,7 +129,7 @@ impl TryFrom for Fin { } impl TryFrom for Prefs { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("prefs", ns::MAM) { diff --git a/src/media_element.rs b/src/media_element.rs index b1c36a64638fe7ee28a8de7ab376e4007a1f3e55..aa15efb5637e8d440f40a7de5f1d58d6fedd5fd1 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::{Element, IntoElements, ElementEmitter}; @@ -42,7 +42,7 @@ pub struct MediaElement { } impl TryFrom for MediaElement { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("media", ns::MEDIA_ELEMENT) { diff --git a/src/message.rs b/src/message.rs index 47c8db16eaebfbbb8256a7a45a2ce91a589bb4d0..3c7fefc8e4065e11e29d149157d8b833ca4ea2dd 100644 --- a/src/message.rs +++ b/src/message.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use std::str::FromStr; use std::collections::BTreeMap; @@ -43,7 +43,7 @@ pub enum MessagePayload { } impl TryFrom for MessagePayload { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { @@ -143,7 +143,7 @@ impl Message { } impl TryFrom for Message { - type Error = Error; + type Err = Error; fn try_from(root: Element) -> Result { if !root.is("message", ns::JABBER_CLIENT) { diff --git a/src/message_correct.rs b/src/message_correct.rs index 8ce747f114d9139b843c58bf34d52d8a46ec5043..5376f8604370de2e889bd3a642cb521a1e81d9f7 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::Element; @@ -18,7 +18,7 @@ pub struct Replace { } impl TryFrom for Replace { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("replace", ns::MESSAGE_CORRECT) { diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 75667d877352ccd6e38404a1a16ef4e1431cf7db..e7e6a238e0b64329df12150a4ab1de9cb990b7e7 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::Element; @@ -18,7 +18,7 @@ pub struct Muc { } impl TryFrom for Muc { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("x", ns::MUC) { diff --git a/src/muc/user.rs b/src/muc/user.rs index 833b5604a959b43f9455f0003331b001f1646574..1609b0c8d1680df7d7a4f66e18d282116e43e61a 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -4,8 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; -use std::convert::TryInto; +use try_from::{TryFrom, TryInto}; use std::str::FromStr; use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; @@ -74,7 +73,7 @@ pub enum Status { } impl TryFrom for Status { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("status", ns::MUC_USER) { @@ -160,7 +159,7 @@ pub enum Actor { } impl TryFrom for Actor { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("actor", ns::MUC_USER) { @@ -210,7 +209,7 @@ pub struct Continue { } impl TryFrom for Continue { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("continue", ns::MUC_USER) { @@ -247,7 +246,7 @@ impl IntoElements for Continue { pub struct Reason(String); impl TryFrom for Reason { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("reason", ns::MUC_USER) { @@ -305,7 +304,7 @@ pub struct Item { } impl TryFrom for Item { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("item", ns::MUC_USER) { @@ -374,7 +373,7 @@ pub struct MucUser { } impl TryFrom for MucUser { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("x", ns::MUC_USER) { diff --git a/src/ping.rs b/src/ping.rs index 84a7cebf0d9796abc09046bb29346a617c84b99c..272044517ae4b2873212364011b8946f284e3e26 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -5,7 +5,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::Element; @@ -17,7 +17,7 @@ use ns; pub struct Ping; impl TryFrom for Ping { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("ping", ns::PING) { diff --git a/src/presence.rs b/src/presence.rs index 74458797a457e320a0e158790cca120f5f8b90c4..1eb0b23fb86f56faec9065f417d0ddaa78c4dc95 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -5,7 +5,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use std::str::FromStr; use std::collections::BTreeMap; @@ -91,7 +91,7 @@ pub enum PresencePayload { } impl TryFrom for PresencePayload { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { @@ -214,7 +214,7 @@ impl Presence { } impl TryFrom for Presence { - type Error = Error; + type Err = Error; fn try_from(root: Element) -> Result { if !root.is("presence", ns::JABBER_CLIENT) { diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 07e465c89a59dc3c7bdc3af520b1f787751dd588..ef5e994b8abb4471c07d78374e17e0a0eae660d7 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; @@ -138,7 +138,7 @@ fn parse_items(elem: Element, node: String) -> Result { } impl TryFrom for PubSubEvent { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("event", ns::PUBSUB_EVENT) { diff --git a/src/receipts.rs b/src/receipts.rs index 72b357a1de277ef15308ed3704a6c0a58fe17cb7..ae564f747066bdb0b3ec0244abb5507d5b51245b 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::Element; @@ -19,7 +19,7 @@ pub enum Receipt { } impl TryFrom for Receipt { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { for _ in elem.children() { diff --git a/src/roster.rs b/src/roster.rs index db939ad8135ae537040af90125947eacdca36e4e..4e6a38c6d1bf9d6684db15746b6a56688adf50dc 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; @@ -32,7 +32,7 @@ pub struct Item { } impl TryFrom for Item { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("item", ns::ROSTER) { @@ -83,7 +83,7 @@ pub struct Roster { } impl TryFrom for Roster { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("query", ns::ROSTER) { diff --git a/src/rsm.rs b/src/rsm.rs index e8d7109d8465dc7867077cfa0375bb5aa0ce2d05..cfb6f29243140d517ff9b07b3bbc93f3aa916d90 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::Element; @@ -25,7 +25,7 @@ pub struct Set { } impl TryFrom for Set { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("set", ns::RSM) { diff --git a/src/stanza_error.rs b/src/stanza_error.rs index badd1efab7606026dde90871142add5083bb35a7..c25c5792ec0ac234f3d9fc34f714dffa14ead38c 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use std::str::FromStr; use std::collections::BTreeMap; @@ -122,7 +122,7 @@ pub struct StanzaError { } impl TryFrom for StanzaError { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("error", ns::JABBER_CLIENT) { diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 38008d0c5c8230050c7a12c8f200e9dccebe88fc..634def50a063c0ecbc159e0012fe7df270c2bee1 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::Element; use jid::Jid; @@ -25,7 +25,7 @@ pub enum StanzaId { } impl TryFrom for StanzaId { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { let is_stanza_id = elem.is("stanza-id", ns::SID); From 5ec921fa6fa5a0e9888396286c593c45d80ba240 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Jul 2017 20:11:00 +0100 Subject: [PATCH 281/698] attention: Add missing unknown attribute check. --- src/attention.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/attention.rs b/src/attention.rs index c53040c4876bc0046e7f0b5974b13f5a5d87b9eb..333d19000e740c2c4d3f1543849667bfef8cc241 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -25,6 +25,9 @@ impl TryFrom for Attention { for _ in elem.children() { return Err(Error::ParseError("Unknown child in attention element.")); } + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown attribute in attention element.")); + } Ok(Attention) } } @@ -61,6 +64,17 @@ mod tests { assert_eq!(message, "Unknown child in attention element."); } + #[test] + fn test_invalid_attribute() { + let elem: Element = "".parse().unwrap(); + let error = Attention::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in attention element."); + } + #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); From 487dbdc6de0e429ac5c0bef88ac6f4ef1709452a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Jul 2017 20:36:13 +0100 Subject: [PATCH 282/698] =?UTF-8?q?Replace=20Into=20with=20From=20for=20Element.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows Element::from() to work, and since Into is automatically implemented for any type implementing From there is no change to existing code. --- src/attention.rs | 4 +-- src/chatstates.rs | 6 ++-- src/delay.rs | 10 +++---- src/disco.rs | 30 ++++++++++---------- src/ecaps2.rs | 10 +++---- src/eme.rs | 8 +++--- src/forwarding.rs | 8 +++--- src/hashes.rs | 8 +++--- src/ibb.rs | 6 ++-- src/ibr.rs | 12 ++++---- src/idle.rs | 6 ++-- src/iq.rs | 32 ++++++++++----------- src/jingle.rs | 49 ++++++++++++++++---------------- src/jingle_ft.rs | 24 ++++++++-------- src/jingle_ibb.rs | 10 +++---- src/jingle_s5b.rs | 28 +++++++++--------- src/mam.rs | 44 ++++++++++++++--------------- src/message.rs | 64 +++++++++++++++++++++--------------------- src/message_correct.rs | 6 ++-- src/muc/muc.rs | 7 +++-- src/muc/user.rs | 49 ++++++++++++++++---------------- src/ping.rs | 4 +-- src/presence.rs | 26 ++++++++--------- src/receipts.rs | 6 ++-- src/roster.rs | 20 ++++++------- src/rsm.rs | 34 +++++++++++----------- src/stanza_error.rs | 14 ++++----- src/stanza_id.rs | 6 ++-- 28 files changed, 266 insertions(+), 265 deletions(-) diff --git a/src/attention.rs b/src/attention.rs index 333d19000e740c2c4d3f1543849667bfef8cc241..b72079b9599de39e50cfa2f9664ace6c6cfe531c 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -32,8 +32,8 @@ impl TryFrom for Attention { } } -impl Into for Attention { - fn into(self) -> Element { +impl From for Element { + fn from(_: Attention) -> Element { Element::builder("attention") .ns(ns::ATTENTION) .build() diff --git a/src/chatstates.rs b/src/chatstates.rs index ff1baa9563bf82dad351fcfe15aeb5002b0e9b5e..a5af910c6dca44762f9305f30f15ed5723e653b1 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -45,9 +45,9 @@ impl TryFrom for ChatState { } } -impl Into for ChatState { - fn into(self) -> Element { - Element::builder(match self { +impl From for Element { + fn from(chatstate: ChatState) -> Element { + Element::builder(match chatstate { ChatState::Active => "active", ChatState::Composing => "composing", ChatState::Gone => "gone", diff --git a/src/delay.rs b/src/delay.rs index 7b56ab7eeeff733bb0f587ea4b2e280464b804bc..b96db2bdc2440081688b5c45cf460ab622812f98 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -45,13 +45,13 @@ impl TryFrom for Delay { } } -impl Into for Delay { - fn into(self) -> Element { +impl From for Element { + fn from(delay: Delay) -> Element { Element::builder("delay") .ns(ns::DELAY) - .attr("from", self.from.and_then(|value| Some(String::from(value)))) - .attr("stamp", self.stamp.to_rfc3339()) - .append(self.data) + .attr("from", delay.from.and_then(|value| Some(String::from(value)))) + .attr("stamp", delay.stamp.to_rfc3339()) + .append(delay.data) .build() } } diff --git a/src/disco.rs b/src/disco.rs index 43727d51d82ba3ebf1d4ce1a6a04e200bee7dfb5..aee3de6ae612cacb8e40e35ef6bb66c0f8baab23 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -53,11 +53,11 @@ pub struct Feature { pub var: String, } -impl Into for Feature { - fn into(self) -> Element { +impl From for Element { + fn from(feature: Feature) -> Element { Element::builder("feature") .ns(ns::DISCO_INFO) - .attr("var", self.var) + .attr("var", feature.var) .build() } } @@ -76,14 +76,14 @@ pub struct Identity { pub name: Option, } -impl Into for Identity { - fn into(self) -> Element { +impl From for Element { + fn from(identity: Identity) -> Element { Element::builder("identity") .ns(ns::DISCO_INFO) - .attr("category", self.category) - .attr("type", self.type_) - .attr("xml:lang", self.lang) - .attr("name", self.name) + .attr("category", identity.category) + .attr("type", identity.type_) + .attr("xml:lang", identity.lang) + .attr("name", identity.name) .build() } } @@ -174,16 +174,16 @@ impl TryFrom for DiscoInfoResult { } } -impl Into for DiscoInfoResult { - fn into(self) -> Element { - for _ in self.extensions { +impl From for Element { + fn from(disco: DiscoInfoResult) -> Element { + for _ in disco.extensions { panic!("Not yet implemented!"); } Element::builder("query") .ns(ns::DISCO_INFO) - .attr("node", self.node) - .append(self.identities) - .append(self.features) + .attr("node", disco.node) + .append(disco.identities) + .append(disco.features) .build() } } diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 0e92796de154b539ffd0323840d60fcba3599916..54228c8d78a9c0cd4d61033532c25fd2720712df 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -47,13 +47,13 @@ impl TryFrom for ECaps2 { } } -impl Into for ECaps2 { - fn into(mut self) -> Element { +impl From for Element { + fn from(mut ecaps2: ECaps2) -> Element { Element::builder("c") .ns(ns::ECAPS2) - .append(self.hashes.drain(..) - .map(|hash| hash.into()) - .collect::>()) + .append(ecaps2.hashes.drain(..) + .map(|hash| hash.into()) + .collect::>()) .build() } } diff --git a/src/eme.rs b/src/eme.rs index ee71e21c75cd96c3a888654a6602c3955a9eb769..ba07f95cef5613123e180f88bd1148cc93970510 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -35,12 +35,12 @@ impl TryFrom for ExplicitMessageEncryption { } } -impl Into for ExplicitMessageEncryption { - fn into(self) -> Element { +impl From for Element { + fn from(eme: ExplicitMessageEncryption) -> Element { Element::builder("encryption") .ns(ns::EME) - .attr("namespace", self.namespace) - .attr("name", self.name) + .attr("namespace", eme.namespace) + .attr("name", eme.name) .build() } } diff --git a/src/forwarding.rs b/src/forwarding.rs index d10860ff30a9cee175461c38b6b26eca99ff7077..3afaabdf6a1af5d36e8f22e265a3c35a2fe0a830 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -48,12 +48,12 @@ impl TryFrom for Forwarded { } } -impl Into for Forwarded { - fn into(self) -> Element { +impl From for Element { + fn from(forwarded: Forwarded) -> Element { Element::builder("forwarded") .ns(ns::FORWARD) - .append(match self.delay { Some(delay) => { let elem: Element = delay.into(); Some(elem) }, None => None }) - .append(match self.stanza { Some(stanza) => { let elem: Element = stanza.into(); Some(elem) }, None => None }) + .append(match forwarded.delay { Some(delay) => { let elem: Element = delay.into(); Some(elem) }, None => None }) + .append(match forwarded.stanza { Some(stanza) => { let elem: Element = stanza.into(); Some(elem) }, None => None }) .build() } } diff --git a/src/hashes.rs b/src/hashes.rs index 6520fede0a90e880aa2ccee3df8276b24ed615aa..bcaf94ac1e874ca641239c74a8987711364bd500 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -96,12 +96,12 @@ impl TryFrom for Hash { } } -impl Into for Hash { - fn into(self) -> Element { +impl From for Element { + fn from(hash: Hash) -> Element { Element::builder("hash") .ns(ns::HASHES) - .attr("algo", self.algo) - .append(base64::encode(&self.hash)) + .attr("algo", hash.algo) + .append(base64::encode(&hash.hash)) .build() } } diff --git a/src/ibb.rs b/src/ibb.rs index 969f34194fd25fe8836689b6b90d4aee4577976b..cdc9b5cdb2e5395d6fe18ce8be1478be0f64dff3 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -78,9 +78,9 @@ impl TryFrom for IBB { } } -impl Into for IBB { - fn into(self) -> Element { - match self { +impl From for Element { + fn from(ibb: IBB) -> Element { + match ibb { IBB::Open { block_size, sid, stanza } => { Element::builder("open") .ns(ns::IBB) diff --git a/src/ibr.rs b/src/ibr.rs index a0c7cc723b7f0e1947a618a054e8f012b1f532fc..37c54f7b3428424a1ce8ce9b3b8704e4ea326ebe 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -64,16 +64,16 @@ impl TryFrom for Query { } } -impl Into for Query { - fn into(self) -> Element { +impl From for Element { + fn from(query: Query) -> Element { Element::builder("query") .ns(ns::REGISTER) - .append(if self.registered { Some(Element::builder("registered").ns(ns::REGISTER)) } else { None }) - .append(self.fields.iter().map(|(name, value)| { + .append(if query.registered { Some(Element::builder("registered").ns(ns::REGISTER)) } else { None }) + .append(query.fields.iter().map(|(name, value)| { Element::builder(name.clone()).ns(ns::REGISTER).append(value.clone()) }).collect::>()) - .append(if self.remove { Some(Element::builder("remove").ns(ns::REGISTER)) } else { None }) - .append(self.form) + .append(if query.remove { Some(Element::builder("remove").ns(ns::REGISTER)) } else { None }) + .append(query.form) .build() } } diff --git a/src/idle.rs b/src/idle.rs index 3809ff8b0e05b208291d504bf2d761caa5cd5067..51c203934056755d7a07bc771aea6837ced92d63 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -33,11 +33,11 @@ impl TryFrom for Idle { } } -impl Into for Idle { - fn into(self) -> Element { +impl From for Element { + fn from(idle: Idle) -> Element { Element::builder("idle") .ns(ns::IDLE) - .attr("since", self.since.to_rfc3339()) + .attr("since", idle.since.to_rfc3339()) .build() } } diff --git a/src/iq.rs b/src/iq.rs index 1016102194fd1bf6846be50b96d8c78afb61e546..d1078b348706686a8e1f917cc0cff0d14654ae05 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -83,9 +83,9 @@ impl TryFrom for IqGetPayload { } } -impl Into for IqGetPayload { - fn into(self) -> Element { - match self { +impl From for Element { + fn from(payload: IqGetPayload) -> Element { + match payload { IqGetPayload::Roster(roster) => roster.into(), IqGetPayload::DiscoInfo(disco) => disco.into(), IqGetPayload::Ping(ping) => ping.into(), @@ -122,9 +122,9 @@ impl TryFrom for IqSetPayload { } } -impl Into for IqSetPayload { - fn into(self) -> Element { - match self { +impl From for Element { + fn from(payload: IqSetPayload) -> Element { + match payload { IqSetPayload::Roster(roster) => roster.into(), IqSetPayload::IBB(ibb) => ibb.into(), IqSetPayload::Jingle(jingle) => jingle.into(), @@ -157,9 +157,9 @@ impl TryFrom for IqResultPayload { } } -impl Into for IqResultPayload { - fn into(self) -> Element { - match self { +impl From for Element { + fn from(payload: IqResultPayload) -> Element { + match payload { IqResultPayload::Roster(roster) => roster.into(), IqResultPayload::DiscoInfo(disco) => disco.into(), IqResultPayload::MamQuery(query) => query.into(), @@ -267,16 +267,16 @@ impl TryFrom for Iq { } } -impl Into for Iq { - fn into(self) -> Element { +impl From for Element { + fn from(iq: Iq) -> Element { let mut stanza = Element::builder("iq") .ns(ns::JABBER_CLIENT) - .attr("from", self.from.and_then(|value| Some(String::from(value)))) - .attr("to", self.to.and_then(|value| Some(String::from(value)))) - .attr("id", self.id) - .attr("type", &self.payload) + .attr("from", iq.from.and_then(|value| Some(String::from(value)))) + .attr("to", iq.to.and_then(|value| Some(String::from(value)))) + .attr("id", iq.id) + .attr("type", &iq.payload) .build(); - let elem = match self.payload { + let elem = match iq.payload { IqType::Get(elem) | IqType::Set(elem) | IqType::Result(Some(elem)) => elem, diff --git a/src/jingle.rs b/src/jingle.rs index c9470326f32803c1f85c4714dd8be12f85c6993a..1d0c07f15434050eb1dfdc39c4a1ca3afaf57aac 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -95,17 +95,17 @@ impl TryFrom for Content { } } -impl Into for Content { - fn into(self) -> Element { +impl From for Element { + fn from(content: Content) -> Element { Element::builder("content") .ns(ns::JINGLE) - .attr("creator", self.creator) - .attr("disposition", self.disposition) - .attr("name", self.name) - .attr("senders", self.senders) - .append(self.description) - .append(self.transport) - .append(self.security) + .attr("creator", content.creator) + .attr("disposition", content.disposition) + .attr("name", content.name) + .attr("senders", content.senders) + .append(content.description) + .append(content.transport) + .append(content.security) .build() } } @@ -165,9 +165,9 @@ impl FromStr for Reason { } } -impl Into for Reason { - fn into(self) -> Element { - Element::builder(match self { +impl From for Element { + fn from(reason: Reason) -> Element { + Element::builder(match reason { Reason::AlternativeSession => "alternative-session", Reason::Busy => "busy", Reason::Cancel => "cancel", @@ -231,12 +231,11 @@ impl TryFrom for ReasonElement { } } -impl Into for ReasonElement { - fn into(self) -> Element { - let reason: Element = self.reason.into(); +impl From for Element { + fn from(reason: ReasonElement) -> Element { Element::builder("reason") - .append(reason) - .append(self.text) + .append(Element::from(reason.reason)) + .append(reason.text) .build() } } @@ -297,16 +296,16 @@ impl TryFrom for Jingle { } } -impl Into for Jingle { - fn into(self) -> Element { +impl From for Element { + fn from(jingle: Jingle) -> Element { Element::builder("jingle") .ns(ns::JINGLE) - .attr("action", self.action) - .attr("initiator", match self.initiator { Some(initiator) => Some(String::from(initiator)), None => None }) - .attr("responder", match self.responder { Some(responder) => Some(String::from(responder)), None => None }) - .attr("sid", self.sid) - .append(self.contents) - .append(self.reason) + .attr("action", jingle.action) + .attr("initiator", match jingle.initiator { Some(initiator) => Some(String::from(initiator)), None => None }) + .attr("responder", match jingle.responder { Some(responder) => Some(String::from(responder)), None => None }) + .attr("sid", jingle.sid) + .append(jingle.contents) + .append(jingle.reason) .build() } } diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 5e070cd5e6d8f4291ebf87da8c1e31749049914c..7bd0df8d5f2462c8cd740ddb7fc8b765141b661b 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -172,57 +172,57 @@ impl TryFrom for Description { } } -impl Into for File { - fn into(self) -> Element { +impl From for Element { + fn from(file: File) -> Element { let mut root = Element::builder("file") .ns(ns::JINGLE_FT) .build(); - if let Some(date) = self.date { + if let Some(date) = file.date { root.append_child(Element::builder("date") .ns(ns::JINGLE_FT) .append(date.to_rfc3339()) .build()); } - if let Some(media_type) = self.media_type { + if let Some(media_type) = file.media_type { root.append_child(Element::builder("media-type") .ns(ns::JINGLE_FT) .append(media_type) .build()); } - if let Some(name) = self.name { + if let Some(name) = file.name { root.append_child(Element::builder("name") .ns(ns::JINGLE_FT) .append(name) .build()); } - if let Some(desc) = self.desc { + if let Some(desc) = file.desc { root.append_child(Element::builder("desc") .ns(ns::JINGLE_FT) .append(desc) .build()); } - if let Some(size) = self.size { + if let Some(size) = file.size { root.append_child(Element::builder("size") .ns(ns::JINGLE_FT) .append(format!("{}", size)) .build()); } - if let Some(range) = self.range { + if let Some(range) = file.range { root.append_child(Element::builder("range") .ns(ns::JINGLE_FT) .append(range) .build()); } - for hash in self.hashes { + for hash in file.hashes { root.append_child(hash.into()); } root } } -impl Into for Description { - fn into(self) -> Element { - let file: Element = self.file.into(); +impl From for Element { + fn from(description: Description) -> Element { + let file: Element = description.file.into(); Element::builder("description") .ns(ns::JINGLE_FT) .append(file) diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 08a313297e88c1b16a5b3d9a83229679c54f0b3a..ed1c6d189ec5c5662aa4aab3682bd58cde9223a9 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -42,13 +42,13 @@ impl TryFrom for Transport { } } -impl Into for Transport { - fn into(self) -> Element { +impl From for Element { + fn from(transport: Transport) -> Element { Element::builder("transport") .ns(ns::JINGLE_IBB) - .attr("block-size", self.block_size) - .attr("sid", self.sid) - .attr("stanza", self.stanza) + .attr("block-size", transport.block_size) + .attr("sid", transport.sid) + .attr("stanza", transport.stanza) .build() } } diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 603dbb242f7246e7ed4e388e3d895f90bb31020d..268529047efa6b0eb497cdf3f8752c7c247db109 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -40,16 +40,16 @@ pub struct Candidate { pub type_: Type, } -impl Into for Candidate { - fn into(self) -> Element { +impl From for Element { + fn from(candidate: Candidate) -> Element { Element::builder("candidate") .ns(ns::JINGLE_S5B) - .attr("cid", self.cid) - .attr("host", self.host) - .attr("jid", String::from(self.jid)) - .attr("port", self.port) - .attr("priority", self.priority) - .attr("type", self.type_) + .attr("cid", candidate.cid) + .attr("host", candidate.host) + .attr("jid", String::from(candidate.jid)) + .attr("port", candidate.port) + .attr("priority", candidate.priority) + .attr("type", candidate.type_) .build() } } @@ -137,14 +137,14 @@ impl TryFrom for Transport { } } -impl Into for Transport { - fn into(self) -> Element { +impl From for Element { + fn from(transport: Transport) -> Element { Element::builder("transport") .ns(ns::JINGLE_S5B) - .attr("sid", self.sid) - .attr("dstaddr", self.dstaddr) - .attr("mode", self.mode) - .append(match self.payload { + .attr("sid", transport.sid) + .attr("dstaddr", transport.dstaddr) + .attr("mode", transport.mode) + .append(match transport.payload { TransportPayload::Candidates(mut candidates) => { candidates.drain(..) .map(|candidate| candidate.into()) diff --git a/src/mam.rs b/src/mam.rs index 62b035e1691487c1932bdf1dd54de955c3b92db6..bbc08a6d19b1736779d54cf5d522f3101c55eb8e 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -161,52 +161,52 @@ impl TryFrom for Prefs { } } -impl Into for Query { - fn into(self) -> Element { +impl From for Element { + fn from(query: Query) -> Element { Element::builder("query") .ns(ns::MAM) - .attr("queryid", self.queryid) - .attr("node", self.node) - //.append(self.form.map(|form| -> Element { form.into() })) - .append(self.set.map(|set| -> Element { set.into() })) + .attr("queryid", query.queryid) + .attr("node", query.node) + //.append(query.form.map(|form| -> Element { form.into() })) + .append(query.set.map(|set| -> Element { set.into() })) .build() } } -impl Into for Result_ { - fn into(self) -> Element { +impl From for Element { + fn from(result: Result_) -> Element { let mut elem = Element::builder("result") .ns(ns::MAM) - .attr("queryid", self.queryid) - .attr("id", self.id) + .attr("queryid", result.queryid) + .attr("id", result.id) .build(); - elem.append_child(self.forwarded.into()); + elem.append_child(result.forwarded.into()); elem } } -impl Into for Fin { - fn into(self) -> Element { +impl From for Element { + fn from(fin: Fin) -> Element { let mut elem = Element::builder("fin") .ns(ns::MAM) - .attr("complete", if self.complete { Some("true") } else { None }) + .attr("complete", if fin.complete { Some("true") } else { None }) .build(); - elem.append_child(self.set.into()); + elem.append_child(fin.set.into()); elem } } -impl Into for Prefs { - fn into(self) -> Element { +impl From for Element { + fn from(prefs: Prefs) -> Element { let mut elem = Element::builder("prefs") .ns(ns::MAM) - .attr("default", self.default_) + .attr("default", prefs.default_) .build(); - if !self.always.is_empty() { + if !prefs.always.is_empty() { let mut always = Element::builder("always") .ns(ns::RSM) .build(); - for jid in self.always { + for jid in prefs.always { always.append_child(Element::builder("jid") .ns(ns::RSM) .append(String::from(jid)) @@ -214,11 +214,11 @@ impl Into for Prefs { } elem.append_child(always); } - if !self.never.is_empty() { + if !prefs.never.is_empty() { let mut never = Element::builder("never") .ns(ns::RSM) .build(); - for jid in self.never { + for jid in prefs.never { never.append_child(Element::builder("jid") .ns(ns::RSM) .append(String::from(jid)) diff --git a/src/message.rs b/src/message.rs index 3c7fefc8e4065e11e29d149157d8b833ca4ea2dd..03fc2e8c79dc970ecc3e6edae9a499813abd1b1f 100644 --- a/src/message.rs +++ b/src/message.rs @@ -84,9 +84,9 @@ impl TryFrom for MessagePayload { } } -impl Into for MessagePayload { - fn into(self) -> Element { - match self { +impl From for Element { + fn from(payload: MessagePayload) -> Element { + match payload { MessagePayload::StanzaError(stanza_error) => stanza_error.into(), MessagePayload::Attention(attention) => attention.into(), MessagePayload::ChatState(chatstate) => chatstate.into(), @@ -199,37 +199,37 @@ impl TryFrom for Message { } } -impl Into for Message { - fn into(self) -> Element { +impl From for Element { + fn from(message: Message) -> Element { Element::builder("message") .ns(ns::JABBER_CLIENT) - .attr("from", self.from.and_then(|value| Some(String::from(value)))) - .attr("to", self.to.and_then(|value| Some(String::from(value)))) - .attr("id", self.id) - .attr("type", self.type_) - .append(self.subjects.iter() - .map(|(lang, subject)| { - Element::builder("subject") - .ns(ns::JABBER_CLIENT) - .attr("xml:lang", match lang.as_ref() { - "" => None, - lang => Some(lang), - }) - .append(subject) - .build() }) - .collect::>()) - .append(self.bodies.iter() - .map(|(lang, body)| { - Element::builder("body") - .ns(ns::JABBER_CLIENT) - .attr("xml:lang", match lang.as_ref() { - "" => None, - lang => Some(lang), - }) - .append(body) - .build() }) - .collect::>()) - .append(self.payloads) + .attr("from", message.from.and_then(|value| Some(String::from(value)))) + .attr("to", message.to.and_then(|value| Some(String::from(value)))) + .attr("id", message.id) + .attr("type", message.type_) + .append(message.subjects.iter() + .map(|(lang, subject)| { + Element::builder("subject") + .ns(ns::JABBER_CLIENT) + .attr("xml:lang", match lang.as_ref() { + "" => None, + lang => Some(lang), + }) + .append(subject) + .build() }) + .collect::>()) + .append(message.bodies.iter() + .map(|(lang, body)| { + Element::builder("body") + .ns(ns::JABBER_CLIENT) + .attr("xml:lang", match lang.as_ref() { + "" => None, + lang => Some(lang), + }) + .append(body) + .build() }) + .collect::>()) + .append(message.payloads) .build() } } diff --git a/src/message_correct.rs b/src/message_correct.rs index 5376f8604370de2e889bd3a642cb521a1e81d9f7..bb5c2b2e25cc037aa1305f52ac11bf47067b6200 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -37,11 +37,11 @@ impl TryFrom for Replace { } } -impl Into for Replace { - fn into(self) -> Element { +impl From for Element { + fn from(replace: Replace) -> Element { Element::builder("replace") .ns(ns::MESSAGE_CORRECT) - .attr("id", self.id) + .attr("id", replace.id) .build() } } diff --git a/src/muc/muc.rs b/src/muc/muc.rs index e7e6a238e0b64329df12150a4ab1de9cb990b7e7..550cc61e6c603215cbfb94994570ce9eebf7fee7 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -1,4 +1,5 @@ // Copyright (c) 2017 Maxime “pep” Buquet +// Copyright (c) 2017 Emmanuel Gil Peyrot // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -44,11 +45,11 @@ impl TryFrom for Muc { } } -impl Into for Muc { - fn into(self) -> Element { +impl From for Element { + fn from(muc: Muc) -> Element { Element::builder("x") .ns(ns::MUC) - .append(self.password) + .append(muc.password) .build() } } diff --git a/src/muc/user.rs b/src/muc/user.rs index 1609b0c8d1680df7d7a4f66e18d282116e43e61a..48da79666889ebb5357f15ba3d50c0fa0ef5e03e 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -1,4 +1,5 @@ // Copyright (c) 2017 Maxime “pep” Buquet +// Copyright (c) 2017 Emmanuel Gil Peyrot // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -113,11 +114,11 @@ impl TryFrom for Status { } } -impl Into for Status { - fn into(self) -> Element { +impl From for Element { + fn from(status: Status) -> Element { Element::builder("status") .ns(ns::MUC_USER) - .attr("code", match self { + .attr("code", match status { Status::NonAnonymousRoom => 100, Status::AffiliationChange => 101, Status::ConfigShowsUnavailableMembers => 102, @@ -186,11 +187,11 @@ impl TryFrom for Actor { } } -impl Into for Actor { - fn into(self) -> Element { +impl From for Element { + fn from(actor: Actor) -> Element { let elem = Element::builder("actor").ns(ns::MUC_USER); - (match self { + (match actor { Actor::Jid(jid) => elem.attr("jid", String::from(jid)), Actor::Nick(nick) => elem.attr("nick", nick), }).build() @@ -227,11 +228,11 @@ impl TryFrom for Continue { } } -impl Into for Continue { - fn into(self) -> Element { +impl From for Element { + fn from(cont: Continue) -> Element { Element::builder("continue") .ns(ns::MUC_USER) - .attr("thread", self.thread) + .attr("thread", cont.thread) .build() } } @@ -262,11 +263,11 @@ impl TryFrom for Reason { } } -impl Into for Reason { - fn into(self) -> Element { +impl From for Element { + fn from(reason: Reason) -> Element { Element::builder("reason") .ns(ns::MUC_USER) - .append(self.0) + .append(reason.0) .build() } } @@ -348,20 +349,20 @@ impl TryFrom for Item { } } -impl Into for Item { - fn into(self) -> Element { +impl From for Element { + fn from(item: Item) -> Element { Element::builder("item") .ns(ns::MUC_USER) - .attr("affiliation", self.affiliation) - .attr("jid", match self.jid { + .attr("affiliation", item.affiliation) + .attr("jid", match item.jid { Some(jid) => Some(String::from(jid)), None => None, }) - .attr("nick", self.nick) - .attr("role", self.role) - .append(self.actor) - .append(self.continue_) - .append(self.reason) + .attr("nick", item.nick) + .attr("role", item.role) + .append(item.actor) + .append(item.continue_) + .append(item.reason) .build() } } @@ -400,11 +401,11 @@ impl TryFrom for MucUser { } } -impl Into for MucUser { - fn into(self) -> Element { +impl From for Element { + fn from(muc_user: MucUser) -> Element { Element::builder("x") .ns(ns::MUC_USER) - .append(self.status) + .append(muc_user.status) .build() } } diff --git a/src/ping.rs b/src/ping.rs index 272044517ae4b2873212364011b8946f284e3e26..df8b193997ad413ad71960289d6a1c26075deb69 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -33,8 +33,8 @@ impl TryFrom for Ping { } } -impl Into for Ping { - fn into(self) -> Element { +impl From for Element { + fn from(_: Ping) -> Element { Element::builder("ping") .ns(ns::PING) .build() diff --git a/src/presence.rs b/src/presence.rs index 1eb0b23fb86f56faec9065f417d0ddaa78c4dc95..e74a6e2855cd3f8b87b56a653f02733497276abb 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -117,9 +117,9 @@ impl TryFrom for PresencePayload { } } -impl Into for PresencePayload { - fn into(self) -> Element { - match self { +impl From for Element { + fn from(payload: PresencePayload) -> Element { + match payload { PresencePayload::StanzaError(stanza_error) => stanza_error.into(), PresencePayload::Muc(muc) => muc.into(), PresencePayload::Caps(caps) => caps.into(), @@ -282,16 +282,16 @@ impl TryFrom for Presence { } } -impl Into for Presence { - fn into(self) -> Element { +impl From for Element { + fn from(presence: Presence) -> Element { Element::builder("presence") .ns(ns::JABBER_CLIENT) - .attr("from", self.from.and_then(|value| Some(String::from(value)))) - .attr("to", self.to.and_then(|value| Some(String::from(value)))) - .attr("id", self.id) - .attr("type", self.type_) - .append(self.show) - .append(self.statuses.iter().map(|(lang, status)| { + .attr("from", presence.from.and_then(|value| Some(String::from(value)))) + .attr("to", presence.to.and_then(|value| Some(String::from(value)))) + .attr("id", presence.id) + .attr("type", presence.type_) + .append(presence.show) + .append(presence.statuses.iter().map(|(lang, status)| { Element::builder("status") .attr("xml:lang", match lang.as_ref() { "" => None, @@ -300,8 +300,8 @@ impl Into for Presence { .append(status) .build() }).collect::>()) - .append(if self.priority == 0 { None } else { Some(format!("{}", self.priority)) }) - .append(self.payloads) + .append(if presence.priority == 0 { None } else { Some(format!("{}", presence.priority)) }) + .append(presence.payloads) .build() } } diff --git a/src/receipts.rs b/src/receipts.rs index ae564f747066bdb0b3ec0244abb5507d5b51245b..7c3625b8daac481a120d068a52e100d17f825891 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -44,9 +44,9 @@ impl TryFrom for Receipt { } } -impl Into for Receipt { - fn into(self) -> Element { - match self { +impl From for Element { + fn from(receipt: Receipt) -> Element { + match receipt { Receipt::Request => Element::builder("request") .ns(ns::RECEIPTS), Receipt::Received(id) => Element::builder("received") diff --git a/src/roster.rs b/src/roster.rs index 4e6a38c6d1bf9d6684db15746b6a56688adf50dc..73522c282709e79fe2544b23e9d419e68bab8761 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -58,14 +58,14 @@ impl TryFrom for Item { } } -impl Into for Item { - fn into(self) -> Element { +impl From for Element { + fn from(item: Item) -> Element { Element::builder("item") .ns(ns::ROSTER) - .attr("jid", String::from(self.jid)) - .attr("name", self.name) - .attr("subscription", self.subscription) - .append(self.groups.iter().map(|group| Element::builder("group").ns(ns::ROSTER).append(group)).collect::>()) + .attr("jid", String::from(item.jid)) + .attr("name", item.name) + .attr("subscription", item.subscription) + .append(item.groups.iter().map(|group| Element::builder("group").ns(ns::ROSTER).append(group)).collect::>()) .build() } } @@ -110,12 +110,12 @@ impl TryFrom for Roster { } } -impl Into for Roster { - fn into(self) -> Element { +impl From for Element { + fn from(roster: Roster) -> Element { Element::builder("query") .ns(ns::ROSTER) - .attr("ver", self.ver) - .append(self.items) + .attr("ver", roster.ver) + .append(roster.items) .build() } } diff --git a/src/rsm.rs b/src/rsm.rs index cfb6f29243140d517ff9b07b3bbc93f3aa916d90..008757dde50077dc620385da3044356bb9750c25 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -86,34 +86,34 @@ impl TryFrom for Set { } } -impl Into for Set { - fn into(self) -> Element { +impl From for Element { + fn from(set: Set) -> Element { let mut elem = Element::builder("set") .ns(ns::RSM) .build(); - if self.after.is_some() { - elem.append_child(Element::builder("after").ns(ns::RSM).append(self.after).build()); + if set.after.is_some() { + elem.append_child(Element::builder("after").ns(ns::RSM).append(set.after).build()); } - if self.before.is_some() { - elem.append_child(Element::builder("before").ns(ns::RSM).append(self.before).build()); + if set.before.is_some() { + elem.append_child(Element::builder("before").ns(ns::RSM).append(set.before).build()); } - if self.count.is_some() { - elem.append_child(Element::builder("count").ns(ns::RSM).append(format!("{}", self.count.unwrap())).build()); + if set.count.is_some() { + elem.append_child(Element::builder("count").ns(ns::RSM).append(format!("{}", set.count.unwrap())).build()); } - if self.first.is_some() { + if set.first.is_some() { elem.append_child(Element::builder("first") .ns(ns::RSM) - .attr("index", self.first_index) - .append(self.first).build()); + .attr("index", set.first_index) + .append(set.first).build()); } - if self.index.is_some() { - elem.append_child(Element::builder("index").ns(ns::RSM).append(format!("{}", self.index.unwrap())).build()); + if set.index.is_some() { + elem.append_child(Element::builder("index").ns(ns::RSM).append(format!("{}", set.index.unwrap())).build()); } - if self.last.is_some() { - elem.append_child(Element::builder("last").ns(ns::RSM).append(self.last).build()); + if set.last.is_some() { + elem.append_child(Element::builder("last").ns(ns::RSM).append(set.last).build()); } - if self.max.is_some() { - elem.append_child(Element::builder("max").ns(ns::RSM).append(format!("{}", self.max.unwrap())).build()); + if set.max.is_some() { + elem.append_child(Element::builder("max").ns(ns::RSM).append(format!("{}", set.max.unwrap())).build()); } elem } diff --git a/src/stanza_error.rs b/src/stanza_error.rs index c25c5792ec0ac234f3d9fc34f714dffa14ead38c..4a83b8dce9ec2d7720134c378b183bc52a823e1a 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -172,15 +172,15 @@ impl TryFrom for StanzaError { } } -impl Into for StanzaError { - fn into(self) -> Element { +impl From for Element { + fn from(err: StanzaError) -> Element { let mut root = Element::builder("error") .ns(ns::JABBER_CLIENT) - .attr("type", self.type_) - .attr("by", self.by.and_then(|by| Some(String::from(by)))) - .append(self.defined_condition) + .attr("type", err.type_) + .attr("by", err.by.and_then(|by| Some(String::from(by)))) + .append(err.defined_condition) .build(); - for (lang, text) in self.texts { + for (lang, text) in err.texts { let elem = Element::builder("text") .ns(ns::XMPP_STANZAS) .attr("xml:lang", lang) @@ -188,7 +188,7 @@ impl Into for StanzaError { .build(); root.append_child(elem); } - if let Some(other) = self.other { + if let Some(other) = err.other { root.append_child(other); } root diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 634def50a063c0ecbc159e0012fe7df270c2bee1..72687d9b3dbd335dadf16d14fea9262344ba4852 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -45,9 +45,9 @@ impl TryFrom for StanzaId { } } -impl Into for StanzaId { - fn into(self) -> Element { - match self { +impl From for Element { + fn from(stanza_id: StanzaId) -> Element { + match stanza_id { StanzaId::StanzaId { id, by } => { Element::builder("stanza-id") .ns(ns::SID) From cc563b46d27bf2abbb0a01761c81f560b55c97af Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Jul 2017 20:52:25 +0100 Subject: [PATCH 283/698] ecaps2, forwarding, jingle_s5b, mam: Replace complex into expressions with .map(Element::from). --- src/ecaps2.rs | 4 ++-- src/forwarding.rs | 4 ++-- src/jingle_s5b.rs | 4 ++-- src/mam.rs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 54228c8d78a9c0cd4d61033532c25fd2720712df..be80ffd53d3e2f5c88990c8ced7c93e422802bc8 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -52,8 +52,8 @@ impl From for Element { Element::builder("c") .ns(ns::ECAPS2) .append(ecaps2.hashes.drain(..) - .map(|hash| hash.into()) - .collect::>()) + .map(Element::from) + .collect::>()) .build() } } diff --git a/src/forwarding.rs b/src/forwarding.rs index 3afaabdf6a1af5d36e8f22e265a3c35a2fe0a830..86474dd1b6cd0456887ca2648cdb6a4fc7c68e6c 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -52,8 +52,8 @@ impl From for Element { fn from(forwarded: Forwarded) -> Element { Element::builder("forwarded") .ns(ns::FORWARD) - .append(match forwarded.delay { Some(delay) => { let elem: Element = delay.into(); Some(elem) }, None => None }) - .append(match forwarded.stanza { Some(stanza) => { let elem: Element = stanza.into(); Some(elem) }, None => None }) + .append(forwarded.delay.map(Element::from)) + .append(forwarded.stanza.map(Element::from)) .build() } } diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 268529047efa6b0eb497cdf3f8752c7c247db109..7ef1e5020420cab1b3306a6ae795417ba617e770 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -147,8 +147,8 @@ impl From for Element { .append(match transport.payload { TransportPayload::Candidates(mut candidates) => { candidates.drain(..) - .map(|candidate| candidate.into()) - .collect::>() + .map(Element::from) + .collect::>() }, TransportPayload::Activated(cid) => { vec!(Element::builder("activated") diff --git a/src/mam.rs b/src/mam.rs index bbc08a6d19b1736779d54cf5d522f3101c55eb8e..b52e80413b69a5028fc03674b95f5fe4b8f96570 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -167,8 +167,8 @@ impl From for Element { .ns(ns::MAM) .attr("queryid", query.queryid) .attr("node", query.node) - //.append(query.form.map(|form| -> Element { form.into() })) - .append(query.set.map(|set| -> Element { set.into() })) + //.append(query.form.map(Element::from)) + .append(query.set.map(Element::from)) .build() } } From 5543d715994f9bccd1c6600ecf98232cd240afa6 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Jul 2017 20:53:59 +0100 Subject: [PATCH 284/698] attention: Use super::* in tests like every other module. --- src/attention.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/attention.rs b/src/attention.rs index b72079b9599de39e50cfa2f9664ace6c6cfe531c..e394785f76737b0b85b646be69760477be5f64a5 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -42,10 +42,7 @@ impl From for Element { #[cfg(test)] mod tests { - use try_from::TryFrom; - use minidom::Element; - use error::Error; - use super::Attention; + use super::*; #[test] fn test_simple() { From 2571aa7666055c548f7c99f96a86008c12e9650f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Wed, 19 Jul 2017 00:21:44 +0100 Subject: [PATCH 285/698] presence: Add with_ helpers to build a Presence --- src/presence.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/presence.rs b/src/presence.rs index e74a6e2855cd3f8b87b56a653f02733497276abb..30a2615dd94e7d834b837365be35f6b18c2843fd 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -211,6 +211,36 @@ impl Presence { payloads: vec!(), } } + + pub fn with_from(mut self, from: Option) -> Presence { + self.from = from; + self + } + + pub fn with_to(mut self, to: Option) -> Presence { + self.to = to; + self + } + + pub fn with_id(mut self, id: Option) -> Presence { + self.id = id; + self + } + + pub fn with_show(mut self, show: Show) -> Presence { + self.show = show; + self + } + + pub fn with_priority(mut self, priority: i8) -> Presence { + self.priority = priority; + self + } + + pub fn with_payloads(mut self, payloads: Vec) -> Presence { + self.payloads = payloads; + self + } } impl TryFrom for Presence { From f6f6faeb77bceff83a4cf2d6ea467b90b3597b67 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Jul 2017 23:08:23 +0100 Subject: [PATCH 286/698] iq, jingle_ft: Simplify item counting with Iterator::count(). --- src/iq.rs | 2 +- src/jingle_ft.rs | 2 +- src/presence.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index d1078b348706686a8e1f917cc0cff0d14654ae05..aed6a83e39d12a1dbc15f923d055be5b861b6d17 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -222,7 +222,7 @@ impl TryFrom for Iq { return Err(Error::ParseError("Wrong number of children in iq element.")); } error_payload = Some(StanzaError::try_from(elem.clone())?); - } else if root.children().collect::>().len() != 2 { + } else if root.children().count() != 2 { return Err(Error::ParseError("Wrong number of children in iq element.")); } } else { diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 7bd0df8d5f2462c8cd740ddb7fc8b765141b661b..85ac2dfac7e8504d4d7aee11811836a9764fd228 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -91,7 +91,7 @@ impl TryFrom for Description { if !elem.is("description", ns::JINGLE_FT) { return Err(Error::ParseError("This is not a JingleFT description element.")); } - if elem.children().collect::>().len() != 1 { + if elem.children().count() != 1 { return Err(Error::ParseError("JingleFT description element must have exactly one child.")); } diff --git a/src/presence.rs b/src/presence.rs index 30a2615dd94e7d834b837365be35f6b18c2843fd..a0147c7d02d1770e491c9864247caaf87fd7dca1 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -485,6 +485,6 @@ mod tests { presence.statuses.insert(String::from(""), status); let elem: Element = presence.into(); assert!(elem.is("presence", ns::JABBER_CLIENT)); - assert!(elem.children().collect::>()[0].is("status", ns::JABBER_CLIENT)); + assert!(elem.children().next().unwrap().is("status", ns::JABBER_CLIENT)); } } From 5df585ca40239282fb0c05d7ef0633e5784a4876 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Jul 2017 23:09:22 +0100 Subject: [PATCH 287/698] data_forms, ibr, message, presence, roster: Always use into_iter. --- src/data_forms.rs | 2 +- src/ibr.rs | 4 ++-- src/message.rs | 4 ++-- src/presence.rs | 2 +- src/roster.rs | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index d4678c097573e6795345449330c3f67b248a87d5..bccdca0928e1964efa2f932ec1983709dd9fda82 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -72,7 +72,7 @@ impl From for Element { .attr("label", field.label) .append(if field.required { Some(Element::builder("required").ns(ns::DATA_FORMS).build()) } else { None }) .append(field.options) - .append(field.values.iter().map(|value| { + .append(field.values.into_iter().map(|value| { Element::builder("value").ns(ns::DATA_FORMS).append(value).build() }).collect::>()) .append(field.media) diff --git a/src/ibr.rs b/src/ibr.rs index 37c54f7b3428424a1ce8ce9b3b8704e4ea326ebe..59dc15d81e1c689fe2494746214b1b67fa55394c 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -69,8 +69,8 @@ impl From for Element { Element::builder("query") .ns(ns::REGISTER) .append(if query.registered { Some(Element::builder("registered").ns(ns::REGISTER)) } else { None }) - .append(query.fields.iter().map(|(name, value)| { - Element::builder(name.clone()).ns(ns::REGISTER).append(value.clone()) + .append(query.fields.into_iter().map(|(name, value)| { + Element::builder(name).ns(ns::REGISTER).append(value) }).collect::>()) .append(if query.remove { Some(Element::builder("remove").ns(ns::REGISTER)) } else { None }) .append(query.form) diff --git a/src/message.rs b/src/message.rs index 03fc2e8c79dc970ecc3e6edae9a499813abd1b1f..bbb6a5e075cfa35698619440563a52dea65072a7 100644 --- a/src/message.rs +++ b/src/message.rs @@ -207,7 +207,7 @@ impl From for Element { .attr("to", message.to.and_then(|value| Some(String::from(value)))) .attr("id", message.id) .attr("type", message.type_) - .append(message.subjects.iter() + .append(message.subjects.into_iter() .map(|(lang, subject)| { Element::builder("subject") .ns(ns::JABBER_CLIENT) @@ -218,7 +218,7 @@ impl From for Element { .append(subject) .build() }) .collect::>()) - .append(message.bodies.iter() + .append(message.bodies.into_iter() .map(|(lang, body)| { Element::builder("body") .ns(ns::JABBER_CLIENT) diff --git a/src/presence.rs b/src/presence.rs index a0147c7d02d1770e491c9864247caaf87fd7dca1..098c401ad94a478479e480586aa0194ca36c1c05 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -321,7 +321,7 @@ impl From for Element { .attr("id", presence.id) .attr("type", presence.type_) .append(presence.show) - .append(presence.statuses.iter().map(|(lang, status)| { + .append(presence.statuses.into_iter().map(|(lang, status)| { Element::builder("status") .attr("xml:lang", match lang.as_ref() { "" => None, diff --git a/src/roster.rs b/src/roster.rs index 73522c282709e79fe2544b23e9d419e68bab8761..a1ed2dc541e78ea639e0a24b299ec6459f969ee0 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -65,7 +65,7 @@ impl From for Element { .attr("jid", String::from(item.jid)) .attr("name", item.name) .attr("subscription", item.subscription) - .append(item.groups.iter().map(|group| Element::builder("group").ns(ns::ROSTER).append(group)).collect::>()) + .append(item.groups.into_iter().map(|group| Element::builder("group").ns(ns::ROSTER).append(group)).collect::>()) .build() } } From 87af0f36155aed0ea6bf003ea6c03061a17f87bd Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Jul 2017 23:10:13 +0100 Subject: [PATCH 288/698] message, presence, iq: Improve documentation. --- src/iq.rs | 1 + src/lib.rs | 12 ++++++++++-- src/message.rs | 1 + src/presence.rs | 1 + 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index aed6a83e39d12a1dbc15f923d055be5b861b6d17..aab1efcbd09145df9823383e989134d23f86b6ad 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -190,6 +190,7 @@ impl<'a> IntoAttributeValue for &'a IqType { } } +/// The main structure representing the `` stanza. #[derive(Debug, Clone)] pub struct Iq { pub from: Option, diff --git a/src/lib.rs b/src/lib.rs index 35dacabd7cf1c705514a6ce56cf46383b2d6ba58..359ba7b0c940dc13d64d762f4f9b1ffbe84f3219 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,7 +58,11 @@ macro_rules! generate_attribute { ($elem:ident, $name:tt, {$($a:ident => $b:tt),+}) => ( #[derive(Debug, Clone, PartialEq)] pub enum $elem { - $($a),+ + $( + #[doc=$b] + #[doc="value for this attribute."] + $a + ),+ } impl FromStr for $elem { type Err = Error; @@ -80,7 +84,11 @@ macro_rules! generate_attribute { ($elem:ident, $name:tt, {$($a:ident => $b:tt),+}, Default = $default:ident) => ( #[derive(Debug, Clone, PartialEq)] pub enum $elem { - $($a),+ + $( + #[doc=$b] + #[doc="value for this attribute."] + $a + ),+ } impl FromStr for $elem { type Err = Error; diff --git a/src/message.rs b/src/message.rs index bbb6a5e075cfa35698619440563a52dea65072a7..ea56042501228f54ceec2913c68d909c464415ff 100644 --- a/src/message.rs +++ b/src/message.rs @@ -115,6 +115,7 @@ type Body = String; type Subject = String; type Thread = String; +/// The main structure representing the `` stanza. #[derive(Debug, Clone)] pub struct Message { pub from: Option, diff --git a/src/presence.rs b/src/presence.rs index 098c401ad94a478479e480586aa0194ca36c1c05..fe970e3b28a7fda338465a4324855b8496706cf6 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -186,6 +186,7 @@ impl IntoAttributeValue for Type { } } +/// The main structure representing the `` stanza. #[derive(Debug, Clone)] pub struct Presence { pub from: Option, From fd31e691af6e03b2235a9449e61f2c48c437daec Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Jul 2017 23:46:44 +0100 Subject: [PATCH 289/698] lib: Improve the wording of the docstring. --- src/lib.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 359ba7b0c940dc13d64d762f4f9b1ffbe84f3219..b3f57fdf675ebffca54f744d5efd7b68eca9b97f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,19 @@ //! A crate parsing common XMPP elements into Rust structures. //! -//! Each module implements the `TryFrom<&minidom::Element>` trait, which takes -//! a minidom `Element` reference and returns a `Result`. +//! Each module implements the [`TryFrom`] trait, which takes a minidom +//! [`Element`] reference and returns a `Result` whose value is `Ok` if the +//! element parsed correctly, `Err(error::Error)` otherwise. //! -//! Parsed structs can then be manipulated manually, and must be serialised -//! back before being sent over the wire. +//! The returned structure can be manipuled as any Rust structure, with each +//! field being public. You can also create the same structure manually, with +//! some having `new()` and `with_*()` helper methods to create them. +//! +//! Once you are happy with your structure, you can serialise it back to an +//! [`Element`], using either `From` or `Into`, which give you what +//! you want to be sending on the wire. +//! +//! [`TryFrom`]: ../try_from/trait.TryFrom.html +//! [`Element`]: ../minidom/element/struct.Element.html // Copyright (c) 2017 Emmanuel Gil Peyrot // Copyright (c) 2017 Maxime “pep” Buquet From 7612c53f9af82c7cba7548c21e9ce519c838a13f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Jul 2017 23:47:21 +0100 Subject: [PATCH 290/698] attention, chatstates, eme, ping: Add a docstring on the structs. --- src/attention.rs | 1 + src/chatstates.rs | 11 +++++++++++ src/eme.rs | 5 +++++ src/ping.rs | 1 + 4 files changed, 18 insertions(+) diff --git a/src/attention.rs b/src/attention.rs index e394785f76737b0b85b646be69760477be5f64a5..7d0b1f24ff3835472c464c9915973a1629fd2780 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -12,6 +12,7 @@ use error::Error; use ns; +/// Structure representing an `` element. #[derive(Debug, Clone)] pub struct Attention; diff --git a/src/chatstates.rs b/src/chatstates.rs index a5af910c6dca44762f9305f30f15ed5723e653b1..4093edbd29c805a39dbe65731dbf365ff3341413 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -12,12 +12,23 @@ use error::Error; use ns; +/// Enum representing chatstate elements part of the +/// `http://jabber.org/protocol/chatstates` namespace. #[derive(Debug, Clone)] pub enum ChatState { + /// `` Active, + + /// `` Composing, + + /// `` Gone, + + /// `` Inactive, + + /// `` Paused, } diff --git a/src/eme.rs b/src/eme.rs index ba07f95cef5613123e180f88bd1148cc93970510..288431b9c975b95ebc180c7bfeb74c43da63d72a 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -12,9 +12,14 @@ use error::Error; use ns; +/// Structure representing an `` element. #[derive(Debug, Clone)] pub struct ExplicitMessageEncryption { + /// Namespace of the encryption scheme used. pub namespace: String, + + /// User-friendly name for the encryption scheme, should be `None` for OTR, + /// legacy OpenPGP and OX. pub name: Option, } diff --git a/src/ping.rs b/src/ping.rs index df8b193997ad413ad71960289d6a1c26075deb69..c97fa5026ac5595ac3db83332afa492746a3ea3b 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -13,6 +13,7 @@ use error::Error; use ns; +/// Structure representing a `` element. #[derive(Debug, Clone)] pub struct Ping; From 21cee25b27e9c55025d317d66c2ac5862c11baa8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Jul 2017 01:19:34 +0100 Subject: [PATCH 291/698] Replace .and_then() with .map() wherever it makes sense. --- src/delay.rs | 2 +- src/iq.rs | 4 ++-- src/message.rs | 4 ++-- src/presence.rs | 4 ++-- src/pubsub/event.rs | 16 ++++++++-------- src/stanza_error.rs | 2 +- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index b96db2bdc2440081688b5c45cf460ab622812f98..e5123ec2d208493227b3c00888ab39bf914da7e7 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -49,7 +49,7 @@ impl From for Element { fn from(delay: Delay) -> Element { Element::builder("delay") .ns(ns::DELAY) - .attr("from", delay.from.and_then(|value| Some(String::from(value)))) + .attr("from", delay.from.map(String::from)) .attr("stamp", delay.stamp.to_rfc3339()) .append(delay.data) .build() diff --git a/src/iq.rs b/src/iq.rs index aab1efcbd09145df9823383e989134d23f86b6ad..1eae2549cb66a24330e719622615b78c728f2465 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -272,8 +272,8 @@ impl From for Element { fn from(iq: Iq) -> Element { let mut stanza = Element::builder("iq") .ns(ns::JABBER_CLIENT) - .attr("from", iq.from.and_then(|value| Some(String::from(value)))) - .attr("to", iq.to.and_then(|value| Some(String::from(value)))) + .attr("from", iq.from.map(String::from)) + .attr("to", iq.to.map(String::from)) .attr("id", iq.id) .attr("type", &iq.payload) .build(); diff --git a/src/message.rs b/src/message.rs index ea56042501228f54ceec2913c68d909c464415ff..29234321f4baabaef635a52593ba4b9e60b6ee11 100644 --- a/src/message.rs +++ b/src/message.rs @@ -204,8 +204,8 @@ impl From for Element { fn from(message: Message) -> Element { Element::builder("message") .ns(ns::JABBER_CLIENT) - .attr("from", message.from.and_then(|value| Some(String::from(value)))) - .attr("to", message.to.and_then(|value| Some(String::from(value)))) + .attr("from", message.from.map(String::from)) + .attr("to", message.to.map(String::from)) .attr("id", message.id) .attr("type", message.type_) .append(message.subjects.into_iter() diff --git a/src/presence.rs b/src/presence.rs index fe970e3b28a7fda338465a4324855b8496706cf6..93c5cf941b246dcf775f683743fc0a8636a59f1e 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -317,8 +317,8 @@ impl From for Element { fn from(presence: Presence) -> Element { Element::builder("presence") .ns(ns::JABBER_CLIENT) - .attr("from", presence.from.and_then(|value| Some(String::from(value)))) - .attr("to", presence.to.and_then(|value| Some(String::from(value)))) + .attr("from", presence.from.map(String::from)) + .attr("to", presence.to.map(String::from)) .attr("id", presence.id) .attr("type", presence.type_) .append(presence.show) diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index ef5e994b8abb4471c07d78374e17e0a0eae660d7..04519e96e462cc60ff8a778f707b7132cd1eb0d7 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -31,7 +31,7 @@ impl From for Element { .ns(ns::PUBSUB_EVENT) .attr("id", item.id) .attr("node", item.node) - .attr("publisher", item.publisher.and_then(|publisher| Some(String::from(publisher)))) + .attr("publisher", item.publisher.map(String::from)) .append(item.payload) .build() } @@ -222,11 +222,11 @@ impl From for Element { Element::builder("purge") .ns(ns::PUBSUB_EVENT) .attr("node", node) - .append(redirect.and_then(|redirect| { - Some(Element::builder("redirect") - .ns(ns::PUBSUB_EVENT) - .attr("uri", redirect) - .build()) + .append(redirect.map(|redirect| { + Element::builder("redirect") + .ns(ns::PUBSUB_EVENT) + .attr("uri", redirect) + .build() })) .build() }, @@ -260,8 +260,8 @@ impl From for Element { Element::builder("subscription") .ns(ns::PUBSUB_EVENT) .attr("node", node) - .attr("expiry", expiry.and_then(|expiry| Some(expiry.to_rfc3339()))) - .attr("jid", jid.and_then(|jid| Some(String::from(jid)))) + .attr("expiry", expiry.map(|expiry| expiry.to_rfc3339())) + .attr("jid", jid.map(String::from)) .attr("subid", subid) .attr("subscription", subscription) .build() diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 4a83b8dce9ec2d7720134c378b183bc52a823e1a..3b7273b3fa8db3d7498de02f779bb8c1b28be6da 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -177,7 +177,7 @@ impl From for Element { let mut root = Element::builder("error") .ns(ns::JABBER_CLIENT) .attr("type", err.type_) - .attr("by", err.by.and_then(|by| Some(String::from(by)))) + .attr("by", err.by.map(String::from)) .append(err.defined_condition) .build(); for (lang, text) in err.texts { From 3b6733f38b4e748e18021202a772e6f76f5b8751 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Jul 2017 01:20:29 +0100 Subject: [PATCH 292/698] =?UTF-8?q?Upgrade=20to=20minidom=C2=A00.4.4=20to?= =?UTF-8?q?=20avoid=20having=20to=20redefine=20IntoElements=20for=20each?= =?UTF-8?q?=20Into.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 2 +- src/data_forms.rs | 20 +------------------- src/disco.rs | 14 +------------- src/ibr.rs | 8 +------- src/jingle.rs | 14 +------------- src/media_element.rs | 14 +------------- src/muc/user.rs | 26 +------------------------- src/pubsub/event.rs | 8 +------- src/roster.rs | 8 +------- 9 files changed, 9 insertions(+), 105 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b70538110dc033d592a6b06bab35c010d605c298..b4a760b270f509f5a6344b3f0246c9ba04e1898c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ categories = ["parsing", "network-programming"] license = "MPL-2.0" [dependencies] -minidom = "0.4.3" +minidom = "0.4.4" jid = "0.2.0" base64 = "0.6.0" digest = "0.6.0" diff --git a/src/data_forms.rs b/src/data_forms.rs index bccdca0928e1964efa2f932ec1983709dd9fda82..eb751e0ba27632389faa0f286dfff851eea24c44 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -7,7 +7,7 @@ use try_from::TryFrom; use std::str::FromStr; -use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; +use minidom::{Element, IntoAttributeValue}; use error::Error; use ns; @@ -46,12 +46,6 @@ impl From for Element { } } -impl IntoElements for Option_ { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - #[derive(Debug, Clone)] pub struct Field { pub var: String, @@ -80,12 +74,6 @@ impl From for Element { } } -impl IntoElements for Field { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - generate_attribute!(DataFormType, "type", { Cancel => "cancel", Form => "form", @@ -236,12 +224,6 @@ impl From for Element { } } -impl IntoElements for DataForm { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/disco.rs b/src/disco.rs index aee3de6ae612cacb8e40e35ef6bb66c0f8baab23..167440edabd21a8afebbed84ec33af01cc6efce1 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -6,7 +6,7 @@ use try_from::TryFrom; -use minidom::{Element, IntoElements, ElementEmitter}; +use minidom::Element; use error::Error; use ns; @@ -62,12 +62,6 @@ impl From for Element { } } -impl IntoElements for Feature { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - #[derive(Debug, Clone)] pub struct Identity { pub category: String, // TODO: use an enum here. @@ -88,12 +82,6 @@ impl From for Element { } } -impl IntoElements for Identity { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - #[derive(Debug, Clone)] pub struct DiscoInfoResult { pub node: Option, diff --git a/src/ibr.rs b/src/ibr.rs index 59dc15d81e1c689fe2494746214b1b67fa55394c..c51153e65f2f72bfd503d8ff3bbed07cc501fc3d 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -7,7 +7,7 @@ use std::collections::HashMap; use try_from::TryFrom; -use minidom::{Element, IntoElements, ElementEmitter}; +use minidom::Element; use error::Error; @@ -78,12 +78,6 @@ impl From for Element { } } -impl IntoElements for Query { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/jingle.rs b/src/jingle.rs index 1d0c07f15434050eb1dfdc39c4a1ca3afaf57aac..62fb4b5afdc4b6c387bac2f434b998dcb5e96c10 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -7,7 +7,7 @@ use try_from::TryFrom; use std::str::FromStr; -use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; +use minidom::{Element, IntoAttributeValue}; use jid::Jid; use error::Error; @@ -110,12 +110,6 @@ impl From for Element { } } -impl IntoElements for Content { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - #[derive(Debug, Clone, PartialEq)] pub enum Reason { AlternativeSession, //(String), @@ -240,12 +234,6 @@ impl From for Element { } } -impl IntoElements for ReasonElement { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - generate_id!(SessionId); #[derive(Debug, Clone)] diff --git a/src/media_element.rs b/src/media_element.rs index aa15efb5637e8d440f40a7de5f1d58d6fedd5fd1..b2e5857d449630ab36956cd4b2bcc46f736b558f 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -6,7 +6,7 @@ use try_from::TryFrom; -use minidom::{Element, IntoElements, ElementEmitter}; +use minidom::Element; use error::Error; @@ -28,12 +28,6 @@ impl From for Element { } } -impl IntoElements for URI { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - #[derive(Debug, Clone)] pub struct MediaElement { pub width: Option, @@ -81,12 +75,6 @@ impl From for Element { } } -impl IntoElements for MediaElement { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/muc/user.rs b/src/muc/user.rs index 48da79666889ebb5357f15ba3d50c0fa0ef5e03e..afc9e72187705a9117b436ad45c252587c47e505 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -8,7 +8,7 @@ use try_from::{TryFrom, TryInto}; use std::str::FromStr; -use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; +use minidom::{Element, IntoAttributeValue}; use jid::Jid; @@ -142,12 +142,6 @@ impl From for Element { } } -impl IntoElements for Status { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - /// Optional element used in elements inside presence stanzas of type /// "unavailable" that are sent to users who are kick or banned, as well as within IQs for tracking /// purposes. -- CHANGELOG 0.17 (2002-10-23) @@ -198,12 +192,6 @@ impl From for Element { } } -impl IntoElements for Actor { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - #[derive(Debug, Clone, PartialEq)] pub struct Continue { thread: Option, @@ -237,12 +225,6 @@ impl From for Element { } } -impl IntoElements for Continue { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - #[derive(Debug, Clone, PartialEq)] pub struct Reason(String); @@ -272,12 +254,6 @@ impl From for Element { } } -impl IntoElements for Reason { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - generate_attribute!(Affiliation, "affiliation", { Owner => "owner", Admin => "admin", diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 04519e96e462cc60ff8a778f707b7132cd1eb0d7..a6fa4550a6775751a465a13a855e82235770508f 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -7,7 +7,7 @@ use try_from::TryFrom; use std::str::FromStr; -use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; +use minidom::{Element, IntoAttributeValue}; use jid::Jid; use chrono::{DateTime, FixedOffset}; @@ -37,12 +37,6 @@ impl From for Element { } } -impl IntoElements for Item { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - generate_attribute!(Subscription, "subscription", { None => "none", Pending => "pending", diff --git a/src/roster.rs b/src/roster.rs index a1ed2dc541e78ea639e0a24b299ec6459f969ee0..1a4cc50e8df99c8b3ba89478364ef6339eaa4af7 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -7,7 +7,7 @@ use try_from::TryFrom; use std::str::FromStr; -use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; +use minidom::{Element, IntoAttributeValue}; use jid::Jid; use error::Error; @@ -70,12 +70,6 @@ impl From for Element { } } -impl IntoElements for Item { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - #[derive(Debug, Clone)] pub struct Roster { pub ver: Option, From 4454da15b600025c3b4f338313a8883d3d281df5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Jul 2017 17:33:58 +0100 Subject: [PATCH 293/698] disco: Implement disco#items. --- src/disco.rs | 160 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/ns.rs | 2 + 2 files changed, 162 insertions(+) diff --git a/src/disco.rs b/src/disco.rs index 167440edabd21a8afebbed84ec33af01cc6efce1..aa816decd0bbbe07bd61f0b4fdb339c96c2e640c 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -7,6 +7,7 @@ use try_from::TryFrom; use minidom::Element; +use jid::Jid; use error::Error; use ns; @@ -176,9 +177,131 @@ impl From for Element { } } +#[derive(Debug, Clone)] +pub struct DiscoItemsQuery { + pub node: Option, +} + +impl TryFrom for DiscoItemsQuery { + type Err = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("query", ns::DISCO_ITEMS) { + return Err(Error::ParseError("This is not a disco#items element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in disco#items.")); + } + for (attr, _) in elem.attrs() { + if attr != "node" { + return Err(Error::ParseError("Unknown attribute in disco#items.")); + } + } + Ok(DiscoItemsQuery { + node: get_attr!(elem, "node", optional), + }) + } +} + +impl From for Element { + fn from(disco: DiscoItemsQuery) -> Element { + Element::builder("query") + .ns(ns::DISCO_ITEMS) + .attr("node", disco.node) + .build() + } +} + +#[derive(Debug, Clone)] +pub struct Item { + pub jid: Jid, + pub node: Option, + pub name: Option, +} + +impl TryFrom for Item { + type Err = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("item", ns::DISCO_ITEMS) { + return Err(Error::ParseError("This is not an item element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in item element.")); + } + for (attr, _) in elem.attrs() { + if attr != "jid" && attr != "node" && attr != "name" { + return Err(Error::ParseError("Unknown attribute in item element.")); + } + } + Ok(Item { + jid: get_attr!(elem, "jid", required), + node: get_attr!(elem, "node", optional), + name: get_attr!(elem, "name", optional), + }) + } +} + +impl From for Element { + fn from(item: Item) -> Element { + Element::builder("item") + .ns(ns::DISCO_ITEMS) + .attr("jid", String::from(item.jid)) + .attr("node", item.node) + .attr("name", item.name) + .build() + } +} + +#[derive(Debug, Clone)] +pub struct DiscoItemsResult { + pub node: Option, + pub items: Vec, +} + +impl TryFrom for DiscoItemsResult { + type Err = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("query", ns::DISCO_ITEMS) { + return Err(Error::ParseError("This is not a disco#items element.")); + } + for (attr, _) in elem.attrs() { + if attr != "node" { + return Err(Error::ParseError("Unknown attribute in disco#items.")); + } + } + + let mut items: Vec = vec!(); + for child in elem.children() { + if child.is("item", ns::DISCO_ITEMS) { + items.push(Item::try_from(child.clone())?); + } else { + return Err(Error::ParseError("Unknown element in disco#items.")); + } + } + + Ok(DiscoItemsResult { + node: get_attr!(elem, "node", optional), + items: items, + }) + } +} + +impl From for Element { + fn from(disco: DiscoItemsResult) -> Element { + Element::builder("query") + .ns(ns::DISCO_ITEMS) + .attr("node", disco.node) + .append(disco.items) + .build() + } +} + #[cfg(test)] mod tests { use super::*; + use std::str::FromStr; #[test] fn test_simple() { @@ -273,4 +396,41 @@ mod tests { }; assert_eq!(message, "disco#info feature not present in disco#info."); } + + #[test] + fn test_simple_items() { + let elem: Element = "".parse().unwrap(); + let query = DiscoItemsQuery::try_from(elem).unwrap(); + assert!(query.node.is_none()); + + let elem: Element = "".parse().unwrap(); + let query = DiscoItemsQuery::try_from(elem).unwrap(); + assert_eq!(query.node, Some(String::from("coucou"))); + } + + #[test] + fn test_simple_items_result() { + let elem: Element = "".parse().unwrap(); + let query = DiscoItemsResult::try_from(elem).unwrap(); + assert!(query.node.is_none()); + assert!(query.items.is_empty()); + + let elem: Element = "".parse().unwrap(); + let query = DiscoItemsResult::try_from(elem).unwrap(); + assert_eq!(query.node, Some(String::from("coucou"))); + assert!(query.items.is_empty()); + } + + #[test] + fn test_answers_items_result() { + let elem: Element = "".parse().unwrap(); + let query = DiscoItemsResult::try_from(elem).unwrap(); + assert_eq!(query.items.len(), 2); + assert_eq!(query.items[0].jid, Jid::from_str("component").unwrap()); + assert_eq!(query.items[0].node, None); + assert_eq!(query.items[0].name, None); + assert_eq!(query.items[1].jid, Jid::from_str("component2").unwrap()); + assert_eq!(query.items[1].node, Some(String::from("test"))); + assert_eq!(query.items[1].name, Some(String::from("A component"))); + } } diff --git a/src/ns.rs b/src/ns.rs index 7edee1e4aec9af43771c630cdd6380871c075971..0d06a1d7ae483136a705ecaa1fa96cf72f9cc8bd 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -18,6 +18,8 @@ pub const DATA_FORMS: &str = "jabber:x:data"; /// XEP-0030: Service Discovery pub const DISCO_INFO: &str = "http://jabber.org/protocol/disco#info"; +/// XEP-0030: Service Discovery +pub const DISCO_ITEMS: &str = "http://jabber.org/protocol/disco#items"; /// XEP-0045: Multi-User Chat pub const MUC: &str = "http://jabber.org/protocol/muc"; From 85ea78d5a73a8aa20477859da2bcd0b81c7949ef Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Jul 2017 18:46:40 +0100 Subject: [PATCH 294/698] ChangeLog: Add 0.7.0 release notes. --- ChangeLog | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/ChangeLog b/ChangeLog index 5edf6648790440e927d0026eb6ffbf044a801d6e..e10497cef567b7f3679308811b7925c495a610e8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +Version 0.7.0: +2017-07-23 Emmanuel Gil Peyrot + * New parsers/serialisers: + - Jingle Message Initialisation (XEP-0353) was added. + - The disco#items query (XEP-0030) is now supported, in + addition to the existing disco#info one. + * Breaking changes: + - Replaced many type aliases with proper wrapping structs. + - Split Disco into a query and a result part, since they have + very different constraints. + - Split IqPayload in three to avoid parsing queries as results + for example. + * Improvements: + - Use TryFrom from the try_from crate, thus removing the + dependency on nightly! + - Always implement From instead of Into, the latter is + generated anyway. + - Add helpers to construct your Presence stanza. + Version 0.6.0: 2017-06-27 Emmanuel Gil Peyrot * New parsers/serialisers: From e26bd3306e98d65224dec0e2deb2083239177e1f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Jul 2017 18:47:46 +0100 Subject: [PATCH 295/698] Release version 0.7.0. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b4a760b270f509f5a6344b3f0246c9ba04e1898c..6f27f34abe1190d0de070392d5bab336eed8dee1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.6.0" +version = "0.7.0" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", From 83c9713ab949929e996283da730a7443135bf639 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 24 Jul 2017 23:11:10 +0100 Subject: [PATCH 297/698] caps, ecaps2: Make Blake2b panic and update the blake2 crate to 0.6.1. See https://github.com/RustCrypto/hashes/issues/34 for more information. --- Cargo.toml | 2 +- src/caps.rs | 9 +++++++-- src/ecaps2.rs | 10 ++++++++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6f27f34abe1190d0de070392d5bab336eed8dee1..7b425d4453207e9d05292c7e7dffa2a73e5ac348 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,6 @@ digest = "0.6.0" sha-1 = "0.4.0" sha2 = "0.6.0" sha3 = "0.6.0" -blake2 = "0.6.0" +blake2 = "0.6.1" chrono = "0.4.0" try_from = "0.2.2" diff --git a/src/caps.rs b/src/caps.rs index aad50eb2d644807a73f50922ce8fe7058f0f7ec5..a2bdd11bed7d92720a3e383980c4c7f2a7ea3ddd 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -15,11 +15,12 @@ use error::Error; use ns; use base64; +use digest::Digest; use sha_1::Sha1; use sha2::{Sha256, Sha512}; use sha3::{Sha3_256, Sha3_512}; -use blake2::Blake2b; -use digest::{Digest, VariableOutput}; +//use blake2::Blake2b; +//use digest::{Digest, VariableOutput}; #[derive(Debug, Clone)] pub struct Caps { @@ -173,6 +174,9 @@ pub fn hash_caps(data: &[u8], algo: Algo) -> Result { let hash = hasher.result(); get_hash_vec(hash.as_slice()) }, + Algo::Blake2b_256 + | Algo::Blake2b_512 => panic!("See https://github.com/RustCrypto/hashes/issues/34"), + /* Algo::Blake2b_256 => { let mut hasher = Blake2b::default(); hasher.input(data); @@ -187,6 +191,7 @@ pub fn hash_caps(data: &[u8], algo: Algo) -> Result { let hash = hasher.variable_result(&mut buf).unwrap(); get_hash_vec(hash) }, + */ Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)), }, algo: algo, diff --git a/src/ecaps2.rs b/src/ecaps2.rs index be80ffd53d3e2f5c88990c8ced7c93e422802bc8..593acec073db802114d157456dcab35da7d19483 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -15,10 +15,11 @@ use error::Error; use ns; use base64; +use digest::Digest; use sha2::{Sha256, Sha512}; use sha3::{Sha3_256, Sha3_512}; -use blake2::Blake2b; -use digest::{Digest, VariableOutput}; +//use blake2::Blake2b; +//use digest::{Digest, VariableOutput}; #[derive(Debug, Clone)] pub struct ECaps2 { @@ -151,6 +152,9 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { let hash = hasher.result(); get_hash_vec(hash.as_slice()) }, + Algo::Blake2b_256 + | Algo::Blake2b_512 => panic!("See https://github.com/RustCrypto/hashes/issues/34"), + /* Algo::Blake2b_256 => { let mut hasher = Blake2b::default(); hasher.input(data); @@ -165,6 +169,7 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { let hash = hasher.variable_result(&mut buf).unwrap(); get_hash_vec(hash) }, + */ Algo::Sha_1 => return Err(String::from("Disabled algorithm sha-1: unsafe.")), Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)), }, @@ -455,6 +460,7 @@ mod tests { } #[test] + #[ignore] fn test_blake2b_512() { let hash = ecaps2::hash_ecaps2("abc".as_bytes(), Algo::Blake2b_512).unwrap(); let known_hash: Vec = vec!( From 712e6dfe1156abacd6da71fdacfd097a0729ef52 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 24 Jul 2017 23:11:31 +0100 Subject: [PATCH 298/698] Release version 0.7.1. --- Cargo.toml | 2 +- ChangeLog | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 7b425d4453207e9d05292c7e7dffa2a73e5ac348..c5ff5e0977e3a3026cafca7507ae8d4ac2486c91 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.7.0" +version = "0.7.1" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", diff --git a/ChangeLog b/ChangeLog index e10497cef567b7f3679308811b7925c495a610e8..a780194d330fdc8b629141fa96167beafa2a9bd2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +Version 0.7.0: +2017-07-24 Emmanuel Gil Peyrot + * Hotfixes: + - Stub out blake2 support, since the blake2 crate broke its API + between their 0.6.0 and 0.6.1 releases… + Version 0.7.0: 2017-07-23 Emmanuel Gil Peyrot * New parsers/serialisers: From 4b3ced042e54c8d3e4491b664e7af839ae413c3a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 26 Jul 2017 00:04:20 +0100 Subject: [PATCH 300/698] ecaps2, jingle_s5b: Replace drain() with into_iter(). --- src/ecaps2.rs | 4 ++-- src/jingle_s5b.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 593acec073db802114d157456dcab35da7d19483..8166e5b0f3e525f4019c7628fd4d9f24ce0c3200 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -49,10 +49,10 @@ impl TryFrom for ECaps2 { } impl From for Element { - fn from(mut ecaps2: ECaps2) -> Element { + fn from(ecaps2: ECaps2) -> Element { Element::builder("c") .ns(ns::ECAPS2) - .append(ecaps2.hashes.drain(..) + .append(ecaps2.hashes.into_iter() .map(Element::from) .collect::>()) .build() diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 7ef1e5020420cab1b3306a6ae795417ba617e770..d454a3d99e060473c3348fe1c55736ec679f0904 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -145,8 +145,8 @@ impl From for Element { .attr("dstaddr", transport.dstaddr) .attr("mode", transport.mode) .append(match transport.payload { - TransportPayload::Candidates(mut candidates) => { - candidates.drain(..) + TransportPayload::Candidates(candidates) => { + candidates.into_iter() .map(Element::from) .collect::>() }, @@ -161,7 +161,7 @@ impl From for Element { .ns(ns::JINGLE_S5B) .build()) }, - TransportPayload::CandidateUsed(ref cid) => { + TransportPayload::CandidateUsed(cid) => { vec!(Element::builder("candidate-used") .ns(ns::JINGLE_S5B) .attr("cid", cid) From 75ae6100b4dbf7f59f4f54acc6c645139bcd4ac1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 02:41:42 +0100 Subject: [PATCH 301/698] data_forms: Remove unneccessary swallowing of an error. --- src/data_forms.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index eb751e0ba27632389faa0f286dfff851eea24c44..a2330a52dd616df97e8dc81da19e51f898349917 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -186,10 +186,8 @@ impl TryFrom for DataForm { value: value, }); } else if element.is("media", ns::MEDIA_ELEMENT) { - match MediaElement::try_from(element.clone()) { - Ok(media_element) => field.media.push(media_element), - Err(_) => (), // TODO: is it really nice to swallow this error? - } + let media_element = MediaElement::try_from(element.clone())?; + field.media.push(media_element); } else { return Err(Error::ParseError("Field child isn’t a value or media element.")); } From f83fe922446ef55cfd688f4c10d674253d18b31d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 02:47:33 +0100 Subject: [PATCH 302/698] jingle: Wrap the disposition in a struct, to get better type safety. --- src/jingle.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index 62fb4b5afdc4b6c387bac2f434b998dcb5e96c10..364ee4b82d6a57579a75e236a98b4ef14dc9347e 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -45,10 +45,19 @@ generate_attribute!(Senders, "senders", { generate_id!(ContentId); +// TODO: the list of values is defined, use an enum! +generate_id!(Disposition); + +impl Default for Disposition { + fn default() -> Disposition { + Disposition(String::from("session")) + } +} + #[derive(Debug, Clone)] pub struct Content { pub creator: Creator, - pub disposition: String, // TODO: the list of values is defined, use an enum! + pub disposition: Disposition, pub name: ContentId, pub senders: Senders, pub description: Option, @@ -66,7 +75,7 @@ impl TryFrom for Content { let mut content = Content { creator: get_attr!(elem, "creator", required), - disposition: get_attr!(elem, "disposition", optional).unwrap_or(String::from("session")), + disposition: get_attr!(elem, "disposition", default), name: get_attr!(elem, "name", required), senders: get_attr!(elem, "senders", default), description: None, @@ -344,7 +353,7 @@ mod tests { assert_eq!(jingle.contents[0].creator, Creator::Initiator); assert_eq!(jingle.contents[0].name, ContentId(String::from("coucou"))); assert_eq!(jingle.contents[0].senders, Senders::Both); - assert_eq!(jingle.contents[0].disposition, "session"); + assert_eq!(jingle.contents[0].disposition, Disposition(String::from("session"))); let elem: Element = "".parse().unwrap(); let jingle = Jingle::try_from(elem).unwrap(); @@ -352,7 +361,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let jingle = Jingle::try_from(elem).unwrap(); - assert_eq!(jingle.contents[0].disposition, "early-session"); + assert_eq!(jingle.contents[0].disposition, Disposition(String::from("early-session"))); } #[test] From dfb736a9731973626ec7c4c73dd991eb4e72f9e4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 03:03:45 +0100 Subject: [PATCH 303/698] rsm: Change if .is_some() { .unwrap() } into if let Some(). --- src/rsm.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rsm.rs b/src/rsm.rs index 008757dde50077dc620385da3044356bb9750c25..fa6f6c814fdbcedf798cb7588cc8da5d37706b33 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -97,8 +97,8 @@ impl From for Element { if set.before.is_some() { elem.append_child(Element::builder("before").ns(ns::RSM).append(set.before).build()); } - if set.count.is_some() { - elem.append_child(Element::builder("count").ns(ns::RSM).append(format!("{}", set.count.unwrap())).build()); + if let Some(count) = set.count { + elem.append_child(Element::builder("count").ns(ns::RSM).append(format!("{}", count)).build()); } if set.first.is_some() { elem.append_child(Element::builder("first") @@ -106,14 +106,14 @@ impl From for Element { .attr("index", set.first_index) .append(set.first).build()); } - if set.index.is_some() { - elem.append_child(Element::builder("index").ns(ns::RSM).append(format!("{}", set.index.unwrap())).build()); + if let Some(index) = set.index { + elem.append_child(Element::builder("index").ns(ns::RSM).append(format!("{}", index)).build()); } if set.last.is_some() { elem.append_child(Element::builder("last").ns(ns::RSM).append(set.last).build()); } - if set.max.is_some() { - elem.append_child(Element::builder("max").ns(ns::RSM).append(format!("{}", set.max.unwrap())).build()); + if let Some(max) = set.max { + elem.append_child(Element::builder("max").ns(ns::RSM).append(format!("{}", max)).build()); } elem } From db35d28c9c62484e397ac591377efc9cb6afa1d2 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 03:44:35 +0100 Subject: [PATCH 304/698] ibb: Split the stupid enum into three different structs. --- src/ibb.rs | 207 ++++++++++++++++++++++++++--------------------------- src/iq.rs | 16 +++-- 2 files changed, 113 insertions(+), 110 deletions(-) diff --git a/src/ibb.rs b/src/ibb.rs index cdc9b5cdb2e5395d6fe18ce8be1478be0f64dff3..2c01e1c741aa9785c5a308471f5bbfea2ed9e9d5 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -20,90 +20,104 @@ generate_attribute!(Stanza, "stanza", { }, Default = Iq); #[derive(Debug, Clone)] -pub enum IBB { - Open { - block_size: u16, - sid: String, - stanza: Stanza, - }, - Data { - seq: u16, - sid: String, - data: Vec, - }, - Close { - sid: String, - }, +pub struct Open { + pub block_size: u16, + pub sid: String, + pub stanza: Stanza, } -impl TryFrom for IBB { +impl TryFrom for Open { type Err = Error; - fn try_from(elem: Element) -> Result { - if elem.is("open", ns::IBB) { - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in open element.")); - } - let block_size = get_attr!(elem, "block-size", required); - let sid = get_attr!(elem, "sid", required); - let stanza = get_attr!(elem, "stanza", default); - Ok(IBB::Open { - block_size: block_size, - sid: sid, - stanza: stanza - }) - } else if elem.is("data", ns::IBB) { - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in data element.")); - } - let seq = get_attr!(elem, "seq", required); - let sid = get_attr!(elem, "sid", required); - let data = base64::decode(&elem.text())?; - Ok(IBB::Data { - seq: seq, - sid: sid, - data: data - }) - } else if elem.is("close", ns::IBB) { - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in close element.")); - } - let sid = get_attr!(elem, "sid", required); - Ok(IBB::Close { - sid: sid, - }) - } else { - Err(Error::ParseError("This is not an ibb element.")) + fn try_from(elem: Element) -> Result { + if !elem.is("open", ns::IBB) { + return Err(Error::ParseError("This is not an open element.")); } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in open element.")); + } + Ok(Open { + block_size: get_attr!(elem, "block-size", required), + sid: get_attr!(elem, "sid", required), + stanza: get_attr!(elem, "stanza", default), + }) + } +} + +impl From for Element { + fn from(open: Open) -> Element { + Element::builder("open") + .ns(ns::IBB) + .attr("block-size", open.block_size) + .attr("sid", open.sid) + .attr("stanza", open.stanza) + .build() + } +} + +#[derive(Debug, Clone)] +pub struct Data { + pub seq: u16, + pub sid: String, + pub data: Vec, +} + +impl TryFrom for Data { + type Err = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("data", ns::IBB) { + return Err(Error::ParseError("This is not a data element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in data element.")); + } + Ok(Data { + seq: get_attr!(elem, "seq", required), + sid: get_attr!(elem, "sid", required), + data: base64::decode(&elem.text())?, + }) + } +} + +impl From for Element { + fn from(data: Data) -> Element { + Element::builder("data") + .ns(ns::IBB) + .attr("seq", data.seq) + .attr("sid", data.sid) + .append(base64::encode(&data.data)) + .build() } } -impl From for Element { - fn from(ibb: IBB) -> Element { - match ibb { - IBB::Open { block_size, sid, stanza } => { - Element::builder("open") - .ns(ns::IBB) - .attr("block-size", block_size) - .attr("sid", sid) - .attr("stanza", stanza) - .build() - }, - IBB::Data { seq, sid, data } => { - Element::builder("data") - .ns(ns::IBB) - .attr("seq", seq) - .attr("sid", sid) - .append(base64::encode(&data)) - .build() - }, - IBB::Close { sid } => { - Element::builder("close") - .ns(ns::IBB) - .attr("sid", sid) - .build() - }, +#[derive(Debug, Clone)] +pub struct Close { + pub sid: String, +} + +impl TryFrom for Close { + type Err = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("close", ns::IBB) { + return Err(Error::ParseError("This is not a close element.")); } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in close element.")); + } + Ok(Close { + sid: get_attr!(elem, "sid", required), + }) + } +} + +impl From for Element { + fn from(close: Close) -> Element { + Element::builder("close") + .ns(ns::IBB) + .attr("sid", close.sid) + .build() } } @@ -115,41 +129,26 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let open = IBB::try_from(elem).unwrap(); - match open { - IBB::Open { block_size, sid, stanza } => { - assert_eq!(block_size, 3); - assert_eq!(sid, "coucou"); - assert_eq!(stanza, Stanza::Iq); - }, - _ => panic!(), - } + let open = Open::try_from(elem).unwrap(); + assert_eq!(open.block_size, 3); + assert_eq!(open.sid, "coucou"); + assert_eq!(open.stanza, Stanza::Iq); let elem: Element = "AAAA".parse().unwrap(); - let data = IBB::try_from(elem).unwrap(); - match data { - IBB::Data { seq, sid, data } => { - assert_eq!(seq, 0); - assert_eq!(sid, "coucou"); - assert_eq!(data, vec!(0, 0, 0)); - }, - _ => panic!(), - } + let data = Data::try_from(elem).unwrap(); + assert_eq!(data.seq, 0); + assert_eq!(data.sid, "coucou"); + assert_eq!(data.data, vec!(0, 0, 0)); let elem: Element = "".parse().unwrap(); - let close = IBB::try_from(elem).unwrap(); - match close { - IBB::Close { sid } => { - assert_eq!(sid, "coucou"); - }, - _ => panic!(), - } + let close = Close::try_from(elem).unwrap(); + assert_eq!(close.sid, "coucou"); } #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); - let error = IBB::try_from(elem).unwrap_err(); + let error = Open::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -157,7 +156,7 @@ mod tests { assert_eq!(message, "Required attribute 'block-size' missing."); let elem: Element = "".parse().unwrap(); - let error = IBB::try_from(elem).unwrap_err(); + let error = Open::try_from(elem).unwrap_err(); let message = match error { Error::ParseIntError(error) => error, _ => panic!(), @@ -165,7 +164,7 @@ mod tests { assert_eq!(message.description(), "invalid digit found in string"); let elem: Element = "".parse().unwrap(); - let error = IBB::try_from(elem).unwrap_err(); + let error = Open::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(error) => error, _ => panic!(), @@ -176,7 +175,7 @@ mod tests { #[test] fn test_invalid_stanza() { let elem: Element = "".parse().unwrap(); - let error = IBB::try_from(elem).unwrap_err(); + let error = Open::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/iq.rs b/src/iq.rs index 1eae2549cb66a24330e719622615b78c728f2465..2584ebeb66f1c081cce8e3f2aa9bdded33bb8a08 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -19,7 +19,7 @@ use ns; use stanza_error::StanzaError; use roster::Roster; use disco::{DiscoInfoResult, DiscoInfoQuery}; -use ibb::IBB; +use ibb::{Open as IbbOpen, Data as IbbData, Close as IbbClose}; use jingle::Jingle; use ping::Ping; use mam::{Query as MamQuery, Fin as MamFin, Prefs as MamPrefs}; @@ -40,7 +40,9 @@ pub enum IqGetPayload { #[derive(Debug, Clone)] pub enum IqSetPayload { Roster(Roster), - IBB(IBB), + IbbOpen(IbbOpen), + IbbData(IbbData), + IbbClose(IbbClose), Jingle(Jingle), MamQuery(MamQuery), MamPrefs(MamPrefs), @@ -106,9 +108,9 @@ impl TryFrom for IqSetPayload { ("query", ns::ROSTER) => IqSetPayload::Roster(Roster::try_from(elem)?), // XEP-0047 - ("open", ns::IBB) - | ("data", ns::IBB) - | ("close", ns::IBB) => IqSetPayload::IBB(IBB::try_from(elem)?), + ("open", ns::IBB) => IqSetPayload::IbbOpen(IbbOpen::try_from(elem)?), + ("data", ns::IBB) => IqSetPayload::IbbData(IbbData::try_from(elem)?), + ("close", ns::IBB) => IqSetPayload::IbbClose(IbbClose::try_from(elem)?), // XEP-0166 ("jingle", ns::JINGLE) => IqSetPayload::Jingle(Jingle::try_from(elem)?), @@ -126,7 +128,9 @@ impl From for Element { fn from(payload: IqSetPayload) -> Element { match payload { IqSetPayload::Roster(roster) => roster.into(), - IqSetPayload::IBB(ibb) => ibb.into(), + IqSetPayload::IbbOpen(open) => open.into(), + IqSetPayload::IbbData(data) => data.into(), + IqSetPayload::IbbClose(close) => close.into(), IqSetPayload::Jingle(jingle) => jingle.into(), IqSetPayload::MamQuery(query) => query.into(), IqSetPayload::MamPrefs(prefs) => prefs.into(), From a0b1d93ff09398474110088b0f77d3ba8ce3f11d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 03:51:41 +0100 Subject: [PATCH 305/698] stanza_id: Split the stupid enum into two different structs. --- src/message.rs | 8 ++-- src/stanza_id.rs | 95 +++++++++++++++++++++++++----------------------- 2 files changed, 54 insertions(+), 49 deletions(-) diff --git a/src/message.rs b/src/message.rs index 29234321f4baabaef635a52593ba4b9e60b6ee11..90bce3dbf9d022a95f1cf056f51084a72a2bf2c5 100644 --- a/src/message.rs +++ b/src/message.rs @@ -23,7 +23,7 @@ use delay::Delay; use attention::Attention; use message_correct::Replace; use eme::ExplicitMessageEncryption; -use stanza_id::StanzaId; +use stanza_id::{StanzaId, OriginId}; use mam::Result_ as MamResult; /// Lists every known payload of a ``. @@ -37,6 +37,7 @@ pub enum MessagePayload { MessageCorrect(Replace), ExplicitMessageEncryption(ExplicitMessageEncryption), StanzaId(StanzaId), + OriginId(OriginId), MamResult(MamResult), Unknown(Element), @@ -73,8 +74,8 @@ impl TryFrom for MessagePayload { ("result", ns::MAM) => MessagePayload::MamResult(MamResult::try_from(elem)?), // XEP-0359 - ("stanza-id", ns::SID) - | ("origin-id", ns::SID) => MessagePayload::StanzaId(StanzaId::try_from(elem)?), + ("stanza-id", ns::SID) => MessagePayload::StanzaId(StanzaId::try_from(elem)?), + ("origin-id", ns::SID) => MessagePayload::OriginId(OriginId::try_from(elem)?), // XEP-0380 ("encryption", ns::EME) => MessagePayload::ExplicitMessageEncryption(ExplicitMessageEncryption::try_from(elem)?), @@ -95,6 +96,7 @@ impl From for Element { MessagePayload::MessageCorrect(replace) => replace.into(), MessagePayload::ExplicitMessageEncryption(eme) => eme.into(), MessagePayload::StanzaId(stanza_id) => stanza_id.into(), + MessagePayload::OriginId(origin_id) => origin_id.into(), MessagePayload::MamResult(result) => result.into(), MessagePayload::Unknown(elem) => elem, diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 72687d9b3dbd335dadf16d14fea9262344ba4852..43ce96452983f6ccaeee0fe68a952bfbcc75aba8 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -14,54 +14,65 @@ use error::Error; use ns; #[derive(Debug, Clone)] -pub enum StanzaId { - StanzaId { - id: String, - by: Jid, - }, - OriginId { - id: String, - }, +pub struct StanzaId { + pub id: String, + pub by: Jid, } impl TryFrom for StanzaId { type Err = Error; fn try_from(elem: Element) -> Result { - let is_stanza_id = elem.is("stanza-id", ns::SID); - if !is_stanza_id && !elem.is("origin-id", ns::SID) { - return Err(Error::ParseError("This is not a stanza-id or origin-id element.")); + if !elem.is("stanza-id", ns::SID) { + return Err(Error::ParseError("This is not a stanza-id element.")); } for _ in elem.children() { - return Err(Error::ParseError("Unknown child in stanza-id or origin-id element.")); + return Err(Error::ParseError("Unknown child in stanza-id element.")); } - let id = get_attr!(elem, "id", required); - Ok(if is_stanza_id { - let by = get_attr!(elem, "by", required); - StanzaId::StanzaId { id, by } - } else { - StanzaId::OriginId { id } + Ok(StanzaId { + id: get_attr!(elem, "id", required), + by: get_attr!(elem, "by", required), }) } } impl From for Element { fn from(stanza_id: StanzaId) -> Element { - match stanza_id { - StanzaId::StanzaId { id, by } => { - Element::builder("stanza-id") - .ns(ns::SID) - .attr("id", id) - .attr("by", String::from(by)) - .build() - }, - StanzaId::OriginId { id } => { - Element::builder("origin-id") - .ns(ns::SID) - .attr("id", id) - .build() - }, + Element::builder("stanza-id") + .ns(ns::SID) + .attr("id", stanza_id.id) + .attr("by", String::from(stanza_id.by)) + .build() + } +} + +#[derive(Debug, Clone)] +pub struct OriginId { + pub id: String, +} + +impl TryFrom for OriginId { + type Err = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("origin-id", ns::SID) { + return Err(Error::ParseError("This is not an origin-id element.")); } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in origin-id element.")); + } + Ok(OriginId { + id: get_attr!(elem, "id", required), + }) + } +} + +impl From for Element { + fn from(origin_id: OriginId) -> Element { + Element::builder("origin-id") + .ns(ns::SID) + .attr("id", origin_id.id) + .build() } } @@ -74,20 +85,12 @@ mod tests { fn test_simple() { let elem: Element = "".parse().unwrap(); let stanza_id = StanzaId::try_from(elem).unwrap(); - if let StanzaId::StanzaId { id, by } = stanza_id { - assert_eq!(id, String::from("coucou")); - assert_eq!(by, Jid::from_str("coucou@coucou").unwrap()); - } else { - panic!(); - } + assert_eq!(stanza_id.id, String::from("coucou")); + assert_eq!(stanza_id.by, Jid::from_str("coucou@coucou").unwrap()); let elem: Element = "".parse().unwrap(); - let stanza_id = StanzaId::try_from(elem).unwrap(); - if let StanzaId::OriginId { id } = stanza_id { - assert_eq!(id, String::from("coucou")); - } else { - panic!(); - } + let origin_id = OriginId::try_from(elem).unwrap(); + assert_eq!(origin_id.id, String::from("coucou")); } #[test] @@ -98,7 +101,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown child in stanza-id or origin-id element."); + assert_eq!(message, "Unknown child in stanza-id element."); } #[test] @@ -126,7 +129,7 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let stanza_id = StanzaId::StanzaId { id: String::from("coucou"), by: Jid::from_str("coucou@coucou").unwrap() }; + let stanza_id = StanzaId { id: String::from("coucou"), by: Jid::from_str("coucou@coucou").unwrap() }; let elem2 = stanza_id.into(); assert_eq!(elem, elem2); } From 37d1ae64ad49e128cd89cbfbb9b1f33b7cad6d24 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 04:00:25 +0100 Subject: [PATCH 306/698] receipts: Split the stupid enum into two different structs. --- src/message.rs | 12 ++++--- src/receipts.rs | 90 ++++++++++++++++++++++++++++++------------------- 2 files changed, 63 insertions(+), 39 deletions(-) diff --git a/src/message.rs b/src/message.rs index 90bce3dbf9d022a95f1cf056f51084a72a2bf2c5..759b7d2304c1653177693dad8cbe30763e151cf1 100644 --- a/src/message.rs +++ b/src/message.rs @@ -18,7 +18,7 @@ use ns; use stanza_error::StanzaError; use chatstates::ChatState; -use receipts::Receipt; +use receipts::{Request as ReceiptRequest, Received as ReceiptReceived}; use delay::Delay; use attention::Attention; use message_correct::Replace; @@ -31,7 +31,8 @@ use mam::Result_ as MamResult; pub enum MessagePayload { StanzaError(StanzaError), ChatState(ChatState), - Receipt(Receipt), + ReceiptRequest(ReceiptRequest), + ReceiptReceived(ReceiptReceived), Delay(Delay), Attention(Attention), MessageCorrect(Replace), @@ -58,8 +59,8 @@ impl TryFrom for MessagePayload { | ("gone", ns::CHATSTATES) => MessagePayload::ChatState(ChatState::try_from(elem)?), // XEP-0184 - ("request", ns::RECEIPTS) - | ("received", ns::RECEIPTS) => MessagePayload::Receipt(Receipt::try_from(elem)?), + ("request", ns::RECEIPTS) => MessagePayload::ReceiptRequest(ReceiptRequest::try_from(elem)?), + ("received", ns::RECEIPTS) => MessagePayload::ReceiptReceived(ReceiptReceived::try_from(elem)?), // XEP-0203 ("delay", ns::DELAY) => MessagePayload::Delay(Delay::try_from(elem)?), @@ -91,7 +92,8 @@ impl From for Element { MessagePayload::StanzaError(stanza_error) => stanza_error.into(), MessagePayload::Attention(attention) => attention.into(), MessagePayload::ChatState(chatstate) => chatstate.into(), - MessagePayload::Receipt(receipt) => receipt.into(), + MessagePayload::ReceiptRequest(request) => request.into(), + MessagePayload::ReceiptReceived(received) => received.into(), MessagePayload::Delay(delay) => delay.into(), MessagePayload::MessageCorrect(replace) => replace.into(), MessagePayload::ExplicitMessageEncryption(eme) => eme.into(), diff --git a/src/receipts.rs b/src/receipts.rs index 7c3625b8daac481a120d068a52e100d17f825891..6ccec7eae1f500062175bfdc776eb5e3ac178106 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -13,46 +13,65 @@ use error::Error; use ns; #[derive(Debug, Clone)] -pub enum Receipt { - Request, - Received(Option), +pub struct Request; + +impl TryFrom for Request { + type Err = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("request", ns::RECEIPTS) { + return Err(Error::ParseError("This is not a request element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in request element.")); + } + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown attribute in request element.")); + } + Ok(Request) + } } -impl TryFrom for Receipt { +impl From for Element { + fn from(_: Request) -> Element { + Element::builder("request") + .ns(ns::RECEIPTS) + .build() + } +} + +#[derive(Debug, Clone)] +pub struct Received { + pub id: Option, +} + +impl TryFrom for Received { type Err = Error; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { + if !elem.is("received", ns::RECEIPTS) { + return Err(Error::ParseError("This is not a received element.")); + } for _ in elem.children() { - return Err(Error::ParseError("Unknown child in receipt element.")); + return Err(Error::ParseError("Unknown child in received element.")); } - if elem.is("request", ns::RECEIPTS) { - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown attribute in request element.")); - } - Ok(Receipt::Request) - } else if elem.is("received", ns::RECEIPTS) { - for (attr, _) in elem.attrs() { - if attr != "id" { - return Err(Error::ParseError("Unknown attribute in received element.")); - } + for (attr, _) in elem.attrs() { + if attr != "id" { + return Err(Error::ParseError("Unknown attribute in received element.")); } - let id = get_attr!(elem, "id", optional); - Ok(Receipt::Received(id)) - } else { - Err(Error::ParseError("This is not a receipt element.")) } + Ok(Received { + id: get_attr!(elem, "id", optional), + }) } } -impl From for Element { - fn from(receipt: Receipt) -> Element { - match receipt { - Receipt::Request => Element::builder("request") - .ns(ns::RECEIPTS), - Receipt::Received(id) => Element::builder("received") - .ns(ns::RECEIPTS) - .attr("id", id), - }.build() +impl From for Element { + fn from(received: Received) -> Element { + Element::builder("received") + .ns(ns::RECEIPTS) + .attr("id", received.id) + .build() } } @@ -63,22 +82,25 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - Receipt::try_from(elem).unwrap(); + Request::try_from(elem).unwrap(); let elem: Element = "".parse().unwrap(); - Receipt::try_from(elem).unwrap(); + Received::try_from(elem).unwrap(); let elem: Element = "".parse().unwrap(); - Receipt::try_from(elem).unwrap(); + Received::try_from(elem).unwrap(); } #[test] fn test_serialise() { - let receipt = Receipt::Request; + let receipt = Request; let elem: Element = receipt.into(); assert!(elem.is("request", ns::RECEIPTS)); + assert_eq!(elem.attrs().count(), 0); - let receipt = Receipt::Received(Some(String::from("coucou"))); + let receipt = Received { + id: Some(String::from("coucou")), + }; let elem: Element = receipt.into(); assert!(elem.is("received", ns::RECEIPTS)); assert_eq!(elem.attr("id"), Some("coucou")); From 58760fc28dc8349f993bf764e12340845cf5ff69 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 04:19:58 +0100 Subject: [PATCH 307/698] data_forms: Split field parsing into its own TryFrom impl. --- src/data_forms.rs | 138 +++++++++++++++++++++++++--------------------- 1 file changed, 74 insertions(+), 64 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index a2330a52dd616df97e8dc81da19e51f898349917..15135ef3df2edfde96108bf0015bce6bf59e46ed 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -57,6 +57,78 @@ pub struct Field { pub media: Vec, } +impl Field { + fn is_list(&self) -> bool { + self.type_ == FieldType::ListSingle || + self.type_ == FieldType::ListMulti + } +} + +impl TryFrom for Field { + type Err = Error; + + fn try_from(elem: Element) -> Result { + let mut field = Field { + var: get_attr!(elem, "var", required), + type_: get_attr!(elem, "type", default), + label: get_attr!(elem, "label", optional), + required: false, + options: vec!(), + values: vec!(), + media: vec!(), + }; + for element in elem.children() { + if element.is("value", ns::DATA_FORMS) { + for _ in element.children() { + return Err(Error::ParseError("Value element must not have any child.")); + } + for _ in element.attrs() { + return Err(Error::ParseError("Value element must not have any attribute.")); + } + field.values.push(element.text()); + } else if element.is("required", ns::DATA_FORMS) { + if field.required { + return Err(Error::ParseError("More than one required element.")); + } + for _ in element.children() { + return Err(Error::ParseError("Required element must not have any child.")); + } + for _ in element.attrs() { + return Err(Error::ParseError("Required element must not have any attribute.")); + } + field.required = true; + } else if element.is("option", ns::DATA_FORMS) { + if !field.is_list() { + return Err(Error::ParseError("Option element found in non-list field.")); + } + let label = get_attr!(element, "label", optional); + let mut value = None; + for child2 in element.children() { + if child2.is("value", ns::DATA_FORMS) { + if value.is_some() { + return Err(Error::ParseError("More than one value element in option element")); + } + value = Some(child2.text()); + } else { + return Err(Error::ParseError("Non-value element in option element")); + } + } + let value = value.ok_or(Error::ParseError("No value element in option element"))?; + field.options.push(Option_ { + label: label, + value: value, + }); + } else if element.is("media", ns::MEDIA_ELEMENT) { + let media_element = MediaElement::try_from(element.clone())?; + field.media.push(media_element); + } else { + return Err(Error::ParseError("Field child isn’t a value or media element.")); + } + } + Ok(field) + } +} + impl From for Element { fn from(field: Field) -> Element { Element::builder("field") @@ -129,70 +201,8 @@ impl TryFrom for DataForm { } form.instructions = Some(child.text()); } else if child.is("field", ns::DATA_FORMS) { - let var: String = get_attr!(child, "var", required); - let field_type = get_attr!(child, "type", default); - let label = get_attr!(child, "label", optional); - - let is_form_type = var == "FORM_TYPE" && field_type == FieldType::Hidden; - let is_list = field_type == FieldType::ListSingle || field_type == FieldType::ListMulti; - let mut field = Field { - var: var, - type_: field_type, - label: label, - required: false, - options: vec!(), - values: vec!(), - media: vec!(), - }; - for element in child.children() { - if element.is("value", ns::DATA_FORMS) { - for _ in element.children() { - return Err(Error::ParseError("Value element must not have any child.")); - } - for _ in element.attrs() { - return Err(Error::ParseError("Value element must not have any attribute.")); - } - field.values.push(element.text()); - } else if element.is("required", ns::DATA_FORMS) { - if field.required { - return Err(Error::ParseError("More than one required element.")); - } - for _ in element.children() { - return Err(Error::ParseError("Required element must not have any child.")); - } - for _ in element.attrs() { - return Err(Error::ParseError("Required element must not have any attribute.")); - } - field.required = true; - } else if element.is("option", ns::DATA_FORMS) { - if !is_list { - return Err(Error::ParseError("Option element found in non-list field.")); - } - let label = get_attr!(element, "label", optional); - let mut value = None; - for child2 in element.children() { - if child2.is("value", ns::DATA_FORMS) { - if value.is_some() { - return Err(Error::ParseError("More than one value element in option element")); - } - value = Some(child2.text()); - } else { - return Err(Error::ParseError("Non-value element in option element")); - } - } - let value = value.ok_or(Error::ParseError("No value element in option element"))?; - field.options.push(Option_ { - label: label, - value: value, - }); - } else if element.is("media", ns::MEDIA_ELEMENT) { - let media_element = MediaElement::try_from(element.clone())?; - field.media.push(media_element); - } else { - return Err(Error::ParseError("Field child isn’t a value or media element.")); - } - } - if is_form_type { + let field = Field::try_from(child.clone())?; + if field.var == "FORM_TYPE" && field.type_ == FieldType::Hidden { if form.form_type.is_some() { return Err(Error::ParseError("More than one FORM_TYPE in a data form.")); } From 5ece20a029f27bac74eab2e954c8f692f0bf9595 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 04:25:55 +0100 Subject: [PATCH 308/698] disco: Create a mutable DiscoInfoResult at the beginning of its parsing. --- src/disco.rs | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index aa816decd0bbbe07bd61f0b4fdb339c96c2e640c..1649682dd53a3d98961b2d14a250320100aca41a 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -99,16 +99,17 @@ impl TryFrom for DiscoInfoResult { return Err(Error::ParseError("This is not a disco#info element.")); } - let mut identities: Vec = vec!(); - let mut features: Vec = vec!(); - let mut extensions: Vec = vec!(); - - let node = get_attr!(elem, "node", optional); + let mut result = DiscoInfoResult { + node: get_attr!(elem, "node", optional), + identities: vec!(), + features: vec!(), + extensions: vec!(), + }; for child in elem.children() { if child.is("feature", ns::DISCO_INFO) { let feature = get_attr!(child, "var", required); - features.push(Feature { + result.features.push(Feature { var: feature, }); } else if child.is("identity", ns::DISCO_INFO) { @@ -124,7 +125,7 @@ impl TryFrom for DiscoInfoResult { let lang = get_attr!(child, "xml:lang", optional); let name = get_attr!(child, "name", optional); - identities.push(Identity { + result.identities.push(Identity { category: category, type_: type_, lang: lang, @@ -135,31 +136,26 @@ impl TryFrom for DiscoInfoResult { if data_form.type_ != DataFormType::Result_ { return Err(Error::ParseError("Data form must have a 'result' type in disco#info.")); } - match data_form.form_type { - Some(_) => extensions.push(data_form), - None => return Err(Error::ParseError("Data form found without a FORM_TYPE.")), + if data_form.form_type.is_none() { + return Err(Error::ParseError("Data form found without a FORM_TYPE.")); } + result.extensions.push(data_form); } else { return Err(Error::ParseError("Unknown element in disco#info.")); } } - if identities.is_empty() { + if result.identities.is_empty() { return Err(Error::ParseError("There must be at least one identity in disco#info.")); } - if features.is_empty() { + if result.features.is_empty() { return Err(Error::ParseError("There must be at least one feature in disco#info.")); } - if !features.contains(&Feature { var: ns::DISCO_INFO.to_owned() }) { + if !result.features.contains(&Feature { var: ns::DISCO_INFO.to_owned() }) { return Err(Error::ParseError("disco#info feature not present in disco#info.")); } - Ok(DiscoInfoResult { - node: node, - identities: identities, - features: features, - extensions: extensions - }) + Ok(result) } } From 4d3717d17095fcea8f1a1aad5d5a5039500dc2bf Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 04:35:15 +0100 Subject: [PATCH 309/698] disco: Split Feature and Identity parsing out of DiscoQueryResult. --- src/disco.rs | 74 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 22 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index 1649682dd53a3d98961b2d14a250320100aca41a..f3fad62e85352ddf55bada8b9331137dd333c3c4 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -54,6 +54,27 @@ pub struct Feature { pub var: String, } +impl TryFrom for Feature { + type Err = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("feature", ns::DISCO_INFO) { + return Err(Error::ParseError("This is not a disco#info feature element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in disco#info feature element.")); + } + for (attr, _) in elem.attrs() { + if attr != "var" { + return Err(Error::ParseError("Unknown attribute in disco#info feature element.")); + } + } + Ok(Feature { + var: get_attr!(elem, "var", required) + }) + } +} + impl From for Element { fn from(feature: Feature) -> Element { Element::builder("feature") @@ -71,6 +92,33 @@ pub struct Identity { pub name: Option, } +impl TryFrom for Identity { + type Err = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("identity", ns::DISCO_INFO) { + return Err(Error::ParseError("This is not a disco#info identity element.")); + } + + let category = get_attr!(elem, "category", required); + if category == "" { + return Err(Error::ParseError("Identity must have a non-empty 'category' attribute.")) + } + + let type_ = get_attr!(elem, "type", required); + if type_ == "" { + return Err(Error::ParseError("Identity must have a non-empty 'type' attribute.")) + } + + Ok(Identity { + category: category, + type_: type_, + lang: get_attr!(elem, "xml:lang", optional), + name: get_attr!(elem, "name", optional), + }) + } +} + impl From for Element { fn from(identity: Identity) -> Element { Element::builder("identity") @@ -108,29 +156,11 @@ impl TryFrom for DiscoInfoResult { for child in elem.children() { if child.is("feature", ns::DISCO_INFO) { - let feature = get_attr!(child, "var", required); - result.features.push(Feature { - var: feature, - }); + let feature = Feature::try_from(child.clone())?; + result.features.push(feature); } else if child.is("identity", ns::DISCO_INFO) { - let category = get_attr!(child, "category", required); - if category == "" { - return Err(Error::ParseError("Identity must have a non-empty 'category' attribute.")) - } - - let type_ = get_attr!(child, "type", required); - if type_ == "" { - return Err(Error::ParseError("Identity must have a non-empty 'type' attribute.")) - } - - let lang = get_attr!(child, "xml:lang", optional); - let name = get_attr!(child, "name", optional); - result.identities.push(Identity { - category: category, - type_: type_, - lang: lang, - name: name, - }); + let identity = Identity::try_from(child.clone())?; + result.identities.push(identity); } else if child.is("x", ns::DATA_FORMS) { let data_form = DataForm::try_from(child.clone())?; if data_form.type_ != DataFormType::Result_ { From 67e72b009e0e5b12c74c308e89bb415826e4f30d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 04:39:50 +0100 Subject: [PATCH 310/698] disco: Fix serialisation of extensions, and add a test. --- src/disco.rs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index f3fad62e85352ddf55bada8b9331137dd333c3c4..88e105ce4c1ab69c9c923663fdeb07f88a313c35 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -191,14 +191,12 @@ impl TryFrom for DiscoInfoResult { impl From for Element { fn from(disco: DiscoInfoResult) -> Element { - for _ in disco.extensions { - panic!("Not yet implemented!"); - } Element::builder("query") .ns(ns::DISCO_INFO) .attr("node", disco.node) .append(disco.identities) .append(disco.features) + .append(disco.extensions) .build() } } @@ -339,6 +337,21 @@ mod tests { assert!(query.extensions.is_empty()); } + #[test] + fn test_extension() { + let elem: Element = "example".parse().unwrap(); + let elem1 = elem.clone(); + let query = DiscoInfoResult::try_from(elem).unwrap(); + assert!(query.node.is_none()); + assert_eq!(query.identities.len(), 1); + assert_eq!(query.features.len(), 1); + assert_eq!(query.extensions.len(), 1); + assert_eq!(query.extensions[0].form_type, Some(String::from("example"))); + + let elem2 = query.into(); + assert_eq!(elem1, elem2); + } + #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); From 45d196463c236e70d0b967f339fcbc9bc39f6268 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 04:51:51 +0100 Subject: [PATCH 311/698] disco: Document every struct and their fields. --- src/disco.rs | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/disco.rs b/src/disco.rs index 88e105ce4c1ab69c9c923663fdeb07f88a313c35..b976e362d2c13c2993b3367b1707ab0e7266fbe5 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#![deny(missing_docs)] + use try_from::TryFrom; use minidom::Element; @@ -14,8 +16,13 @@ use ns; use data_forms::{DataForm, DataFormType}; +/// Structure representing a `` element. +/// +/// It should only be used in an ``, as it can only represent +/// the request, and not a result. #[derive(Debug, Clone)] pub struct DiscoInfoQuery { + /// Node on which we are doing the discovery. pub node: Option, } @@ -49,8 +56,10 @@ impl From for Element { } } +/// Structure representing a `` element. #[derive(Debug, Clone, PartialEq)] pub struct Feature { + /// Namespace of the feature we want to represent. pub var: String, } @@ -84,11 +93,19 @@ impl From for Element { } } +/// Structure representing an `` element. #[derive(Debug, Clone)] pub struct Identity { + /// Category of this identity. pub category: String, // TODO: use an enum here. + + /// Type of this identity. pub type_: String, // TODO: use an enum here. + + /// Lang of the name of this identity. pub lang: Option, + + /// Name of this identity. pub name: Option, } @@ -131,11 +148,22 @@ impl From for Element { } } +/// Structure representing a `` element. +/// +/// It should only be used in an ``, as it can only +/// represent the result, and not a request. #[derive(Debug, Clone)] pub struct DiscoInfoResult { + /// Node on which we have done this discovery. pub node: Option, + + /// List of identities exposed by this entity. pub identities: Vec, + + /// List of features supported by this entity. pub features: Vec, + + /// List of extensions reported by this entity. pub extensions: Vec, } @@ -201,8 +229,13 @@ impl From for Element { } } +/// Structure representing a `` element. +/// +/// It should only be used in an ``, as it can only represent +/// the request, and not a result. #[derive(Debug, Clone)] pub struct DiscoItemsQuery { + /// Node on which we are doing the discovery. pub node: Option, } @@ -236,10 +269,14 @@ impl From for Element { } } +/// Structure representing an `` element. #[derive(Debug, Clone)] pub struct Item { + /// JID of the entity pointed by this item. pub jid: Jid, + /// Node of the entity pointed by this item. pub node: Option, + /// Name of the entity pointed by this item. pub name: Option, } @@ -277,9 +314,17 @@ impl From for Element { } } +/// Structure representing a `` element. +/// +/// It should only be used in an ``, as it can only +/// represent the result, and not a request. #[derive(Debug, Clone)] pub struct DiscoItemsResult { + /// Node on which we have done this discovery. pub node: Option, + + /// List of items pointed by this entity. pub items: Vec, } From 5388696b9526c3378db93a325822e7f5b45a8ce3 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 05:24:20 +0100 Subject: [PATCH 312/698] =?UTF-8?q?lib:=20Remove=20erroneous=20=E2=80=9Cre?= =?UTF-8?q?ference=E2=80=9D=20mention=20in=20the=20module=20docstring.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b3f57fdf675ebffca54f744d5efd7b68eca9b97f..ce1610e445721161cf7f46c1182d74598a1f5e13 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ //! A crate parsing common XMPP elements into Rust structures. //! -//! Each module implements the [`TryFrom`] trait, which takes a minidom -//! [`Element`] reference and returns a `Result` whose value is `Ok` if the +//! Each module implements the [`TryFrom`] trait, which takes a +//! minidom [`Element`] and returns a `Result` whose value is `Ok` if the //! element parsed correctly, `Err(error::Error)` otherwise. //! //! The returned structure can be manipuled as any Rust structure, with each From 6ec1e46953029a14ea45290e2351354ad1abc4d0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 05:36:59 +0100 Subject: [PATCH 313/698] roster: Make Group a proper struct. --- src/lib.rs | 22 ++++++++++++++++++++++ src/roster.rs | 18 +++++++++++------- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ce1610e445721161cf7f46c1182d74598a1f5e13..f88108238fd885afeb7dcea4212fe08c4079edb8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -144,6 +144,28 @@ macro_rules! generate_id { ); } +macro_rules! generate_elem_id { + ($elem:ident, $name:tt, $ns:expr) => ( + #[derive(Debug, Clone, PartialEq, Eq, Hash)] + pub struct $elem(pub String); + impl FromStr for $elem { + type Err = Error; + fn from_str(s: &str) -> Result<$elem, Error> { + // TODO: add a way to parse that differently when needed. + Ok($elem(String::from(s))) + } + } + impl From<$elem> for Element { + fn from(elem: $elem) -> Element { + Element::builder($name) + .ns($ns) + .append(elem.0) + .build() + } + } + ); +} + /// Error type returned by every parser on failure. pub mod error; /// XML namespace definitions used through XMPP. diff --git a/src/roster.rs b/src/roster.rs index 1a4cc50e8df99c8b3ba89478364ef6339eaa4af7..a1a07eb10d9da0ec19a89c1052ab7834c1f2ac94 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -13,7 +13,7 @@ use jid::Jid; use error::Error; use ns; -type Group = String; +generate_elem_id!(Group, "group", ns::ROSTER); generate_attribute!(Subscription, "subscription", { None => "none", @@ -52,7 +52,11 @@ impl TryFrom for Item { for _ in child.children() { return Err(Error::ParseError("Roster item group can’t have children.")); } - item.groups.push(child.text()); + for _ in child.attrs() { + return Err(Error::ParseError("Roster item group can’t have attributes.")); + } + let group = Group(child.text()); + item.groups.push(group); } Ok(item) } @@ -65,7 +69,7 @@ impl From for Element { .attr("jid", String::from(item.jid)) .attr("name", item.name) .attr("subscription", item.subscription) - .append(item.groups.into_iter().map(|group| Element::builder("group").ns(ns::ROSTER).append(group)).collect::>()) + .append(item.groups) .build() } } @@ -163,7 +167,7 @@ mod tests { assert_eq!(roster.items[0].jid, Jid::from_str("romeo@example.net").unwrap()); assert_eq!(roster.items[0].name, Some(String::from("Romeo"))); assert_eq!(roster.items[0].subscription, Some(Subscription::Both)); - assert_eq!(roster.items[0].groups, vec!(String::from("Friends"))); + assert_eq!(roster.items[0].groups, vec!(Group::from_str("Friends").unwrap())); } #[test] @@ -183,8 +187,8 @@ mod tests { assert_eq!(roster.items[0].jid, Jid::from_str("test@example.org").unwrap()); assert_eq!(roster.items[0].name, None); assert_eq!(roster.items[0].groups.len(), 2); - assert_eq!(roster.items[0].groups[0], String::from("A")); - assert_eq!(roster.items[0].groups[1], String::from("B")); + assert_eq!(roster.items[0].groups[0], Group::from_str("A").unwrap()); + assert_eq!(roster.items[0].groups[1], Group::from_str("B").unwrap()); let elem2 = roster.into(); assert_eq!(elem1, elem2); } @@ -210,7 +214,7 @@ mod tests { assert_eq!(roster.items[0].jid, Jid::from_str("nurse@example.com").unwrap()); assert_eq!(roster.items[0].name, Some(String::from("Nurse"))); assert_eq!(roster.items[0].groups.len(), 1); - assert_eq!(roster.items[0].groups[0], String::from("Servants")); + assert_eq!(roster.items[0].groups[0], Group::from_str("Servants").unwrap()); let elem: Element = r#" From 56a66f8c4b1e7f1d2efdd227b04aaab9f33860b1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 06:11:48 +0100 Subject: [PATCH 314/698] message: Enforce more type safety on Body, Subject and Thread. --- src/message.rs | 51 +++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/src/message.rs b/src/message.rs index 759b7d2304c1653177693dad8cbe30763e151cf1..80a0e0445fd4fbc2a440b4f38364328c38401c69 100644 --- a/src/message.rs +++ b/src/message.rs @@ -115,9 +115,10 @@ generate_attribute!(MessageType, "type", { }, Default = Normal); type Lang = String; -type Body = String; -type Subject = String; -type Thread = String; + +generate_elem_id!(Body, "body", ns::JABBER_CLIENT); +generate_elem_id!(Subject, "subject", ns::JABBER_CLIENT); +generate_elem_id!(Thread, "thread", ns::JABBER_CLIENT); /// The main structure representing the `` stanza. #[derive(Debug, Clone)] @@ -168,7 +169,8 @@ impl TryFrom for Message { return Err(Error::ParseError("Unknown child in body element.")); } let lang = get_attr!(root, "xml:lang", default); - if bodies.insert(lang, elem.text()).is_some() { + let body = Body(elem.text()); + if bodies.insert(lang, body).is_some() { return Err(Error::ParseError("Body element present twice for the same xml:lang.")); } } else if elem.is("subject", ns::JABBER_CLIENT) { @@ -176,7 +178,8 @@ impl TryFrom for Message { return Err(Error::ParseError("Unknown child in subject element.")); } let lang = get_attr!(root, "xml:lang", default); - if subjects.insert(lang, elem.text()).is_some() { + let subject = Subject(elem.text()); + if subjects.insert(lang, subject).is_some() { return Err(Error::ParseError("Subject element present twice for the same xml:lang.")); } } else if elem.is("thread", ns::JABBER_CLIENT) { @@ -186,7 +189,7 @@ impl TryFrom for Message { for _ in elem.children() { return Err(Error::ParseError("Unknown child in thread element.")); } - thread = Some(elem.text()); + thread = Some(Thread(elem.text())); } else { payloads.push(elem.clone()) } @@ -214,25 +217,23 @@ impl From for Element { .attr("type", message.type_) .append(message.subjects.into_iter() .map(|(lang, subject)| { - Element::builder("subject") - .ns(ns::JABBER_CLIENT) - .attr("xml:lang", match lang.as_ref() { - "" => None, - lang => Some(lang), - }) - .append(subject) - .build() }) + let mut subject = Element::from(subject); + subject.set_attr("xml:lang", match lang.as_ref() { + "" => None, + lang => Some(lang), + }); + subject + }) .collect::>()) .append(message.bodies.into_iter() .map(|(lang, body)| { - Element::builder("body") - .ns(ns::JABBER_CLIENT) - .attr("xml:lang", match lang.as_ref() { - "" => None, - lang => Some(lang), - }) - .append(body) - .build() }) + let mut body = Element::from(body); + body.set_attr("xml:lang", match lang.as_ref() { + "" => None, + lang => Some(lang), + }); + body + }) .collect::>()) .append(message.payloads) .build() @@ -268,7 +269,7 @@ mod tests { let elem: Element = "Hello world!".parse().unwrap(); let elem1 = elem.clone(); let message = Message::try_from(elem).unwrap(); - assert_eq!(message.bodies[""], "Hello world!"); + assert_eq!(message.bodies[""], Body::from_str("Hello world!").unwrap()); let elem2 = message.into(); assert_eq!(elem1, elem2); @@ -278,7 +279,7 @@ mod tests { fn test_serialise_body() { let elem: Element = "Hello world!".parse().unwrap(); let mut message = Message::new(Some(Jid::from_str("coucou@example.org").unwrap())); - message.bodies.insert(String::from(""), String::from("Hello world!")); + message.bodies.insert(String::from(""), Body::from_str("Hello world!").unwrap()); let elem2 = message.into(); assert_eq!(elem, elem2); } @@ -288,7 +289,7 @@ mod tests { let elem: Element = "Hello world!".parse().unwrap(); let elem1 = elem.clone(); let message = Message::try_from(elem).unwrap(); - assert_eq!(message.subjects[""], "Hello world!"); + assert_eq!(message.subjects[""], Subject::from_str("Hello world!").unwrap()); let elem2 = message.into(); assert_eq!(elem1, elem2); From a04c39512202a83422b64e279cfda31bb3ad3bd2 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 06:22:35 +0100 Subject: [PATCH 315/698] roster: Document most structs and their fields. --- src/roster.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/roster.rs b/src/roster.rs index a1a07eb10d9da0ec19a89c1052ab7834c1f2ac94..82488a344837c2410df71ddad086f3962c6e1f70 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -23,11 +23,19 @@ generate_attribute!(Subscription, "subscription", { Remove => "remove", }); +/// Contact from the user’s contact list. #[derive(Debug, Clone, PartialEq)] pub struct Item { + /// JID of this contact. pub jid: Jid, + + /// Name of this contact. pub name: Option, + + /// Subscription status of this contact. pub subscription: Option, + + /// Groups this contact is part of. pub groups: Vec, } @@ -74,9 +82,17 @@ impl From for Element { } } +/// The contact list of the user. #[derive(Debug, Clone)] pub struct Roster { + /// Version of the contact list. + /// + /// This is an opaque string that should only be sent back to the server on + /// a new connection, if this client is storing the contact list between + /// connections. pub ver: Option, + + /// List of the contacts of the user. pub items: Vec, } From d55fa8e5ddc3fb228f37676ea7e0ce8afb2452a4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 06:26:50 +0100 Subject: [PATCH 316/698] =?UTF-8?q?chatstates:=20Prevent=20compilation=20i?= =?UTF-8?q?f=20the=20module=20isn=E2=80=99t=20properly=20documented.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chatstates.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/chatstates.rs b/src/chatstates.rs index 4093edbd29c805a39dbe65731dbf365ff3341413..2cb657f6fcc2c3f885eb18516e3afd33ef9ef11e 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#![deny(missing_docs)] + use try_from::TryFrom; use minidom::Element; From 99b9525e6fde72f0e392e28370ea13663d7b92d6 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 06:28:20 +0100 Subject: [PATCH 317/698] Update to jid-rs 0.2.3, which implements IntoAttributeValue on Jid. --- Cargo.toml | 5 ++++- src/delay.rs | 2 +- src/disco.rs | 2 +- src/iq.rs | 4 ++-- src/jingle.rs | 4 ++-- src/message.rs | 4 ++-- src/muc/user.rs | 7 ++----- src/presence.rs | 4 ++-- src/pubsub/event.rs | 4 ++-- src/roster.rs | 2 +- src/stanza_error.rs | 2 +- src/stanza_id.rs | 2 +- 12 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c5ff5e0977e3a3026cafca7507ae8d4ac2486c91..c1f48c005b2e9e29c3e14e689ef380e0d52f4dd2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,6 @@ license = "MPL-2.0" [dependencies] minidom = "0.4.4" -jid = "0.2.0" base64 = "0.6.0" digest = "0.6.0" sha-1 = "0.4.0" @@ -23,3 +22,7 @@ sha3 = "0.6.0" blake2 = "0.6.1" chrono = "0.4.0" try_from = "0.2.2" + +[dependencies.jid] +version = "0.2.3" +features = ["minidom"] diff --git a/src/delay.rs b/src/delay.rs index e5123ec2d208493227b3c00888ab39bf914da7e7..97c10ad76423132acb7d86782cccb0e4bd02f819 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -49,7 +49,7 @@ impl From for Element { fn from(delay: Delay) -> Element { Element::builder("delay") .ns(ns::DELAY) - .attr("from", delay.from.map(String::from)) + .attr("from", delay.from) .attr("stamp", delay.stamp.to_rfc3339()) .append(delay.data) .build() diff --git a/src/disco.rs b/src/disco.rs index b976e362d2c13c2993b3367b1707ab0e7266fbe5..b1d21680691242ab22fe6d05e4c2738510debcf0 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -307,7 +307,7 @@ impl From for Element { fn from(item: Item) -> Element { Element::builder("item") .ns(ns::DISCO_ITEMS) - .attr("jid", String::from(item.jid)) + .attr("jid", item.jid) .attr("node", item.node) .attr("name", item.name) .build() diff --git a/src/iq.rs b/src/iq.rs index 2584ebeb66f1c081cce8e3f2aa9bdded33bb8a08..1fd308c0174ca509059c427d58a39c09654a719f 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -276,8 +276,8 @@ impl From for Element { fn from(iq: Iq) -> Element { let mut stanza = Element::builder("iq") .ns(ns::JABBER_CLIENT) - .attr("from", iq.from.map(String::from)) - .attr("to", iq.to.map(String::from)) + .attr("from", iq.from) + .attr("to", iq.to) .attr("id", iq.id) .attr("type", &iq.payload) .build(); diff --git a/src/jingle.rs b/src/jingle.rs index 364ee4b82d6a57579a75e236a98b4ef14dc9347e..d36d0862b951794c5da49e39bc6bf649422e2fa2 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -298,8 +298,8 @@ impl From for Element { Element::builder("jingle") .ns(ns::JINGLE) .attr("action", jingle.action) - .attr("initiator", match jingle.initiator { Some(initiator) => Some(String::from(initiator)), None => None }) - .attr("responder", match jingle.responder { Some(responder) => Some(String::from(responder)), None => None }) + .attr("initiator", jingle.initiator) + .attr("responder", jingle.responder) .attr("sid", jingle.sid) .append(jingle.contents) .append(jingle.reason) diff --git a/src/message.rs b/src/message.rs index 80a0e0445fd4fbc2a440b4f38364328c38401c69..3bf73ae89436d41bbd6774f4937a28b62e504edf 100644 --- a/src/message.rs +++ b/src/message.rs @@ -211,8 +211,8 @@ impl From for Element { fn from(message: Message) -> Element { Element::builder("message") .ns(ns::JABBER_CLIENT) - .attr("from", message.from.map(String::from)) - .attr("to", message.to.map(String::from)) + .attr("from", message.from) + .attr("to", message.to) .attr("id", message.id) .attr("type", message.type_) .append(message.subjects.into_iter() diff --git a/src/muc/user.rs b/src/muc/user.rs index afc9e72187705a9117b436ad45c252587c47e505..9e141ad2defe1fac3aeb41e91861477f41e22044 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -186,7 +186,7 @@ impl From for Element { let elem = Element::builder("actor").ns(ns::MUC_USER); (match actor { - Actor::Jid(jid) => elem.attr("jid", String::from(jid)), + Actor::Jid(jid) => elem.attr("jid", jid), Actor::Nick(nick) => elem.attr("nick", nick), }).build() } @@ -330,10 +330,7 @@ impl From for Element { Element::builder("item") .ns(ns::MUC_USER) .attr("affiliation", item.affiliation) - .attr("jid", match item.jid { - Some(jid) => Some(String::from(jid)), - None => None, - }) + .attr("jid", item.jid) .attr("nick", item.nick) .attr("role", item.role) .append(item.actor) diff --git a/src/presence.rs b/src/presence.rs index 93c5cf941b246dcf775f683743fc0a8636a59f1e..958a82b3f917e8c558ba49a70f4a06b1b18ed349 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -317,8 +317,8 @@ impl From for Element { fn from(presence: Presence) -> Element { Element::builder("presence") .ns(ns::JABBER_CLIENT) - .attr("from", presence.from.map(String::from)) - .attr("to", presence.to.map(String::from)) + .attr("from", presence.from) + .attr("to", presence.to) .attr("id", presence.id) .attr("type", presence.type_) .append(presence.show) diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index a6fa4550a6775751a465a13a855e82235770508f..108d0ab1321cb1067b7f89374f48d8f18c4e0e3a 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -31,7 +31,7 @@ impl From for Element { .ns(ns::PUBSUB_EVENT) .attr("id", item.id) .attr("node", item.node) - .attr("publisher", item.publisher.map(String::from)) + .attr("publisher", item.publisher) .append(item.payload) .build() } @@ -255,7 +255,7 @@ impl From for Element { .ns(ns::PUBSUB_EVENT) .attr("node", node) .attr("expiry", expiry.map(|expiry| expiry.to_rfc3339())) - .attr("jid", jid.map(String::from)) + .attr("jid", jid) .attr("subid", subid) .attr("subscription", subscription) .build() diff --git a/src/roster.rs b/src/roster.rs index 82488a344837c2410df71ddad086f3962c6e1f70..9e2bba31db54f240188b913581e81227e2d5c8be 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -74,7 +74,7 @@ impl From for Element { fn from(item: Item) -> Element { Element::builder("item") .ns(ns::ROSTER) - .attr("jid", String::from(item.jid)) + .attr("jid", item.jid) .attr("name", item.name) .attr("subscription", item.subscription) .append(item.groups) diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 3b7273b3fa8db3d7498de02f779bb8c1b28be6da..0c7ca7e84e77a561e5bd2ce79f9291b739d4bd20 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -177,7 +177,7 @@ impl From for Element { let mut root = Element::builder("error") .ns(ns::JABBER_CLIENT) .attr("type", err.type_) - .attr("by", err.by.map(String::from)) + .attr("by", err.by) .append(err.defined_condition) .build(); for (lang, text) in err.texts { diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 43ce96452983f6ccaeee0fe68a952bfbcc75aba8..feeeadbe0328d00411e776d557227ed9dc7439c4 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -41,7 +41,7 @@ impl From for Element { Element::builder("stanza-id") .ns(ns::SID) .attr("id", stanza_id.id) - .attr("by", String::from(stanza_id.by)) + .attr("by", stanza_id.by) .build() } } From 69cc83c4562f92fe9e8e8637c374005e58a48380 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 06:49:02 +0100 Subject: [PATCH 318/698] message, iq, presence, stanza_error, forwarded: Add support for components hidden behind the component feature flag. --- Cargo.toml | 4 +++ src/forwarding.rs | 4 +-- src/iq.rs | 48 +++++++++++++++++++++++++++++++---- src/mam.rs | 12 +++++++++ src/message.rs | 36 +++++++++++++++++++------- src/ns.rs | 8 ++++++ src/presence.rs | 61 +++++++++++++++++++++++++++++++++++++-------- src/stanza_error.rs | 16 ++++++++++-- 8 files changed, 161 insertions(+), 28 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c1f48c005b2e9e29c3e14e689ef380e0d52f4dd2..a701185456fc1d667cd5a161b3563cf5fd609217 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,3 +26,7 @@ try_from = "0.2.2" [dependencies.jid] version = "0.2.3" features = ["minidom"] + +[features] +# Build xmpp-parsers to make components instead of clients. +component = [] diff --git a/src/forwarding.rs b/src/forwarding.rs index 86474dd1b6cd0456887ca2648cdb6a4fc7c68e6c..2348068b996e90ecc0f8fbc2902a6d508452ae19 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -34,9 +34,9 @@ impl TryFrom for Forwarded { for child in elem.children() { if child.is("delay", ns::DELAY) { delay = Some(Delay::try_from(child.clone())?); - } else if child.is("message", ns::JABBER_CLIENT) { + } else if child.is("message", ns::DEFAULT_NS) { stanza = Some(Message::try_from(child.clone())?); - // TODO: also handle the five other possibilities. + // TODO: also handle the two other possibilities. } else { return Err(Error::ParseError("Unknown child in forwarded element.")); } diff --git a/src/iq.rs b/src/iq.rs index 1fd308c0174ca509059c427d58a39c09654a719f..7e919985b0bdb6fb27978f008b1d37285af0b0fe 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -207,7 +207,7 @@ impl TryFrom for Iq { type Err = Error; fn try_from(root: Element) -> Result { - if !root.is("iq", ns::JABBER_CLIENT) { + if !root.is("iq", ns::DEFAULT_NS) { return Err(Error::ParseError("This is not an iq element.")); } let from = get_attr!(root, "from", optional); @@ -222,7 +222,7 @@ impl TryFrom for Iq { return Err(Error::ParseError("Wrong number of children in iq element.")); } if type_ == "error" { - if elem.is("error", ns::JABBER_CLIENT) { + if elem.is("error", ns::DEFAULT_NS) { if error_payload.is_some() { return Err(Error::ParseError("Wrong number of children in iq element.")); } @@ -275,7 +275,7 @@ impl TryFrom for Iq { impl From for Element { fn from(iq: Iq) -> Element { let mut stanza = Element::builder("iq") - .ns(ns::JABBER_CLIENT) + .ns(ns::DEFAULT_NS) .attr("from", iq.from) .attr("to", iq.to) .attr("id", iq.id) @@ -300,7 +300,10 @@ mod tests { #[test] fn test_require_type() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let error = Iq::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -311,11 +314,16 @@ mod tests { #[test] fn test_get() { + #[cfg(not(feature = "component"))] let elem: Element = " - + + ".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = " + ".parse().unwrap(); let iq = Iq::try_from(elem).unwrap(); - let query: Element = "".parse().unwrap(); + let query: Element = "".parse().unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); assert_eq!(iq.id, None); @@ -327,9 +335,14 @@ mod tests { #[test] fn test_set() { + #[cfg(not(feature = "component"))] let elem: Element = " ".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = " + + ".parse().unwrap(); let iq = Iq::try_from(elem).unwrap(); let vcard: Element = "".parse().unwrap(); assert_eq!(iq.from, None); @@ -343,7 +356,10 @@ mod tests { #[test] fn test_result_empty() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let iq = Iq::try_from(elem).unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); @@ -356,9 +372,14 @@ mod tests { #[test] fn test_result() { + #[cfg(not(feature = "component"))] let elem: Element = " ".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = " + + ".parse().unwrap(); let iq = Iq::try_from(elem).unwrap(); let query: Element = "".parse().unwrap(); assert_eq!(iq.from, None); @@ -372,12 +393,20 @@ mod tests { #[test] fn test_error() { + #[cfg(not(feature = "component"))] let elem: Element = " ".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = " + + + + + ".parse().unwrap(); let iq = Iq::try_from(elem).unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); @@ -396,7 +425,10 @@ mod tests { #[test] fn test_children_invalid() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let error = Iq::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -407,7 +439,10 @@ mod tests { #[test] fn test_serialise() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let iq2 = Iq { from: None, to: None, @@ -420,7 +455,10 @@ mod tests { #[test] fn test_disco() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let iq = Iq::try_from(elem).unwrap(); let payload = match iq.payload { IqType::Get(payload) => IqGetPayload::try_from(payload).unwrap(), diff --git a/src/mam.rs b/src/mam.rs index b52e80413b69a5028fc03674b95f5fe4b8f96570..65299b700be715c8efe2d0ee7685fff300fd759c 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -242,6 +242,7 @@ mod tests { #[test] fn test_result() { + #[cfg(not(feature = "component"))] let elem: Element = r#" @@ -251,6 +252,17 @@ mod tests { +"#.parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = r#" + + + + + Hail to thee + + + "#.parse().unwrap(); Result_::try_from(elem).unwrap(); } diff --git a/src/message.rs b/src/message.rs index 3bf73ae89436d41bbd6774f4937a28b62e504edf..5dba839eacd095851d031a7e898b0e2629c4dcbb 100644 --- a/src/message.rs +++ b/src/message.rs @@ -49,7 +49,7 @@ impl TryFrom for MessagePayload { fn try_from(elem: Element) -> Result { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { - ("error", ns::JABBER_CLIENT) => MessagePayload::StanzaError(StanzaError::try_from(elem)?), + ("error", ns::DEFAULT_NS) => MessagePayload::StanzaError(StanzaError::try_from(elem)?), // XEP-0085 ("active", ns::CHATSTATES) @@ -116,9 +116,9 @@ generate_attribute!(MessageType, "type", { type Lang = String; -generate_elem_id!(Body, "body", ns::JABBER_CLIENT); -generate_elem_id!(Subject, "subject", ns::JABBER_CLIENT); -generate_elem_id!(Thread, "thread", ns::JABBER_CLIENT); +generate_elem_id!(Body, "body", ns::DEFAULT_NS); +generate_elem_id!(Subject, "subject", ns::DEFAULT_NS); +generate_elem_id!(Thread, "thread", ns::DEFAULT_NS); /// The main structure representing the `` stanza. #[derive(Debug, Clone)] @@ -152,7 +152,7 @@ impl TryFrom for Message { type Err = Error; fn try_from(root: Element) -> Result { - if !root.is("message", ns::JABBER_CLIENT) { + if !root.is("message", ns::DEFAULT_NS) { return Err(Error::ParseError("This is not a message element.")); } let from = get_attr!(root, "from", optional); @@ -164,7 +164,7 @@ impl TryFrom for Message { let mut thread = None; let mut payloads = vec!(); for elem in root.children() { - if elem.is("body", ns::JABBER_CLIENT) { + if elem.is("body", ns::DEFAULT_NS) { for _ in elem.children() { return Err(Error::ParseError("Unknown child in body element.")); } @@ -173,7 +173,7 @@ impl TryFrom for Message { if bodies.insert(lang, body).is_some() { return Err(Error::ParseError("Body element present twice for the same xml:lang.")); } - } else if elem.is("subject", ns::JABBER_CLIENT) { + } else if elem.is("subject", ns::DEFAULT_NS) { for _ in elem.children() { return Err(Error::ParseError("Unknown child in subject element.")); } @@ -182,7 +182,7 @@ impl TryFrom for Message { if subjects.insert(lang, subject).is_some() { return Err(Error::ParseError("Subject element present twice for the same xml:lang.")); } - } else if elem.is("thread", ns::JABBER_CLIENT) { + } else if elem.is("thread", ns::DEFAULT_NS) { if thread.is_some() { return Err(Error::ParseError("Thread element present twice.")); } @@ -210,7 +210,7 @@ impl TryFrom for Message { impl From for Element { fn from(message: Message) -> Element { Element::builder("message") - .ns(ns::JABBER_CLIENT) + .ns(ns::DEFAULT_NS) .attr("from", message.from) .attr("to", message.to) .attr("id", message.id) @@ -246,7 +246,10 @@ mod tests { #[test] fn test_simple() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let message = Message::try_from(elem).unwrap(); assert_eq!(message.from, None); assert_eq!(message.to, None); @@ -257,7 +260,10 @@ mod tests { #[test] fn test_serialise() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let mut message = Message::new(None); message.type_ = MessageType::Normal; let elem2 = message.into(); @@ -266,7 +272,10 @@ mod tests { #[test] fn test_body() { + #[cfg(not(feature = "component"))] let elem: Element = "Hello world!".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "Hello world!".parse().unwrap(); let elem1 = elem.clone(); let message = Message::try_from(elem).unwrap(); assert_eq!(message.bodies[""], Body::from_str("Hello world!").unwrap()); @@ -277,7 +286,10 @@ mod tests { #[test] fn test_serialise_body() { + #[cfg(not(feature = "component"))] let elem: Element = "Hello world!".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "Hello world!".parse().unwrap(); let mut message = Message::new(Some(Jid::from_str("coucou@example.org").unwrap())); message.bodies.insert(String::from(""), Body::from_str("Hello world!").unwrap()); let elem2 = message.into(); @@ -286,7 +298,10 @@ mod tests { #[test] fn test_subject() { + #[cfg(not(feature = "component"))] let elem: Element = "Hello world!".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "Hello world!".parse().unwrap(); let elem1 = elem.clone(); let message = Message::try_from(elem).unwrap(); assert_eq!(message.subjects[""], Subject::from_str("Hello world!").unwrap()); @@ -297,7 +312,10 @@ mod tests { #[test] fn test_attention() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let elem1 = elem.clone(); let message = Message::try_from(elem).unwrap(); let elem2 = message.into(); diff --git a/src/ns.rs b/src/ns.rs index 0d06a1d7ae483136a705ecaa1fa96cf72f9cc8bd..5df077e1d5ccbc50f4d51c38a41ea8c7c45f5cb6 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -47,6 +47,9 @@ pub const REGISTER: &str = "jabber:iq:register"; /// XEP-0085: Chat State Notifications pub const CHATSTATES: &str = "http://jabber.org/protocol/chatstates"; +/// XEP-0114: Jabber Component Protocol +pub const COMPONENT_ACCEPT: &str = "jabber:component:accept"; + /// XEP-0115: Entity Capabilities pub const CAPS: &str = "http://jabber.org/protocol/caps"; @@ -119,3 +122,8 @@ pub const EME: &str = "urn:xmpp:eme:0"; pub const ECAPS2: &str = "urn:xmpp:caps"; /// XEP-0390: Entity Capabilities 2.0 pub const ECAPS2_OPTIMIZE: &str = "urn:xmpp:caps:optimize"; + +#[cfg(not(feature = "component"))] +pub const DEFAULT_NS: &str = JABBER_CLIENT; +#[cfg(feature = "component")] +pub const DEFAULT_NS: &str = COMPONENT_ACCEPT; diff --git a/src/presence.rs b/src/presence.rs index 958a82b3f917e8c558ba49a70f4a06b1b18ed349..a1f11fa690319437c1af43cbcc54385d3ddddf1c 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -95,7 +95,7 @@ impl TryFrom for PresencePayload { fn try_from(elem: Element) -> Result { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { - ("error", ns::JABBER_CLIENT) => PresencePayload::StanzaError(StanzaError::try_from(elem)?), + ("error", ns::DEFAULT_NS) => PresencePayload::StanzaError(StanzaError::try_from(elem)?), // XEP-0045 ("x", ns::MUC) => PresencePayload::Muc(Muc::try_from(elem)?), @@ -248,7 +248,7 @@ impl TryFrom for Presence { type Err = Error; fn try_from(root: Element) -> Result { - if !root.is("presence", ns::JABBER_CLIENT) { + if !root.is("presence", ns::DEFAULT_NS) { return Err(Error::ParseError("This is not a presence element.")); } let mut show = None; @@ -264,7 +264,7 @@ impl TryFrom for Presence { payloads: vec!(), }; for elem in root.children() { - if elem.is("show", ns::JABBER_CLIENT) { + if elem.is("show", ns::DEFAULT_NS) { if show.is_some() { return Err(Error::ParseError("More than one show element in a presence.")); } @@ -275,7 +275,7 @@ impl TryFrom for Presence { return Err(Error::ParseError("Unknown attribute in show element.")); } show = Some(Show::from_str(elem.text().as_ref())?); - } else if elem.is("status", ns::JABBER_CLIENT) { + } else if elem.is("status", ns::DEFAULT_NS) { for _ in elem.children() { return Err(Error::ParseError("Unknown child in status element.")); } @@ -288,7 +288,7 @@ impl TryFrom for Presence { if presence.statuses.insert(lang, elem.text()).is_some() { return Err(Error::ParseError("Status element present twice for the same xml:lang.")); } - } else if elem.is("priority", ns::JABBER_CLIENT) { + } else if elem.is("priority", ns::DEFAULT_NS) { if priority.is_some() { return Err(Error::ParseError("More than one priority element in a presence.")); } @@ -316,7 +316,7 @@ impl TryFrom for Presence { impl From for Element { fn from(presence: Presence) -> Element { Element::builder("presence") - .ns(ns::JABBER_CLIENT) + .ns(ns::DEFAULT_NS) .attr("from", presence.from) .attr("to", presence.to) .attr("id", presence.id) @@ -343,7 +343,10 @@ mod tests { #[test] fn test_simple() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.from, None); assert_eq!(presence.to, None); @@ -354,7 +357,10 @@ mod tests { #[test] fn test_serialise() { - let elem: Element = "".parse().unwrap(); + #[cfg(not(feature = "component"))] + let elem: Element = "/>".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "/>".parse().unwrap(); let presence = Presence::new(Type::Unavailable); let elem2 = presence.into(); assert_eq!(elem, elem2); @@ -362,7 +368,10 @@ mod tests { #[test] fn test_show() { + #[cfg(not(feature = "component"))] let elem: Element = "chat".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "chat".parse().unwrap(); let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.show, Show::Chat); @@ -370,8 +379,10 @@ mod tests { #[test] fn test_missing_show_value() { - // "online" used to be a pretty common mistake. + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let error = Presence::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -383,7 +394,10 @@ mod tests { #[test] fn test_invalid_show() { // "online" used to be a pretty common mistake. + #[cfg(not(feature = "component"))] let elem: Element = "online".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "online".parse().unwrap(); let error = Presence::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -394,7 +408,10 @@ mod tests { #[test] fn test_empty_status() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.statuses.len(), 1); @@ -403,7 +420,10 @@ mod tests { #[test] fn test_status() { + #[cfg(not(feature = "component"))] let elem: Element = "Here!".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "Here!".parse().unwrap(); let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.statuses.len(), 1); @@ -412,7 +432,10 @@ mod tests { #[test] fn test_multiple_statuses() { + #[cfg(not(feature = "component"))] let elem: Element = "Here!Là!".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "Here!Là!".parse().unwrap(); let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.statuses.len(), 2); @@ -422,7 +445,10 @@ mod tests { #[test] fn test_invalid_multiple_statuses() { + #[cfg(not(feature = "component"))] let elem: Element = "Here!Là!".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "Here!Là!".parse().unwrap(); let error = Presence::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -433,7 +459,10 @@ mod tests { #[test] fn test_priority() { + #[cfg(not(feature = "component"))] let elem: Element = "-1".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "-1".parse().unwrap(); let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.priority, -1i8); @@ -441,7 +470,10 @@ mod tests { #[test] fn test_invalid_priority() { + #[cfg(not(feature = "component"))] let elem: Element = "128".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "128".parse().unwrap(); let error = Presence::try_from(elem).unwrap_err(); match error { Error::ParseIntError(_) => (), @@ -451,7 +483,10 @@ mod tests { #[test] fn test_unknown_child() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let presence = Presence::try_from(elem).unwrap(); let payload = &presence.payloads[0]; assert!(payload.is("test", "invalid")); @@ -459,7 +494,10 @@ mod tests { #[test] fn test_invalid_status_child() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let error = Presence::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -470,7 +508,10 @@ mod tests { #[test] fn test_invalid_attribute() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let error = Presence::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -485,7 +526,7 @@ mod tests { let mut presence = Presence::new(Type::Unavailable); presence.statuses.insert(String::from(""), status); let elem: Element = presence.into(); - assert!(elem.is("presence", ns::JABBER_CLIENT)); - assert!(elem.children().next().unwrap().is("status", ns::JABBER_CLIENT)); + assert!(elem.is("presence", ns::DEFAULT_NS)); + assert!(elem.children().next().unwrap().is("status", ns::DEFAULT_NS)); } } diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 0c7ca7e84e77a561e5bd2ce79f9291b739d4bd20..8ca9c86289aead4f6ec8d756cb26c9bafcf83784 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -125,7 +125,7 @@ impl TryFrom for StanzaError { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("error", ns::JABBER_CLIENT) { + if !elem.is("error", ns::DEFAULT_NS) { return Err(Error::ParseError("This is not an error element.")); } @@ -175,7 +175,7 @@ impl TryFrom for StanzaError { impl From for Element { fn from(err: StanzaError) -> Element { let mut root = Element::builder("error") - .ns(ns::JABBER_CLIENT) + .ns(ns::DEFAULT_NS) .attr("type", err.type_) .attr("by", err.by) .append(err.defined_condition) @@ -201,7 +201,10 @@ mod tests { #[test] fn test_simple() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let error = StanzaError::try_from(elem).unwrap(); assert_eq!(error.type_, ErrorType::Cancel); assert_eq!(error.defined_condition, DefinedCondition::UndefinedCondition); @@ -209,7 +212,10 @@ mod tests { #[test] fn test_invalid_type() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let error = StanzaError::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -217,7 +223,10 @@ mod tests { }; assert_eq!(message, "Required attribute 'type' missing."); + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let error = StanzaError::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -228,7 +237,10 @@ mod tests { #[test] fn test_invalid_condition() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let error = StanzaError::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, From 1af06fdf6d90c29d2bc4997efc3e0fa92779c963 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 11:45:45 +0100 Subject: [PATCH 319/698] Add an iq version parser (XEP-0092). --- src/lib.rs | 3 ++ src/ns.rs | 3 ++ src/version.rs | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 src/version.rs diff --git a/src/lib.rs b/src/lib.rs index f88108238fd885afeb7dcea4212fe08c4079edb8..cc9a8c31aa76313664a271fc907b3755f7c78bae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -207,6 +207,9 @@ pub mod ibr; /// XEP-0085: Chat State Notifications pub mod chatstates; +/// XEP-0092: Software Version +pub mod version; + /// XEP-0115: Entity Capabilities pub mod caps; diff --git a/src/ns.rs b/src/ns.rs index 5df077e1d5ccbc50f4d51c38a41ea8c7c45f5cb6..22e5f1566d4a3c06c325aa9f77e6b7ec7a11e1d0 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -47,6 +47,9 @@ pub const REGISTER: &str = "jabber:iq:register"; /// XEP-0085: Chat State Notifications pub const CHATSTATES: &str = "http://jabber.org/protocol/chatstates"; +/// XEP-0092: Software Version +pub const VERSION: &str = "jabber:iq:version"; + /// XEP-0114: Jabber Component Protocol pub const COMPONENT_ACCEPT: &str = "jabber:component:accept"; diff --git a/src/version.rs b/src/version.rs new file mode 100644 index 0000000000000000000000000000000000000000..5e7f0abf829d89e4a5b7ed0351657950a0a5aea8 --- /dev/null +++ b/src/version.rs @@ -0,0 +1,94 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use try_from::TryFrom; +use minidom::Element; +use error::Error; +use ns; + +#[derive(Debug, Clone)] +pub struct Version { + pub name: String, + pub version: String, + pub os: Option, +} + +impl TryFrom for Version { + type Err = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("query", ns::VERSION) { + return Err(Error::ParseError("This is not a version element.")); + } + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown child in version element.")); + } + let mut name = None; + let mut version = None; + let mut os = None; + for child in elem.children() { + if child.is("name", ns::VERSION) { + if name.is_some() { + return Err(Error::ParseError("More than one name in version element.")); + } + name = Some(child.text()); + } else if child.is("version", ns::VERSION) { + if version.is_some() { + return Err(Error::ParseError("More than one version in version element.")); + } + version = Some(child.text()); + } else if child.is("os", ns::VERSION) { + if os.is_some() { + return Err(Error::ParseError("More than one os in version element.")); + } + os = Some(child.text()); + } else { + return Err(Error::ParseError("Unknown child in version element.")); + } + } + let name = name.unwrap(); + let version = version.unwrap(); + Ok(Version { + name, + version, + os, + }) + } +} + +impl From for Element { + fn from(version: Version) -> Element { + Element::builder("query") + .ns(ns::VERSION) + .append(Element::builder("name") + .ns(ns::VERSION) + .append(version.name) + .build()) + .append(Element::builder("version") + .ns(ns::VERSION) + .append(version.version) + .build()) + .append(Element::builder("os") + .ns(ns::VERSION) + .append(version.os) + .build()) + .build() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + let elem: Element = "xmpp-rs0.3.0".parse().unwrap(); + let version = Version::try_from(elem).unwrap(); + assert_eq!(version.name, String::from("xmpp-rs")); + assert_eq!(version.version, String::from("0.3.0")); + assert_eq!(version.os, None); + } +} From 04ec34bed8dcb040967bbe246675ea3b04904f46 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Aug 2017 01:48:28 +0100 Subject: [PATCH 320/698] Cargo.toml: Update minidom to 0.6.2 and jid to 0.3.0. --- Cargo.toml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a701185456fc1d667cd5a161b3563cf5fd609217..2318f0f2f0241651d03912c4a95df44d6a481629 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,8 @@ categories = ["parsing", "network-programming"] license = "MPL-2.0" [dependencies] -minidom = "0.4.4" +minidom = "0.6.2" +jid = { version = "0.3.0", features = ["minidom"] } base64 = "0.6.0" digest = "0.6.0" sha-1 = "0.4.0" @@ -23,10 +24,6 @@ blake2 = "0.6.1" chrono = "0.4.0" try_from = "0.2.2" -[dependencies.jid] -version = "0.2.3" -features = ["minidom"] - [features] # Build xmpp-parsers to make components instead of clients. component = [] From 1b1661fd827fa60503ff13130c7f323e85215b11 Mon Sep 17 00:00:00 2001 From: Astro Date: Sat, 19 Aug 2017 00:04:18 +0100 Subject: [PATCH 321/698] Introduce comparing with namespace support. --- src/chatstates.rs | 3 +- src/compare_elements.rs | 118 ++++++++++++++++++++++++++++++++++++++++ src/disco.rs | 3 +- src/ibr.rs | 5 +- src/iq.rs | 7 ++- src/jingle.rs | 3 +- src/jingle_message.rs | 3 +- src/jingle_s5b.rs | 5 +- src/lib.rs | 4 ++ src/message.rs | 7 ++- src/muc/user.rs | 3 +- src/presence.rs | 3 +- src/pubsub/event.rs | 3 +- src/roster.rs | 3 +- src/rsm.rs | 3 +- src/stanza_error.rs | 3 +- 16 files changed, 156 insertions(+), 20 deletions(-) create mode 100644 src/compare_elements.rs diff --git a/src/chatstates.rs b/src/chatstates.rs index 2cb657f6fcc2c3f885eb18516e3afd33ef9ef11e..9a63a28d23fbcf4fab8e150c46212f1dd91a1f7d 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -38,7 +38,8 @@ impl TryFrom for ChatState { type Err = Error; fn try_from(elem: Element) -> Result { - if elem.ns() != Some(ns::CHATSTATES) { + let ns = elem.ns(); + if ns.as_ref().map(|ns| ns.as_str()) != Some(ns::CHATSTATES) { return Err(Error::ParseError("This is not a chatstate element.")); } for _ in elem.children() { diff --git a/src/compare_elements.rs b/src/compare_elements.rs new file mode 100644 index 0000000000000000000000000000000000000000..d133c85c2c51698b518893458c1cada19c0007a7 --- /dev/null +++ b/src/compare_elements.rs @@ -0,0 +1,118 @@ +use minidom::{Node, Element}; + +pub trait NamespaceAwareCompare { + /// Namespace-aware comparison for tests + fn compare_to(&self, other: &Self) -> bool; +} + +impl NamespaceAwareCompare for Node { + fn compare_to(&self, other: &Self) -> bool { + match (self, other) { + (&Node::Element(ref elem1), &Node::Element(ref elem2)) => + Element::compare_to(elem1, elem2), + (&Node::Text(ref text1), &Node::Text(ref text2)) => + text1 == text2, + _ => false, + } + } +} + +impl NamespaceAwareCompare for Element { + fn compare_to(&self, other: &Self) -> bool { + if self.name() == other.name() && + self.ns() == other.ns() && + self.attrs().eq(other.attrs()) + { + let child_elems = self.children().count(); + let text_is_whitespace = self.texts() + .all(|text| text.chars().all(char::is_whitespace)); + if child_elems > 0 && text_is_whitespace { + // Ignore all the whitespace text nodes + self.children().zip(other.children()) + .all(|(node1, node2)| node1.compare_to(node2)) + } else { + // Compare with text nodes + self.nodes().zip(other.nodes()) + .all(|(node1, node2)| node1.compare_to(node2)) + } + } else { + false + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use minidom::Element; + + #[test] + fn simple() { + let elem1: Element = "x 3".parse().unwrap(); + let elem2: Element = "x 3".parse().unwrap(); + assert!(elem1.compare_to(&elem2)); + } + + #[test] + fn wrong_attr_name() { + let elem1: Element = "x 3".parse().unwrap(); + let elem2: Element = "x 3".parse().unwrap(); + assert!(!elem1.compare_to(&elem2)); + } + + #[test] + fn wrong_attr_value() { + let elem1: Element = "x 3".parse().unwrap(); + let elem2: Element = "x 3".parse().unwrap(); + assert!(!elem1.compare_to(&elem2)); + } + + #[test] + fn attr_order() { + let elem1: Element = "".parse().unwrap(); + let elem2: Element = "".parse().unwrap(); + assert!(elem1.compare_to(&elem2)); + } + + #[test] + fn wrong_texts() { + let elem1: Element = "foo".parse().unwrap(); + let elem2: Element = "bar".parse().unwrap(); + assert!(!elem1.compare_to(&elem2)); + } + + #[test] + fn children() { + let elem1: Element = "".parse().unwrap(); + let elem2: Element = "".parse().unwrap(); + assert!(elem1.compare_to(&elem2)); + } + + #[test] + fn wrong_children() { + let elem1: Element = "".parse().unwrap(); + let elem2: Element = "".parse().unwrap(); + assert!(!elem1.compare_to(&elem2)); + } + + #[test] + fn xmlns_wrong() { + let elem1: Element = "".parse().unwrap(); + let elem2: Element = "".parse().unwrap(); + assert!(!elem1.compare_to(&elem2)); + } + + #[test] + fn xmlns_other_prefix() { + let elem1: Element = "".parse().unwrap(); + let elem2: Element = "".parse().unwrap(); + assert!(elem1.compare_to(&elem2)); + } + + #[test] + fn xmlns_dup() { + let elem1: Element = "".parse().unwrap(); + let elem2: Element = "".parse().unwrap(); + assert!(elem1.compare_to(&elem2)); + } +} diff --git a/src/disco.rs b/src/disco.rs index b1d21680691242ab22fe6d05e4c2738510debcf0..e67e7973a6b73d38a2c2e37afc2ed27bbd9f96b8 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -370,6 +370,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use compare_elements::NamespaceAwareCompare; use std::str::FromStr; #[test] @@ -394,7 +395,7 @@ mod tests { assert_eq!(query.extensions[0].form_type, Some(String::from("example"))); let elem2 = query.into(); - assert_eq!(elem1, elem2); + assert!(elem1.compare_to(&elem2)); } #[test] diff --git a/src/ibr.rs b/src/ibr.rs index c51153e65f2f72bfd503d8ff3bbed07cc501fc3d..05281f333255b2d52b6ecdaf63c5284d116bb238 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -81,6 +81,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use compare_elements::NamespaceAwareCompare; #[test] fn test_simple() { @@ -157,7 +158,7 @@ mod tests { assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("x-gender"))).is_ok()); assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("coucou"))).is_err()); let elem2 = query.into(); - assert_eq!(elem1, elem2); + assert!(elem1.compare_to(&elem2)); } #[test] @@ -190,6 +191,6 @@ mod tests { panic!(); } let elem2 = query.into(); - assert_eq!(elem1, elem2); + assert!(elem1.compare_to(&elem2)); } } diff --git a/src/iq.rs b/src/iq.rs index 7e919985b0bdb6fb27978f008b1d37285af0b0fe..65c13a2411a04894835b873d5199a1be5192e8f1 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -297,6 +297,7 @@ impl From for Element { mod tests { use super::*; use stanza_error::{ErrorType, DefinedCondition}; + use compare_elements::NamespaceAwareCompare; #[test] fn test_require_type() { @@ -328,7 +329,7 @@ mod tests { assert_eq!(iq.to, None); assert_eq!(iq.id, None); assert!(match iq.payload { - IqType::Get(element) => element == query, + IqType::Get(element) => element.compare_to(&query), _ => false }); } @@ -349,7 +350,7 @@ mod tests { assert_eq!(iq.to, None); assert_eq!(iq.id, None); assert!(match iq.payload { - IqType::Set(element) => element == vcard, + IqType::Set(element) => element.compare_to(&vcard), _ => false }); } @@ -386,7 +387,7 @@ mod tests { assert_eq!(iq.to, None); assert_eq!(iq.id, None); assert!(match iq.payload { - IqType::Result(Some(element)) => element == query, + IqType::Result(Some(element)) => element.compare_to(&query), _ => false, }); } diff --git a/src/jingle.rs b/src/jingle.rs index d36d0862b951794c5da49e39bc6bf649422e2fa2..d20ed056c5053f36b76b18a7e95dcf03cba47b37 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -208,7 +208,8 @@ impl TryFrom for ReasonElement { let mut reason = None; let mut text = None; for child in elem.children() { - if child.ns() != Some(ns::JINGLE) { + let child_ns = child.ns(); + if child_ns.as_ref().map(|ns| ns.as_str()) != Some(ns::JINGLE) { return Err(Error::ParseError("Reason contains a foreign element.")); } match child.name() { diff --git a/src/jingle_message.rs b/src/jingle_message.rs index 216e1877386fa185894a9bb0936c111b833693b9..57cebe8012e097b401d199441aa76c05ea6a729d 100644 --- a/src/jingle_message.rs +++ b/src/jingle_message.rs @@ -47,7 +47,8 @@ impl TryFrom for JingleMI { type Err = Error; fn try_from(elem: Element) -> Result { - if elem.ns() != Some(ns::JINGLE_MESSAGE) { + let ns = elem.ns(); + if ns.as_ref().map(|ns| ns.as_str()) != Some(ns::JINGLE_MESSAGE) { return Err(Error::ParseError("This is not a Jingle message element.")); } Ok(match elem.name() { diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index d454a3d99e060473c3348fe1c55736ec679f0904..6f8bb902106ebcdbdd1dedae5f00fda9c57d53da 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -181,6 +181,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use compare_elements::NamespaceAwareCompare; #[test] fn test_simple() { @@ -205,7 +206,7 @@ mod tests { payload: TransportPayload::Activated(String::from("coucou")), }; let elem2: Element = transport.into(); - assert_eq!(elem, elem2); + assert!(elem.compare_to(&elem2)); } #[test] @@ -225,6 +226,6 @@ mod tests { })), }; let elem2: Element = transport.into(); - assert_eq!(elem, elem2); + assert!(elem.compare_to(&elem2)); } } diff --git a/src/lib.rs b/src/lib.rs index cc9a8c31aa76313664a271fc907b3755f7c78bae..75b092fb56de96e1e07b674d9a9afc5781496a81 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -171,6 +171,10 @@ pub mod error; /// XML namespace definitions used through XMPP. pub mod ns; +#[cfg(test)] +/// Namespace-aware comparison for tests +mod compare_elements; + /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod message; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core diff --git a/src/message.rs b/src/message.rs index 5dba839eacd095851d031a7e898b0e2629c4dcbb..3a4795b15c59df2a482fe7671347b5e532ecca6f 100644 --- a/src/message.rs +++ b/src/message.rs @@ -243,6 +243,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use compare_elements::NamespaceAwareCompare; #[test] fn test_simple() { @@ -281,7 +282,7 @@ mod tests { assert_eq!(message.bodies[""], Body::from_str("Hello world!").unwrap()); let elem2 = message.into(); - assert_eq!(elem1, elem2); + assert!(elem1.compare_to(&elem2)); } #[test] @@ -293,7 +294,7 @@ mod tests { let mut message = Message::new(Some(Jid::from_str("coucou@example.org").unwrap())); message.bodies.insert(String::from(""), Body::from_str("Hello world!").unwrap()); let elem2 = message.into(); - assert_eq!(elem, elem2); + assert!(elem.compare_to(&elem2)); } #[test] @@ -307,7 +308,7 @@ mod tests { assert_eq!(message.subjects[""], Subject::from_str("Hello world!").unwrap()); let elem2 = message.into(); - assert_eq!(elem1, elem2); + assert!(elem1.compare_to(&elem2)); } #[test] diff --git a/src/muc/user.rs b/src/muc/user.rs index 9e141ad2defe1fac3aeb41e91861477f41e22044..322525cbe2a3cadbe0a292f23f401947d8c15b3f 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -387,6 +387,7 @@ impl From for Element { mod tests { use super::*; use std::error::Error as StdError; + use compare_elements::NamespaceAwareCompare; #[test] fn test_simple() { @@ -418,7 +419,7 @@ mod tests { ".parse().unwrap(); let muc = MucUser { status: vec!(), items: vec!() }; let elem2 = muc.into(); - assert_eq!(elem, elem2); + assert!(elem.compare_to(&elem2)); } #[test] diff --git a/src/presence.rs b/src/presence.rs index a1f11fa690319437c1af43cbcc54385d3ddddf1c..e48e68509dbc8c6f79673527b3fee31d9ecc01d1 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -340,6 +340,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use compare_elements::NamespaceAwareCompare; #[test] fn test_simple() { @@ -363,7 +364,7 @@ mod tests { let elem: Element = "/>".parse().unwrap(); let presence = Presence::new(Type::Unavailable); let elem2 = presence.into(); - assert_eq!(elem, elem2); + assert!(elem.compare_to(&elem2)); } #[test] diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 108d0ab1321cb1067b7f89374f48d8f18c4e0e3a..d055476d5b66ce4a034f1b8d6178bb6579915eda 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -272,6 +272,7 @@ impl From for Element { mod tests { use super::*; use std::str::FromStr; + use compare_elements::NamespaceAwareCompare; #[test] fn test_simple() { @@ -417,6 +418,6 @@ mod tests { } let elem2: Element = event.into(); - assert_eq!(elem, elem2); + assert!(elem.compare_to(&elem2)); } } diff --git a/src/roster.rs b/src/roster.rs index 9e2bba31db54f240188b913581e81227e2d5c8be..ae26c3d25971cf023c1930cf1e91e16535badb0e 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -137,6 +137,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use compare_elements::NamespaceAwareCompare; #[test] fn test_get() { @@ -206,7 +207,7 @@ mod tests { assert_eq!(roster.items[0].groups[0], Group::from_str("A").unwrap()); assert_eq!(roster.items[0].groups[1], Group::from_str("B").unwrap()); let elem2 = roster.into(); - assert_eq!(elem1, elem2); + assert!(elem1.compare_to(&elem2)); } #[test] diff --git a/src/rsm.rs b/src/rsm.rs index fa6f6c814fdbcedf798cb7588cc8da5d37706b33..97e7abc6182852a55f2ec36f2d732e3452d6e191 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -122,6 +122,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use compare_elements::NamespaceAwareCompare; #[test] fn test_simple() { @@ -197,6 +198,6 @@ mod tests { max: None, }; let elem2 = set2.into(); - assert_eq!(elem1, elem2); + assert!(elem1.compare_to(&elem2)); } } diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 8ca9c86289aead4f6ec8d756cb26c9bafcf83784..1ed542c14239bdf9d44eccffefd2109f303a7afc 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -136,6 +136,7 @@ impl TryFrom for StanzaError { let mut other = None; for child in elem.children() { + let child_ns = child.ns(); if child.is("text", ns::XMPP_STANZAS) { for _ in child.children() { return Err(Error::ParseError("Unknown element in error text.")); @@ -144,7 +145,7 @@ impl TryFrom for StanzaError { if texts.insert(lang, child.text()).is_some() { return Err(Error::ParseError("Text element present twice for the same xml:lang.")); } - } else if child.ns() == Some(ns::XMPP_STANZAS) { + } else if child_ns.as_ref().map(|ns| ns.as_str()) == Some(ns::XMPP_STANZAS) { if defined_condition.is_some() { return Err(Error::ParseError("Error must not have more than one defined-condition.")); } From c853a1ff4be2c1c82e80f09205d58f5570895c96 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Aug 2017 01:32:15 +0100 Subject: [PATCH 322/698] compare_elements: Add missing license notice. --- src/compare_elements.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/compare_elements.rs b/src/compare_elements.rs index d133c85c2c51698b518893458c1cada19c0007a7..85c81e75e8e5168870f9739a7f014886cbea1935 100644 --- a/src/compare_elements.rs +++ b/src/compare_elements.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Astro +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::{Node, Element}; pub trait NamespaceAwareCompare { From f69f567448af99c71cb76bf5e80828468740674a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Aug 2017 01:04:56 +0100 Subject: [PATCH 323/698] =?UTF-8?q?Use=20minidom=C2=A00.6.1=E2=80=99s=20El?= =?UTF-8?q?ement::has=5Fns(),=20to=20simplify=20namespace=20comparisons.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chatstates.rs | 3 +-- src/jingle.rs | 3 +-- src/jingle_message.rs | 3 +-- src/stanza_error.rs | 3 +-- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/chatstates.rs b/src/chatstates.rs index 9a63a28d23fbcf4fab8e150c46212f1dd91a1f7d..ecbecdffc4135dbfe7b50aa68aafbb57a9e5402e 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -38,8 +38,7 @@ impl TryFrom for ChatState { type Err = Error; fn try_from(elem: Element) -> Result { - let ns = elem.ns(); - if ns.as_ref().map(|ns| ns.as_str()) != Some(ns::CHATSTATES) { + if !elem.has_ns(ns::CHATSTATES) { return Err(Error::ParseError("This is not a chatstate element.")); } for _ in elem.children() { diff --git a/src/jingle.rs b/src/jingle.rs index d20ed056c5053f36b76b18a7e95dcf03cba47b37..9d142612e5b74a15606dd8d525556d4a706fe580 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -208,8 +208,7 @@ impl TryFrom for ReasonElement { let mut reason = None; let mut text = None; for child in elem.children() { - let child_ns = child.ns(); - if child_ns.as_ref().map(|ns| ns.as_str()) != Some(ns::JINGLE) { + if !child.has_ns(ns::JINGLE) { return Err(Error::ParseError("Reason contains a foreign element.")); } match child.name() { diff --git a/src/jingle_message.rs b/src/jingle_message.rs index 57cebe8012e097b401d199441aa76c05ea6a729d..779af992d0b43f06b7f7b5104301a95966421cdd 100644 --- a/src/jingle_message.rs +++ b/src/jingle_message.rs @@ -47,8 +47,7 @@ impl TryFrom for JingleMI { type Err = Error; fn try_from(elem: Element) -> Result { - let ns = elem.ns(); - if ns.as_ref().map(|ns| ns.as_str()) != Some(ns::JINGLE_MESSAGE) { + if !elem.has_ns(ns::JINGLE_MESSAGE) { return Err(Error::ParseError("This is not a Jingle message element.")); } Ok(match elem.name() { diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 1ed542c14239bdf9d44eccffefd2109f303a7afc..bbc31623bcf98827e438b2470255887de90c1096 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -136,7 +136,6 @@ impl TryFrom for StanzaError { let mut other = None; for child in elem.children() { - let child_ns = child.ns(); if child.is("text", ns::XMPP_STANZAS) { for _ in child.children() { return Err(Error::ParseError("Unknown element in error text.")); @@ -145,7 +144,7 @@ impl TryFrom for StanzaError { if texts.insert(lang, child.text()).is_some() { return Err(Error::ParseError("Text element present twice for the same xml:lang.")); } - } else if child_ns.as_ref().map(|ns| ns.as_str()) == Some(ns::XMPP_STANZAS) { + } else if child.has_ns(ns::XMPP_STANZAS) { if defined_condition.is_some() { return Err(Error::ParseError("Error must not have more than one defined-condition.")); } From 833ef068c62f4a20c039ffbc1c7c451b8a7756ad Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Aug 2017 01:21:07 +0100 Subject: [PATCH 324/698] message: Fix wrong element for xml:lang. --- src/message.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/message.rs b/src/message.rs index 3a4795b15c59df2a482fe7671347b5e532ecca6f..add6d1733adbe62b43e116851867364599c13744 100644 --- a/src/message.rs +++ b/src/message.rs @@ -168,7 +168,7 @@ impl TryFrom for Message { for _ in elem.children() { return Err(Error::ParseError("Unknown child in body element.")); } - let lang = get_attr!(root, "xml:lang", default); + let lang = get_attr!(elem, "xml:lang", default); let body = Body(elem.text()); if bodies.insert(lang, body).is_some() { return Err(Error::ParseError("Body element present twice for the same xml:lang.")); @@ -177,7 +177,7 @@ impl TryFrom for Message { for _ in elem.children() { return Err(Error::ParseError("Unknown child in subject element.")); } - let lang = get_attr!(root, "xml:lang", default); + let lang = get_attr!(elem, "xml:lang", default); let subject = Subject(elem.text()); if subjects.insert(lang, subject).is_some() { return Err(Error::ParseError("Subject element present twice for the same xml:lang.")); From f51fa15b68a1cdeb6dc027706f0cf26ccfe93419 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Aug 2017 01:22:15 +0100 Subject: [PATCH 325/698] jingle_ft: Add forgotten xml:lang support for . --- src/jingle_ft.rs | 70 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 57 insertions(+), 13 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 85ac2dfac7e8504d4d7aee11811836a9764fd228..1931aabd535f76dff19e2bd10029ffee11cc2b1b 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -6,9 +6,12 @@ use try_from::TryFrom; +use std::collections::BTreeMap; +use std::str::FromStr; + use hashes::Hash; -use minidom::{Element, IntoElements, ElementEmitter}; +use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; use chrono::{DateTime, FixedOffset}; use error::Error; @@ -35,12 +38,16 @@ impl IntoElements for Range { } } +type Lang = String; + +generate_id!(Desc); + #[derive(Debug, Clone)] pub struct File { pub date: Option>, pub media_type: Option, pub name: Option, - pub desc: Option, + pub descs: BTreeMap, pub size: Option, pub range: Option, pub hashes: Vec, @@ -98,7 +105,7 @@ impl TryFrom for Description { let mut date = None; let mut media_type = None; let mut name = None; - let mut desc = None; + let mut descs = BTreeMap::new(); let mut size = None; let mut range = None; let mut hashes = vec!(); @@ -123,10 +130,11 @@ impl TryFrom for Description { } name = Some(file_payload.text()); } else if file_payload.is("desc", ns::JINGLE_FT) { - if desc.is_some() { - return Err(Error::ParseError("File must not have more than one desc.")); + let lang = get_attr!(file_payload, "xml:lang", default); + let desc = Desc(file_payload.text()); + if descs.insert(lang, desc).is_some() { + return Err(Error::ParseError("Desc element present twice for the same xml:lang.")); } - desc = Some(file_payload.text()); } else if file_payload.is("size", ns::JINGLE_FT) { if size.is_some() { return Err(Error::ParseError("File must not have more than one size.")); @@ -163,7 +171,7 @@ impl TryFrom for Description { date: date, media_type: media_type, name: name, - desc: desc, + descs: descs, size: size, range: range, hashes: hashes, @@ -195,10 +203,11 @@ impl From for Element { .append(name) .build()); } - if let Some(desc) = file.desc { + for (lang, desc) in file.descs.into_iter() { root.append_child(Element::builder("desc") .ns(ns::JINGLE_FT) - .append(desc) + .attr("xml:lang", lang) + .append(desc.0) .build()); } if let Some(size) = file.size { @@ -250,11 +259,10 @@ mod tests { "#.parse().unwrap(); - let desc = Description::try_from(elem).unwrap(); assert_eq!(desc.file.media_type, Some(String::from("text/plain"))); assert_eq!(desc.file.name, Some(String::from("test.txt"))); - assert_eq!(desc.file.desc, None); + assert_eq!(desc.file.descs, BTreeMap::new()); assert_eq!(desc.file.date, Some(DateTime::parse_from_rfc3339("2015-07-26T21:46:00+01:00").unwrap())); assert_eq!(desc.file.size, Some(6144u64)); assert_eq!(desc.file.range, None); @@ -272,15 +280,51 @@ mod tests { "#.parse().unwrap(); - let desc = Description::try_from(elem).unwrap(); assert_eq!(desc.file.media_type, None); assert_eq!(desc.file.name, None); - assert_eq!(desc.file.desc, None); + assert_eq!(desc.file.descs, BTreeMap::new()); assert_eq!(desc.file.date, None); assert_eq!(desc.file.size, None); assert_eq!(desc.file.range, None); assert_eq!(desc.file.hashes[0].algo, Algo::Sha_1); assert_eq!(desc.file.hashes[0].hash, base64::decode("w0mcJylzCn+AfvuGdqkty2+KP48=").unwrap()); } + + #[test] + fn test_descs() { + let elem: Element = r#" + + + text/plain + Fichier secret ! + Secret file! + w0mcJylzCn+AfvuGdqkty2+KP48= + + +"#.parse().unwrap(); + let desc = Description::try_from(elem).unwrap(); + assert_eq!(desc.file.descs.keys().cloned().collect::>(), ["en", "fr"]); + assert_eq!(desc.file.descs["en"], Desc(String::from("Secret file!"))); + assert_eq!(desc.file.descs["fr"], Desc(String::from("Fichier secret !"))); + + let elem: Element = r#" + + + text/plain + Fichier secret ! + Secret file! + w0mcJylzCn+AfvuGdqkty2+KP48= + + +"#.parse().unwrap(); + let error = Description::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Desc element present twice for the same xml:lang."); + } } From 92277ccde0c54afb526b190195b9cc45d46a3dc5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Aug 2017 02:33:37 +0100 Subject: [PATCH 326/698] jingle_ft: Add support for empty range elements. These can be used to indicate support for ranged transfers. --- src/jingle_ft.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 1931aabd535f76dff19e2bd10029ffee11cc2b1b..876aae37a76e740ab1547ea340c9eb413815820b 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -28,7 +28,7 @@ impl IntoElements for Range { fn into_elements(self, emitter: &mut ElementEmitter) { let mut elem = Element::builder("range") .ns(ns::JINGLE_FT) - .attr("offset", self.offset) + .attr("offset", if self.offset == 0 { None } else { Some(self.offset) }) .attr("length", self.length) .build(); for hash in self.hashes { From 84437ed03f1bc170e55787ea4f79bead4c9791d6 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Aug 2017 01:46:10 +0100 Subject: [PATCH 327/698] ChangeLog: Add imminent vesion 0.8.0. --- ChangeLog | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index a780194d330fdc8b629141fa96167beafa2a9bd2..3f86f90e3c06d3db4fee2a1f2d365a0201e208af 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,25 @@ -Version 0.7.0: +Version 0.8.0: +2017-08-27 Emmanuel Gil Peyrot + * New parsers/serialisers: + - iq:version (XEP-0092) has been added. + - Finally implement extension serialisation in disco. + * Breaking changes: + - Wrap even more elements into their own type, in jingle, + jingle_ft, roster, message. + - Split loose enums into multiple structs where it makes sense, + such as for IBB, StanzaId, Receipts. + - Split disco query and answer elements into their own struct, + to enforce more guarantees on both. + * Improvements: + - Use Vec::into_iter() more to avoid references and clones. + - Make data_forms propagate a media_element error. + - Document more of disco, roster, chatstates. + - Use the minidom feature of jid, for IntoAttributeValue. + - Add a component feature, changing the default namespace to + jabber:component:accept. + - Add support for indicating ranged transfers in jingle_ft. + +Version 0.7.1: 2017-07-24 Emmanuel Gil Peyrot * Hotfixes: - Stub out blake2 support, since the blake2 crate broke its API From 63c6df59db287ca3eed0a83470abb61ba98ba275 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Aug 2017 14:20:15 +0100 Subject: [PATCH 328/698] Release version 0.8.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 2318f0f2f0241651d03912c4a95df44d6a481629..9db6539d876dba3d09a6c8dc03ea3a1294647394 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.7.1" +version = "0.8.0" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", From 33018a5c09a231524647ea7529fea320b6c88bdd Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 10 Oct 2017 17:40:29 +0100 Subject: [PATCH 330/698] =?UTF-8?q?roster:=20Make=20subscription=3D"none"?= =?UTF-8?q?=20the=20default,=20see=20RFC6121=20=C2=A72.1.2.5.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/roster.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/roster.rs b/src/roster.rs index ae26c3d25971cf023c1930cf1e91e16535badb0e..38b183f1d29b6c4dda1a1fb7a750c14e05612724 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -21,7 +21,7 @@ generate_attribute!(Subscription, "subscription", { To => "to", Both => "both", Remove => "remove", -}); +}, Default = None); /// Contact from the user’s contact list. #[derive(Debug, Clone, PartialEq)] @@ -33,7 +33,7 @@ pub struct Item { pub name: Option, /// Subscription status of this contact. - pub subscription: Option, + pub subscription: Subscription, /// Groups this contact is part of. pub groups: Vec, @@ -50,7 +50,7 @@ impl TryFrom for Item { let mut item = Item { jid: get_attr!(elem, "jid", required), name: get_attr!(elem, "name", optional).and_then(|name| if name == "" { None } else { Some(name) }), - subscription: get_attr!(elem, "subscription", optional), + subscription: get_attr!(elem, "subscription", default), groups: vec!(), }; for child in elem.children() { @@ -183,7 +183,7 @@ mod tests { assert_eq!(roster.items.len(), 3); assert_eq!(roster.items[0].jid, Jid::from_str("romeo@example.net").unwrap()); assert_eq!(roster.items[0].name, Some(String::from("Romeo"))); - assert_eq!(roster.items[0].subscription, Some(Subscription::Both)); + assert_eq!(roster.items[0].subscription, Subscription::Both); assert_eq!(roster.items[0].groups, vec!(Group::from_str("Friends").unwrap())); } @@ -245,7 +245,7 @@ mod tests { assert_eq!(roster.items[0].jid, Jid::from_str("nurse@example.com").unwrap()); assert!(roster.items[0].name.is_none()); assert!(roster.items[0].groups.is_empty()); - assert_eq!(roster.items[0].subscription, Some(Subscription::Remove)); + assert_eq!(roster.items[0].subscription, Subscription::Remove); } #[test] From 6add31b5264cfa6f9c259b2c4e16ee93ca7701ff Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 10 Oct 2017 17:52:14 +0100 Subject: [PATCH 331/698] lib: Add check macros, to simplify code. --- src/lib.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 75b092fb56de96e1e07b674d9a9afc5781496a81..2184a843d615b49e6f3f90ebf3b7f4aeae60bc0d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -125,6 +125,40 @@ macro_rules! generate_attribute { ); } +macro_rules! check_self { + ($elem:ident, $name:tt, $ns:expr) => ( + if !$elem.is($name, $ns) { + return Err(Error::ParseError(concat!("This is not a ", $name, " element."))); + } + ); + ($elem:ident, $name:tt, $ns:expr, $pretty_name:tt) => ( + if !$elem.is($name, $ns) { + return Err(Error::ParseError(concat!("This is not a ", $pretty_name, " element."))); + } + ); +} + +macro_rules! check_no_children { + ($elem:ident, $name:tt) => ( + for _ in $elem.children() { + return Err(Error::ParseError(concat!("Unknown child in ", $name, " element."))); + } + ); +} + +macro_rules! check_no_unknown_attributes { + ($elem:ident, $name:tt, [$($attr:tt),*]) => ( + for (_attr, _) in $elem.attrs() { + $( + if _attr == $attr { + continue; + } + )* + return Err(Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); + } + ); +} + macro_rules! generate_id { ($elem:ident) => ( #[derive(Debug, Clone, PartialEq, Eq, Hash)] From cfbfce512e7284b3e65b0ba39401a702d81e4de2 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 10 Oct 2017 17:53:25 +0100 Subject: [PATCH 332/698] lib, ping, attention: Add a macro for singleton elements. --- src/attention.rs | 29 +---------------------------- src/lib.rs | 30 ++++++++++++++++++++++++++++++ src/ping.rs | 29 +---------------------------- 3 files changed, 32 insertions(+), 56 deletions(-) diff --git a/src/attention.rs b/src/attention.rs index 7d0b1f24ff3835472c464c9915973a1629fd2780..da2e6f6494becee18aba3bea136b8e46cf9b19a8 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -12,34 +12,7 @@ use error::Error; use ns; -/// Structure representing an `` element. -#[derive(Debug, Clone)] -pub struct Attention; - -impl TryFrom for Attention { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("attention", ns::ATTENTION) { - return Err(Error::ParseError("This is not an attention element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in attention element.")); - } - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown attribute in attention element.")); - } - Ok(Attention) - } -} - -impl From for Element { - fn from(_: Attention) -> Element { - Element::builder("attention") - .ns(ns::ATTENTION) - .build() - } -} +generate_empty_element!(Attention, "attention", ns::ATTENTION); #[cfg(test)] mod tests { diff --git a/src/lib.rs b/src/lib.rs index 2184a843d615b49e6f3f90ebf3b7f4aeae60bc0d..9f3b80bb68bf3c236f70389899fc48bda272dfd2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -159,6 +159,36 @@ macro_rules! check_no_unknown_attributes { ); } +macro_rules! generate_empty_element { + ($elem:ident, $name:tt, $ns:expr) => ( + // TODO: Find a better way to concatenate doc. + #[doc="Structure representing a "] + #[doc=$name] + #[doc=" element."] + #[derive(Debug, Clone)] + pub struct $elem; + + impl TryFrom for $elem { + type Err = Error; + + fn try_from(elem: Element) -> Result<$elem, Error> { + check_self!(elem, $name, $ns); + check_no_children!(elem, $name); + check_no_unknown_attributes!(elem, $name, []); + Ok($elem) + } + } + + impl From<$elem> for Element { + fn from(_: $elem) -> Element { + Element::builder("attention") + .ns($ns) + .build() + } + } + ); +} + macro_rules! generate_id { ($elem:ident) => ( #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/src/ping.rs b/src/ping.rs index c97fa5026ac5595ac3db83332afa492746a3ea3b..d4044f477d8cd534dbe7ce68356cbe81e010126a 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -13,34 +13,7 @@ use error::Error; use ns; -/// Structure representing a `` element. -#[derive(Debug, Clone)] -pub struct Ping; - -impl TryFrom for Ping { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("ping", ns::PING) { - return Err(Error::ParseError("This is not a ping element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in ping element.")); - } - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown attribute in ping element.")); - } - Ok(Ping) - } -} - -impl From for Element { - fn from(_: Ping) -> Element { - Element::builder("ping") - .ns(ns::PING) - .build() - } -} +generate_empty_element!(Ping, "ping", ns::PING); #[cfg(test)] mod tests { From 66fb8fea74894d4f33498b11ffd219e1d36b6e5e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 10 Oct 2017 18:00:15 +0100 Subject: [PATCH 333/698] message_correct: Use the new helper macros to simplify parsing. --- src/message_correct.rs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/message_correct.rs b/src/message_correct.rs index bb5c2b2e25cc037aa1305f52ac11bf47067b6200..d8200ae19b07401e21a548a6e0b0018e7e4efa1e 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -21,17 +21,9 @@ impl TryFrom for Replace { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("replace", ns::MESSAGE_CORRECT) { - return Err(Error::ParseError("This is not a replace element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in replace element.")); - } - for (attr, _) in elem.attrs() { - if attr != "id" { - return Err(Error::ParseError("Unknown attribute in replace element.")); - } - } + check_self!(elem, "replace", ns::MESSAGE_CORRECT); + check_no_children!(elem, "replace"); + check_no_unknown_attributes!(elem, "replace", ["id"]); let id = get_attr!(elem, "id", required); Ok(Replace { id }) } From a2b603333632785b86d868e59fef4ea8131563be Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 10 Oct 2017 18:04:27 +0100 Subject: [PATCH 334/698] caps: Use the new helper macros to simplify parsing. --- src/caps.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/caps.rs b/src/caps.rs index a2bdd11bed7d92720a3e383980c4c7f2a7ea3ddd..2b986795daa7ab7b9c243fdd6e1a0654353d27bb 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -33,16 +33,12 @@ impl TryFrom for Caps { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("c", ns::CAPS) { - return Err(Error::ParseError("This is not a caps element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in caps element.")); - } - let hash = get_attr!(elem, "hash", required); + check_self!(elem, "c", ns::CAPS, "caps"); + check_no_children!(elem, "caps"); + check_no_unknown_attributes!(elem, "caps", ["hash", "ver", "ext", "node"]); let ver: String = get_attr!(elem, "ver", required); let hash = Hash { - algo: hash, + algo: get_attr!(elem, "hash", required), hash: base64::decode(&ver)?, }; Ok(Caps { From ee243c47207c95199f80ff5232b61651b2c77951 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 10 Oct 2017 18:09:58 +0100 Subject: [PATCH 335/698] chatstates: Use the new helper macros to simplify parsing. --- src/chatstates.rs | 12 +++--------- src/lib.rs | 8 ++++++++ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/chatstates.rs b/src/chatstates.rs index ecbecdffc4135dbfe7b50aa68aafbb57a9e5402e..995cd11e0a11ccb42f63f79689b0e85634c33c69 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -38,15 +38,9 @@ impl TryFrom for ChatState { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.has_ns(ns::CHATSTATES) { - return Err(Error::ParseError("This is not a chatstate element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in chatstate element.")); - } - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown attribute in chatstate element.")); - } + check_ns_only!(elem, "chatstate", ns::CHATSTATES); + check_no_children!(elem, "chatstate"); + check_no_unknown_attributes!(elem, "chatstate", []); Ok(match elem.name() { "active" => ChatState::Active, "composing" => ChatState::Composing, diff --git a/src/lib.rs b/src/lib.rs index 9f3b80bb68bf3c236f70389899fc48bda272dfd2..5b52da0564111cd31c848590aa8ab549ab98ee6a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -138,6 +138,14 @@ macro_rules! check_self { ); } +macro_rules! check_ns_only { + ($elem:ident, $name:tt, $ns:expr) => ( + if !$elem.has_ns($ns) { + return Err(Error::ParseError(concat!("This is not a ", $name, " element."))); + } + ); +} + macro_rules! check_no_children { ($elem:ident, $name:tt) => ( for _ in $elem.children() { From a2c752966881d46a904222ab35d3a551fc7492d9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 10 Oct 2017 18:10:52 +0100 Subject: [PATCH 336/698] eme: Use the new helper macros to simplify parsing. --- src/eme.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/eme.rs b/src/eme.rs index 288431b9c975b95ebc180c7bfeb74c43da63d72a..ad4b437f8098a5cc18551277f7b03ce4eebf4210 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -27,12 +27,9 @@ impl TryFrom for ExplicitMessageEncryption { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("encryption", ns::EME) { - return Err(Error::ParseError("This is not an encryption element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in encryption element.")); - } + check_self!(elem, "encryption", ns::EME); + check_no_children!(elem, "encryption"); + check_no_unknown_attributes!(elem, "encryption", ["namespace", "name"]); Ok(ExplicitMessageEncryption { namespace: get_attr!(elem, "namespace", required), name: get_attr!(elem, "name", optional), @@ -75,7 +72,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "This is not an encryption element."); + assert_eq!(message, "This is not a encryption element."); } #[test] From 9f27f200ca39d79520ae42aa7db5150396584830 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 10 Oct 2017 18:26:05 +0100 Subject: [PATCH 337/698] data_forms: Use the new helper macros to simplify parsing. --- src/data_forms.rs | 109 ++++++++++++++++++++++------------------------ 1 file changed, 51 insertions(+), 58 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index 15135ef3df2edfde96108bf0015bce6bf59e46ed..e3a68f4680079b7069a89befb8de7ed4b16fca7b 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -14,25 +14,35 @@ use ns; use media_element::MediaElement; -generate_attribute!(FieldType, "type", { - Boolean => "boolean", - Fixed => "fixed", - Hidden => "hidden", - JidMulti => "jid-multi", - JidSingle => "jid-single", - ListMulti => "list-multi", - ListSingle => "list-single", - TextMulti => "text-multi", - TextPrivate => "text-private", - TextSingle => "text-single", -}, Default = TextSingle); - #[derive(Debug, Clone)] pub struct Option_ { pub label: Option, pub value: String, } +impl TryFrom for Option_ { + type Err = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "option", ns::DATA_FORMS); + check_no_unknown_attributes!(elem, "option", ["label"]); + let mut value = None; + for child in elem.children() { + if !child.is("value", ns::DATA_FORMS) { + return Err(Error::ParseError("Non-value element in option element")); + } + if value.is_some() { + return Err(Error::ParseError("More than one value element in option element")); + } + value = Some(child.text()); + } + Ok(Option_ { + label: get_attr!(elem, "label", optional), + value: value.ok_or(Error::ParseError("No value element in option element"))?, + }) + } +} + impl From for Element { fn from(option: Option_) -> Element { Element::builder("option") @@ -46,6 +56,19 @@ impl From for Element { } } +generate_attribute!(FieldType, "type", { + Boolean => "boolean", + Fixed => "fixed", + Hidden => "hidden", + JidMulti => "jid-multi", + JidSingle => "jid-single", + ListMulti => "list-multi", + ListSingle => "list-single", + TextMulti => "text-multi", + TextPrivate => "text-private", + TextSingle => "text-single", +}, Default = TextSingle); + #[derive(Debug, Clone)] pub struct Field { pub var: String, @@ -68,6 +91,8 @@ impl TryFrom for Field { type Err = Error; fn try_from(elem: Element) -> Result { + check_self!(elem, "field", ns::DATA_FORMS); + check_no_unknown_attributes!(elem, "field", ["label", "type", "var"]); let mut field = Field { var: get_attr!(elem, "var", required), type_: get_attr!(elem, "type", default), @@ -79,50 +104,27 @@ impl TryFrom for Field { }; for element in elem.children() { if element.is("value", ns::DATA_FORMS) { - for _ in element.children() { - return Err(Error::ParseError("Value element must not have any child.")); - } - for _ in element.attrs() { - return Err(Error::ParseError("Value element must not have any attribute.")); - } + check_no_children!(element, "value"); + check_no_unknown_attributes!(element, "value", []); field.values.push(element.text()); } else if element.is("required", ns::DATA_FORMS) { if field.required { return Err(Error::ParseError("More than one required element.")); } - for _ in element.children() { - return Err(Error::ParseError("Required element must not have any child.")); - } - for _ in element.attrs() { - return Err(Error::ParseError("Required element must not have any attribute.")); - } + check_no_children!(element, "required"); + check_no_unknown_attributes!(element, "required", []); field.required = true; } else if element.is("option", ns::DATA_FORMS) { if !field.is_list() { return Err(Error::ParseError("Option element found in non-list field.")); } - let label = get_attr!(element, "label", optional); - let mut value = None; - for child2 in element.children() { - if child2.is("value", ns::DATA_FORMS) { - if value.is_some() { - return Err(Error::ParseError("More than one value element in option element")); - } - value = Some(child2.text()); - } else { - return Err(Error::ParseError("Non-value element in option element")); - } - } - let value = value.ok_or(Error::ParseError("No value element in option element"))?; - field.options.push(Option_ { - label: label, - value: value, - }); + let option = Option_::try_from(element.clone())?; + field.options.push(option); } else if element.is("media", ns::MEDIA_ELEMENT) { let media_element = MediaElement::try_from(element.clone())?; field.media.push(media_element); } else { - return Err(Error::ParseError("Field child isn’t a value or media element.")); + return Err(Error::ParseError("Field child isn’t a value, option or media element.")); } } Ok(field) @@ -166,9 +168,8 @@ impl TryFrom for DataForm { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("x", ns::DATA_FORMS) { - return Err(Error::ParseError("This is not a data form element.")); - } + check_self!(elem, "x", ns::DATA_FORMS); + check_no_unknown_attributes!(elem, "x", ["type"]); let type_ = get_attr!(elem, "type", required); let mut form = DataForm { type_: type_, @@ -182,23 +183,15 @@ impl TryFrom for DataForm { if form.title.is_some() { return Err(Error::ParseError("More than one title in form element.")); } - for _ in child.children() { - return Err(Error::ParseError("Title element must not have any child.")); - } - for _ in child.attrs() { - return Err(Error::ParseError("Title element must not have any attribute.")); - } + check_no_children!(child, "title"); + check_no_unknown_attributes!(child, "title", []); form.title = Some(child.text()); } else if child.is("instructions", ns::DATA_FORMS) { if form.instructions.is_some() { return Err(Error::ParseError("More than one instructions in form element.")); } - for _ in child.children() { - return Err(Error::ParseError("instructions element must not have any child.")); - } - for _ in child.attrs() { - return Err(Error::ParseError("instructions element must not have any attribute.")); - } + check_no_children!(child, "instructions"); + check_no_unknown_attributes!(child, "instructions", []); form.instructions = Some(child.text()); } else if child.is("field", ns::DATA_FORMS) { let field = Field::try_from(child.clone())?; From 5f6f6a5e910c55b7fd315862547e277149f15198 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 10 Oct 2017 19:00:42 +0100 Subject: [PATCH 338/698] disco: Use the new helper macros to simplify parsing. --- src/disco.rs | 77 +++++++++++++--------------------------------------- 1 file changed, 19 insertions(+), 58 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index e67e7973a6b73d38a2c2e37afc2ed27bbd9f96b8..3e9d8ce1fe2a0313d1d57febf1a803788cf92168 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -30,17 +30,9 @@ impl TryFrom for DiscoInfoQuery { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("query", ns::DISCO_INFO) { - return Err(Error::ParseError("This is not a disco#info element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in disco#info.")); - } - for (attr, _) in elem.attrs() { - if attr != "node" { - return Err(Error::ParseError("Unknown attribute in disco#info.")); - } - } + check_self!(elem, "query", ns::DISCO_INFO); + check_no_children!(elem, "query"); + check_no_unknown_attributes!(elem, "query", ["node"]); Ok(DiscoInfoQuery { node: get_attr!(elem, "node", optional), }) @@ -67,17 +59,9 @@ impl TryFrom for Feature { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("feature", ns::DISCO_INFO) { - return Err(Error::ParseError("This is not a disco#info feature element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in disco#info feature element.")); - } - for (attr, _) in elem.attrs() { - if attr != "var" { - return Err(Error::ParseError("Unknown attribute in disco#info feature element.")); - } - } + check_self!(elem, "feature", ns::DISCO_INFO, "disco#info feature"); + check_no_children!(elem, "disco#info feature"); + check_no_unknown_attributes!(elem, "disco#info feature", ["var"]); Ok(Feature { var: get_attr!(elem, "var", required) }) @@ -113,9 +97,9 @@ impl TryFrom for Identity { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("identity", ns::DISCO_INFO) { - return Err(Error::ParseError("This is not a disco#info identity element.")); - } + check_self!(elem, "identity", ns::DISCO_INFO, "disco#info identity"); + check_no_children!(elem, "disco#info identity"); + check_no_unknown_attributes!(elem, "disco#info identity", ["category", "type", "xml:lang", "name"]); let category = get_attr!(elem, "category", required); if category == "" { @@ -171,9 +155,8 @@ impl TryFrom for DiscoInfoResult { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("query", ns::DISCO_INFO) { - return Err(Error::ParseError("This is not a disco#info element.")); - } + check_self!(elem, "query", ns::DISCO_INFO, "disco#info result"); + check_no_unknown_attributes!(elem, "disco#info result", ["node"]); let mut result = DiscoInfoResult { node: get_attr!(elem, "node", optional), @@ -243,17 +226,9 @@ impl TryFrom for DiscoItemsQuery { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("query", ns::DISCO_ITEMS) { - return Err(Error::ParseError("This is not a disco#items element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in disco#items.")); - } - for (attr, _) in elem.attrs() { - if attr != "node" { - return Err(Error::ParseError("Unknown attribute in disco#items.")); - } - } + check_self!(elem, "query", ns::DISCO_ITEMS, "disco#items query"); + check_no_children!(elem, "disco#items query"); + check_no_unknown_attributes!(elem, "disco#items query", ["node"]); Ok(DiscoItemsQuery { node: get_attr!(elem, "node", optional), }) @@ -284,17 +259,9 @@ impl TryFrom for Item { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("item", ns::DISCO_ITEMS) { - return Err(Error::ParseError("This is not an item element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in item element.")); - } - for (attr, _) in elem.attrs() { - if attr != "jid" && attr != "node" && attr != "name" { - return Err(Error::ParseError("Unknown attribute in item element.")); - } - } + check_self!(elem, "item", ns::DISCO_ITEMS); + check_no_children!(elem, "item"); + check_no_unknown_attributes!(elem, "item", ["jid", "node", "name"]); Ok(Item { jid: get_attr!(elem, "jid", required), node: get_attr!(elem, "node", optional), @@ -332,14 +299,8 @@ impl TryFrom for DiscoItemsResult { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("query", ns::DISCO_ITEMS) { - return Err(Error::ParseError("This is not a disco#items element.")); - } - for (attr, _) in elem.attrs() { - if attr != "node" { - return Err(Error::ParseError("Unknown attribute in disco#items.")); - } - } + check_self!(elem, "query", ns::DISCO_ITEMS, "disco#items query"); + check_no_unknown_attributes!(elem, "disco#items query", ["node"]); let mut items: Vec = vec!(); for child in elem.children() { From e1477f146b463a6729c398d3d5a496c4d5b1e0cf Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 10 Oct 2017 19:27:29 +0100 Subject: [PATCH 339/698] pubsub::event: Use the new helper macros to simplify parsing. --- src/pubsub/event.rs | 59 ++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index d055476d5b66ce4a034f1b8d6178bb6579915eda..12f1f774973e0ebc170140c57d832ddee21f594c 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -25,6 +25,26 @@ pub struct Item { publisher: Option, } +impl TryFrom for Item { + type Err = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "item", ns::PUBSUB_EVENT); + check_no_unknown_attributes!(elem, "item", ["id", "node", "publisher"]); + let mut payloads = elem.children().cloned().collect::>(); + let payload = payloads.pop(); + if !payloads.is_empty() { + return Err(Error::ParseError("More than a single payload in item element.")); + } + Ok(Item { + payload, + id: get_attr!(elem, "id", optional), + node: get_attr!(elem, "node", optional), + publisher: get_attr!(elem, "publisher", optional), + }) + } +} + impl From for Element { fn from(item: Item) -> Element { Element::builder("item") @@ -92,32 +112,15 @@ fn parse_items(elem: Element, node: String) -> Result { Some(false) => (), Some(true) => return Err(Error::ParseError("Mix of item and retract in items element.")), } - let mut payloads = child.children().cloned().collect::>(); - let payload = payloads.pop(); - if !payloads.is_empty() { - return Err(Error::ParseError("More than a single payload in item element.")); - } - let item = Item { - payload, - id: get_attr!(child, "id", optional), - node: get_attr!(child, "node", optional), - publisher: get_attr!(child, "publisher", optional), - }; - items.push(item); + items.push(Item::try_from(child.clone())?); } else if child.is("retract", ns::PUBSUB_EVENT) { match is_retract { None => is_retract = Some(true), Some(true) => (), Some(false) => return Err(Error::ParseError("Mix of item and retract in items element.")), } - for _ in child.children() { - return Err(Error::ParseError("Unknown child in retract element.")); - } - for (attr, _) in child.attrs() { - if attr != "id" { - return Err(Error::ParseError("Unknown attribute in retract element.")); - } - } + check_no_children!(child, "retract"); + check_no_unknown_attributes!(child, "retract", ["id"]); let id = get_attr!(child, "id", required); retracts.push(id); } else { @@ -135,21 +138,11 @@ impl TryFrom for PubSubEvent { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("event", ns::PUBSUB_EVENT) { - return Err(Error::ParseError("This is not an event element.")); - } - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown attribute in event element.")); - } + check_self!(elem, "event", ns::PUBSUB_EVENT); + check_no_unknown_attributes!(elem, "event", []); + let mut payload = None; for child in elem.children() { - /* - for (attr, _) in child.attrs() { - if attr != "node" { - return Err(Error::ParseError("Unknown attribute in items element.")); - } - } - */ let node = get_attr!(child, "node", required); if child.is("configuration", ns::PUBSUB_EVENT) { let mut payloads = child.children().cloned().collect::>(); From a8cfc8e62e230557bba65a500c50475c7adc8c81 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 10 Oct 2017 19:45:08 +0100 Subject: [PATCH 340/698] pubsub::event: Add more type safety. --- src/pubsub/event.rs | 61 ++++++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 12f1f774973e0ebc170140c57d832ddee21f594c..9f8b1e4c199020f70cbdc6ace3cfdde620fc137c 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -17,11 +17,15 @@ use ns; use data_forms::DataForm; +generate_id!(NodeName); +generate_id!(ItemId); +generate_id!(SubscriptionId); + #[derive(Debug, Clone)] pub struct Item { payload: Option, - id: Option, - node: Option, + node: Option, + id: Option, publisher: Option, } @@ -71,37 +75,37 @@ pub enum PubSubEvent { }, */ Configuration { - node: String, + node: NodeName, form: Option, }, Delete { - node: String, + node: NodeName, redirect: Option, }, EmptyItems { - node: String, + node: NodeName, }, PublishedItems { - node: String, + node: NodeName, items: Vec, }, RetractedItems { - node: String, - items: Vec, + node: NodeName, + items: Vec, }, Purge { - node: String, + node: NodeName, }, Subscription { - node: String, + node: NodeName, expiry: Option>, jid: Option, - subid: Option, + subid: Option, subscription: Option, }, } -fn parse_items(elem: Element, node: String) -> Result { +fn parse_items(elem: Element, node: NodeName) -> Result { let mut is_retract = None; let mut items = vec!(); let mut retracts = vec!(); @@ -234,7 +238,12 @@ impl From for Element { Element::builder("items") .ns(ns::PUBSUB_EVENT) .attr("node", node) - .append(items) + .append(items.into_iter().map(|id| { + Element::builder("retract") + .ns(ns::PUBSUB_EVENT) + .attr("id", id) + .build() + }).collect::>()) .build() }, PubSubEvent::Purge { node } => { @@ -272,7 +281,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let event = PubSubEvent::try_from(elem).unwrap(); match event { - PubSubEvent::EmptyItems { node } => assert_eq!(node, String::from("coucou")), + PubSubEvent::EmptyItems { node } => assert_eq!(node, NodeName(String::from("coucou"))), _ => panic!(), } } @@ -283,9 +292,9 @@ mod tests { let event = PubSubEvent::try_from(elem).unwrap(); match event { PubSubEvent::PublishedItems { node, items } => { - assert_eq!(node, String::from("coucou")); - assert_eq!(items[0].id, Some(String::from("test"))); - assert_eq!(items[0].node, Some(String::from("huh?"))); + assert_eq!(node, NodeName(String::from("coucou"))); + assert_eq!(items[0].id, Some(ItemId(String::from("test")))); + assert_eq!(items[0].node, Some(NodeName(String::from("huh?")))); assert_eq!(items[0].publisher, Some(Jid::from_str("test@coucou").unwrap())); assert_eq!(items[0].payload, None); }, @@ -299,7 +308,7 @@ mod tests { let event = PubSubEvent::try_from(elem).unwrap(); match event { PubSubEvent::PublishedItems { node, items } => { - assert_eq!(node, String::from("something")); + assert_eq!(node, NodeName(String::from("something"))); assert_eq!(items[0].id, None); assert_eq!(items[0].node, None); assert_eq!(items[0].publisher, None); @@ -318,9 +327,9 @@ mod tests { let event = PubSubEvent::try_from(elem).unwrap(); match event { PubSubEvent::RetractedItems { node, items } => { - assert_eq!(node, String::from("something")); - assert_eq!(items[0], String::from("coucou")); - assert_eq!(items[1], String::from("test")); + assert_eq!(node, NodeName(String::from("something"))); + assert_eq!(items[0], ItemId(String::from("coucou"))); + assert_eq!(items[1], ItemId(String::from("test"))); }, _ => panic!(), } @@ -332,7 +341,7 @@ mod tests { let event = PubSubEvent::try_from(elem).unwrap(); match event { PubSubEvent::Delete { node, redirect } => { - assert_eq!(node, String::from("coucou")); + assert_eq!(node, NodeName(String::from("coucou"))); assert_eq!(redirect, Some(String::from("hello"))); }, _ => panic!(), @@ -345,7 +354,7 @@ mod tests { let event = PubSubEvent::try_from(elem).unwrap(); match event { PubSubEvent::Purge { node } => { - assert_eq!(node, String::from("coucou")); + assert_eq!(node, NodeName(String::from("coucou"))); }, _ => panic!(), } @@ -357,7 +366,7 @@ mod tests { let event = PubSubEvent::try_from(elem).unwrap(); match event { PubSubEvent::Configuration { node, form: _ } => { - assert_eq!(node, String::from("coucou")); + assert_eq!(node, NodeName(String::from("coucou"))); //assert_eq!(form.type_, Result_); }, _ => panic!(), @@ -401,8 +410,8 @@ mod tests { let event = PubSubEvent::try_from(elem.clone()).unwrap(); match event.clone() { PubSubEvent::Subscription { node, expiry, jid, subid, subscription } => { - assert_eq!(node, String::from("princely_musings")); - assert_eq!(subid, Some(String::from("ba49252aaa4f5d320c24d3766f0bdcade78c78d3"))); + assert_eq!(node, NodeName(String::from("princely_musings"))); + assert_eq!(subid, Some(SubscriptionId(String::from("ba49252aaa4f5d320c24d3766f0bdcade78c78d3")))); assert_eq!(subscription, Some(Subscription::Subscribed)); assert_eq!(jid, Some(Jid::from_str("francisco@denmark.lit").unwrap())); assert_eq!(expiry, Some("2006-02-28T23:59:59Z".parse().unwrap())); From 1892e1ca042b77aafdb96149e15cd614f1656512 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 29 Oct 2017 00:36:36 +0100 Subject: [PATCH 341/698] disco: Check for children ordering in disco#info. --- src/disco.rs | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index 3e9d8ce1fe2a0313d1d57febf1a803788cf92168..2c3189f06f42d542537d80024c1923d33aacfe78 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -164,15 +164,26 @@ impl TryFrom for DiscoInfoResult { features: vec!(), extensions: vec!(), }; + let mut parsing_identities_done = false; + let mut parsing_features_done = false; for child in elem.children() { - if child.is("feature", ns::DISCO_INFO) { - let feature = Feature::try_from(child.clone())?; - result.features.push(feature); - } else if child.is("identity", ns::DISCO_INFO) { + if child.is("identity", ns::DISCO_INFO) { + if parsing_identities_done { + return Err(Error::ParseError("Identity found after features or data forms in disco#info.")); + } let identity = Identity::try_from(child.clone())?; result.identities.push(identity); + } else if child.is("feature", ns::DISCO_INFO) { + parsing_identities_done = true; + if parsing_features_done { + return Err(Error::ParseError("Feature found after data forms in disco#info.")); + } + let feature = Feature::try_from(child.clone())?; + result.features.push(feature); } else if child.is("x", ns::DATA_FORMS) { + parsing_identities_done = true; + parsing_features_done = true; let data_form = DataForm::try_from(child.clone())?; if data_form.type_ != DataFormType::Result_ { return Err(Error::ParseError("Data form must have a 'result' type in disco#info.")); @@ -441,6 +452,22 @@ mod tests { _ => panic!(), }; assert_eq!(message, "disco#info feature not present in disco#info."); + + let elem: Element = "".parse().unwrap(); + let error = DiscoInfoResult::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Identity found after features or data forms in disco#info."); + + let elem: Element = "coucou".parse().unwrap(); + let error = DiscoInfoResult::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Feature found after data forms in disco#info."); } #[test] From f85b451fcf5dbcab6bd4c53b7ca93344a69f5e80 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 15:47:38 +0000 Subject: [PATCH 342/698] Add a new check_no_attributes macro, to avoid the empty list. --- src/chatstates.rs | 2 +- src/data_forms.rs | 8 ++++---- src/lib.rs | 8 +++++++- src/pubsub/event.rs | 2 +- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/chatstates.rs b/src/chatstates.rs index 995cd11e0a11ccb42f63f79689b0e85634c33c69..e226f21d3bb72cc620bb3b7485fad57d89602986 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -40,7 +40,7 @@ impl TryFrom for ChatState { fn try_from(elem: Element) -> Result { check_ns_only!(elem, "chatstate", ns::CHATSTATES); check_no_children!(elem, "chatstate"); - check_no_unknown_attributes!(elem, "chatstate", []); + check_no_attributes!(elem, "chatstate"); Ok(match elem.name() { "active" => ChatState::Active, "composing" => ChatState::Composing, diff --git a/src/data_forms.rs b/src/data_forms.rs index e3a68f4680079b7069a89befb8de7ed4b16fca7b..a54be9c2e7a3139915be92b22140c9b2eb58672e 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -105,14 +105,14 @@ impl TryFrom for Field { for element in elem.children() { if element.is("value", ns::DATA_FORMS) { check_no_children!(element, "value"); - check_no_unknown_attributes!(element, "value", []); + check_no_attributes!(element, "value"); field.values.push(element.text()); } else if element.is("required", ns::DATA_FORMS) { if field.required { return Err(Error::ParseError("More than one required element.")); } check_no_children!(element, "required"); - check_no_unknown_attributes!(element, "required", []); + check_no_attributes!(element, "required"); field.required = true; } else if element.is("option", ns::DATA_FORMS) { if !field.is_list() { @@ -184,14 +184,14 @@ impl TryFrom for DataForm { return Err(Error::ParseError("More than one title in form element.")); } check_no_children!(child, "title"); - check_no_unknown_attributes!(child, "title", []); + check_no_attributes!(child, "title"); form.title = Some(child.text()); } else if child.is("instructions", ns::DATA_FORMS) { if form.instructions.is_some() { return Err(Error::ParseError("More than one instructions in form element.")); } check_no_children!(child, "instructions"); - check_no_unknown_attributes!(child, "instructions", []); + check_no_attributes!(child, "instructions"); form.instructions = Some(child.text()); } else if child.is("field", ns::DATA_FORMS) { let field = Field::try_from(child.clone())?; diff --git a/src/lib.rs b/src/lib.rs index 5b52da0564111cd31c848590aa8ab549ab98ee6a..fee722cc0a00df8bbfed51591176c8c8f7062646 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -154,6 +154,12 @@ macro_rules! check_no_children { ); } +macro_rules! check_no_attributes { + ($elem:ident, $name:tt) => ( + check_no_unknown_attributes!($elem, $name, []); + ); +} + macro_rules! check_no_unknown_attributes { ($elem:ident, $name:tt, [$($attr:tt),*]) => ( for (_attr, _) in $elem.attrs() { @@ -182,7 +188,7 @@ macro_rules! generate_empty_element { fn try_from(elem: Element) -> Result<$elem, Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); - check_no_unknown_attributes!(elem, $name, []); + check_no_attributes!(elem, $name); Ok($elem) } } diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 9f8b1e4c199020f70cbdc6ace3cfdde620fc137c..74b59ae6cdf32baf92705de5a9f5796d5fd3f511 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -143,7 +143,7 @@ impl TryFrom for PubSubEvent { fn try_from(elem: Element) -> Result { check_self!(elem, "event", ns::PUBSUB_EVENT); - check_no_unknown_attributes!(elem, "event", []); + check_no_attributes!(elem, "event"); let mut payload = None; for child in elem.children() { From b7b3340c9f8e58f774b3cbb49d6e42d27c7a71bb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 15:48:11 +0000 Subject: [PATCH 343/698] Add a blocking command parser (XEP-0191). --- src/blocking.rs | 165 ++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 + src/ns.rs | 5 ++ 3 files changed, 173 insertions(+) create mode 100644 src/blocking.rs diff --git a/src/blocking.rs b/src/blocking.rs new file mode 100644 index 0000000000000000000000000000000000000000..084844e67b732d4bd9c41f530ef7eab76ee1f574 --- /dev/null +++ b/src/blocking.rs @@ -0,0 +1,165 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use try_from::TryFrom; + +use jid::Jid; +use minidom::Element; + +use error::Error; + +use ns; + +generate_empty_element!(BlocklistRequest, "blocklist", ns::BLOCKING); + +fn get_children_items(elem: Element) -> Result, Error> { + let mut items = vec!(); + for child in elem.children() { + check_self!(child, "item", ns::BLOCKING); + check_no_unknown_attributes!(child, "item", ["jid"]); + check_no_children!(child, "item"); + items.push(get_attr!(child, "jid", required)); + } + Ok(items) +} + +macro_rules! generate_blocking_element { + ($elem:ident, $name:tt) => ( + #[derive(Debug, Clone)] + pub struct $elem { + pub items: Vec, + } + + impl TryFrom for $elem { + type Err = Error; + + fn try_from(elem: Element) -> Result<$elem, Error> { + check_self!(elem, $name, ns::BLOCKING); + check_no_attributes!(elem, $name); + Ok($elem { + items: get_children_items(elem)?, + }) + } + } + + impl From<$elem> for Element { + fn from(elem: $elem) -> Element { + Element::builder($name) + .ns(ns::BLOCKING) + .append(elem.items.into_iter().map(|jid| { + Element::builder("item") + .ns(ns::BLOCKING) + .attr("jid", jid) + .build() + }).collect::>()) + .build() + } + } + ); +} + +generate_blocking_element!(BlocklistResult, "blocklist"); +generate_blocking_element!(Block, "block"); +generate_blocking_element!(Unblock, "unblock"); + +generate_empty_element!(Blocked, "blocked", ns::BLOCKING_ERRORS); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let request_elem = elem.clone(); + BlocklistRequest::try_from(request_elem).unwrap(); + + let result_elem = elem.clone(); + let result = BlocklistResult::try_from(result_elem).unwrap(); + assert_eq!(result.items, vec!()); + + let elem: Element = "".parse().unwrap(); + let block = Block::try_from(elem).unwrap(); + assert_eq!(block.items, vec!()); + + let elem: Element = "".parse().unwrap(); + let unblock = Unblock::try_from(elem).unwrap(); + assert_eq!(unblock.items, vec!()); + } + + #[test] + fn test_items() { + let elem: Element = "".parse().unwrap(); + let two_items = vec!( + Jid { + node: Some(String::from("coucou")), + domain: String::from("coucou"), + resource: None, + }, + Jid { + node: None, + domain: String::from("domain"), + resource: None, + }, + ); + + let request_elem = elem.clone(); + let error = BlocklistRequest::try_from(request_elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in blocklist element."); + + let result_elem = elem.clone(); + let result = BlocklistResult::try_from(result_elem).unwrap(); + assert_eq!(result.items, two_items); + + let elem: Element = "".parse().unwrap(); + let block = Block::try_from(elem).unwrap(); + assert_eq!(block.items, two_items); + + let elem: Element = "".parse().unwrap(); + let unblock = Unblock::try_from(elem).unwrap(); + assert_eq!(unblock.items, two_items); + } + + #[test] + fn test_invalid() { + let elem: Element = "".parse().unwrap(); + let request_elem = elem.clone(); + let error = BlocklistRequest::try_from(request_elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in blocklist element."); + + let result_elem = elem.clone(); + let error = BlocklistResult::try_from(result_elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in blocklist element."); + + let elem: Element = "".parse().unwrap(); + let error = Block::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in block element."); + + let elem: Element = "".parse().unwrap(); + let error = Unblock::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in unblock element."); + } +} diff --git a/src/lib.rs b/src/lib.rs index fee722cc0a00df8bbfed51591176c8c8f7062646..7eba6799152f8e2e59c371fb60d5a8bab0b672b4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -301,6 +301,9 @@ pub mod jingle; /// XEP-0184: Message Delivery Receipts pub mod receipts; +/// XEP-0191: Blocking Command +pub mod blocking; + /// XEP-0199: XMPP Ping pub mod ping; diff --git a/src/ns.rs b/src/ns.rs index 22e5f1566d4a3c06c325aa9f77e6b7ec7a11e1d0..045957d5b6125648eb2905fdfcb2e850ba292b73 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -62,6 +62,11 @@ pub const JINGLE: &str = "urn:xmpp:jingle:1"; /// XEP-0184: Message Delivery Receipts pub const RECEIPTS: &str = "urn:xmpp:receipts"; +/// XEP-0191: Blocking Command +pub const BLOCKING: &str = "urn:xmpp:blocking"; +/// XEP-0191: Blocking Command +pub const BLOCKING_ERRORS: &str = "urn:xmpp:blocking:errors"; + /// XEP-0199: XMPP Ping pub const PING: &str = "urn:xmpp:ping"; From 0200ced3e5eca4e8898f7952efdf603ffa2adb2a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 16:09:28 +0000 Subject: [PATCH 344/698] jingle_ft: Finish implementation of received. --- src/jingle_ft.rs | 83 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 66 insertions(+), 17 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 876aae37a76e740ab1547ea340c9eb413815820b..97ad5cd4707aacc169754f8dfbff6fafdef3ee06 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -10,6 +10,7 @@ use std::collections::BTreeMap; use std::str::FromStr; use hashes::Hash; +use jingle::Creator; use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; use chrono::{DateTime, FixedOffset}; @@ -58,12 +59,6 @@ pub struct Description { pub file: File, } -#[derive(Debug, Clone)] -pub enum Creator { - Initiator, - Responder, -} - #[derive(Debug, Clone)] pub struct Checksum { pub name: String, @@ -77,17 +72,27 @@ pub struct Received { pub creator: Creator, } -impl IntoElements for Received { - fn into_elements(self, emitter: &mut ElementEmitter) { - let elem = Element::builder("received") - .ns(ns::JINGLE_FT) - .attr("name", self.name) - .attr("creator", match self.creator { - Creator::Initiator => "initiator", - Creator::Responder => "responder", - }) - .build(); - emitter.append_child(elem); +impl TryFrom for Received { + type Err = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "received", ns::JINGLE_FT); + check_no_children!(elem, "received"); + check_no_unknown_attributes!(elem, "received", ["name", "creator"]); + Ok(Received { + name: get_attr!(elem, "name", required), + creator: get_attr!(elem, "creator", required), + }) + } +} + +impl From for Element { + fn from(received: Received) -> Element { + Element::builder("received") + .ns(ns::JINGLE_FT) + .attr("name", received.name) + .attr("creator", received.creator) + .build() } } @@ -327,4 +332,48 @@ mod tests { }; assert_eq!(message, "Desc element present twice for the same xml:lang."); } + + #[test] + fn test_received() { + let elem: Element = "".parse().unwrap(); + let received = Received::try_from(elem).unwrap(); + assert_eq!(received.name, String::from("coucou")); + assert_eq!(received.creator, Creator::Initiator); + let elem2 = Element::from(received.clone()); + let received2 = Received::try_from(elem2).unwrap(); + assert_eq!(received2.name, String::from("coucou")); + assert_eq!(received2.creator, Creator::Initiator); + + let elem: Element = "".parse().unwrap(); + let error = Received::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in received element."); + + let elem: Element = "".parse().unwrap(); + let error = Received::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in received element."); + + let elem: Element = "".parse().unwrap(); + let error = Received::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'name' missing."); + + let elem: Element = "".parse().unwrap(); + let error = Received::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown value for 'creator' attribute."); + } } From db9ef3ef305cedb2e2de6bf83f1487cc505d9a3e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 16:11:09 +0000 Subject: [PATCH 345/698] =?UTF-8?q?jingle=5Fft:=20Use=20jingle=E2=80=99s?= =?UTF-8?q?=20ContentId=20type=20to=20identify=20content=20names.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/jingle_ft.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 97ad5cd4707aacc169754f8dfbff6fafdef3ee06..3558d3022b790791ddbe00bacf0df52801a50162 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -10,7 +10,7 @@ use std::collections::BTreeMap; use std::str::FromStr; use hashes::Hash; -use jingle::Creator; +use jingle::{Creator, ContentId}; use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; use chrono::{DateTime, FixedOffset}; @@ -61,14 +61,14 @@ pub struct Description { #[derive(Debug, Clone)] pub struct Checksum { - pub name: String, + pub name: ContentId, pub creator: Creator, pub file: File, } #[derive(Debug, Clone)] pub struct Received { - pub name: String, + pub name: ContentId, pub creator: Creator, } @@ -337,11 +337,11 @@ mod tests { fn test_received() { let elem: Element = "".parse().unwrap(); let received = Received::try_from(elem).unwrap(); - assert_eq!(received.name, String::from("coucou")); + assert_eq!(received.name, ContentId(String::from("coucou"))); assert_eq!(received.creator, Creator::Initiator); let elem2 = Element::from(received.clone()); let received2 = Received::try_from(elem2).unwrap(); - assert_eq!(received2.name, String::from("coucou")); + assert_eq!(received2.name, ContentId(String::from("coucou"))); assert_eq!(received2.creator, Creator::Initiator); let elem: Element = "".parse().unwrap(); From cf31506580c8d1fd646e7a09d0d70cae144c24d7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 16:25:01 +0000 Subject: [PATCH 346/698] jingle_ft: Split Description::try_from into File::try_from. --- src/jingle_ft.rs | 245 ++++++++++++++++++++++++----------------------- 1 file changed, 125 insertions(+), 120 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 3558d3022b790791ddbe00bacf0df52801a50162..5cc739c878327aa84e903bf1efcf983cc30a9772 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -54,134 +54,76 @@ pub struct File { pub hashes: Vec, } -#[derive(Debug, Clone)] -pub struct Description { - pub file: File, -} - -#[derive(Debug, Clone)] -pub struct Checksum { - pub name: ContentId, - pub creator: Creator, - pub file: File, -} - -#[derive(Debug, Clone)] -pub struct Received { - pub name: ContentId, - pub creator: Creator, -} - -impl TryFrom for Received { +impl TryFrom for File { type Err = Error; - fn try_from(elem: Element) -> Result { - check_self!(elem, "received", ns::JINGLE_FT); - check_no_children!(elem, "received"); - check_no_unknown_attributes!(elem, "received", ["name", "creator"]); - Ok(Received { - name: get_attr!(elem, "name", required), - creator: get_attr!(elem, "creator", required), - }) - } -} - -impl From for Element { - fn from(received: Received) -> Element { - Element::builder("received") - .ns(ns::JINGLE_FT) - .attr("name", received.name) - .attr("creator", received.creator) - .build() - } -} - -impl TryFrom for Description { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("description", ns::JINGLE_FT) { - return Err(Error::ParseError("This is not a JingleFT description element.")); - } - if elem.children().count() != 1 { - return Err(Error::ParseError("JingleFT description element must have exactly one child.")); - } + fn try_from(elem: Element) -> Result { + check_self!(elem, "file", ns::JINGLE_FT); + check_no_attributes!(elem, "file"); + + let mut file = File { + date: None, + media_type: None, + name: None, + descs: BTreeMap::new(), + size: None, + range: None, + hashes: vec!(), + }; - let mut date = None; - let mut media_type = None; - let mut name = None; - let mut descs = BTreeMap::new(); - let mut size = None; - let mut range = None; - let mut hashes = vec!(); - for description_payload in elem.children() { - if !description_payload.is("file", ns::JINGLE_FT) { - return Err(Error::ParseError("Unknown element in JingleFT description.")); - } - for file_payload in description_payload.children() { - if file_payload.is("date", ns::JINGLE_FT) { - if date.is_some() { - return Err(Error::ParseError("File must not have more than one date.")); - } - date = Some(file_payload.text().parse()?); - } else if file_payload.is("media-type", ns::JINGLE_FT) { - if media_type.is_some() { - return Err(Error::ParseError("File must not have more than one media-type.")); - } - media_type = Some(file_payload.text()); - } else if file_payload.is("name", ns::JINGLE_FT) { - if name.is_some() { - return Err(Error::ParseError("File must not have more than one name.")); - } - name = Some(file_payload.text()); - } else if file_payload.is("desc", ns::JINGLE_FT) { - let lang = get_attr!(file_payload, "xml:lang", default); - let desc = Desc(file_payload.text()); - if descs.insert(lang, desc).is_some() { - return Err(Error::ParseError("Desc element present twice for the same xml:lang.")); - } - } else if file_payload.is("size", ns::JINGLE_FT) { - if size.is_some() { - return Err(Error::ParseError("File must not have more than one size.")); - } - size = Some(file_payload.text().parse()?); - } else if file_payload.is("range", ns::JINGLE_FT) { - if range.is_some() { - return Err(Error::ParseError("File must not have more than one range.")); - } - let offset = get_attr!(file_payload, "offset", default); - let length = get_attr!(file_payload, "length", optional); - let mut range_hashes = vec!(); - for hash_element in file_payload.children() { - if !hash_element.is("hash", ns::HASHES) { - return Err(Error::ParseError("Unknown element in JingleFT range.")); - } - range_hashes.push(Hash::try_from(hash_element.clone())?); + for child in elem.children() { + if child.is("date", ns::JINGLE_FT) { + if file.date.is_some() { + return Err(Error::ParseError("File must not have more than one date.")); + } + file.date = Some(child.text().parse()?); + } else if child.is("media-type", ns::JINGLE_FT) { + if file.media_type.is_some() { + return Err(Error::ParseError("File must not have more than one media-type.")); + } + file.media_type = Some(child.text()); + } else if child.is("name", ns::JINGLE_FT) { + if file.name.is_some() { + return Err(Error::ParseError("File must not have more than one name.")); + } + file.name = Some(child.text()); + } else if child.is("desc", ns::JINGLE_FT) { + let lang = get_attr!(child, "xml:lang", default); + let desc = Desc(child.text()); + if file.descs.insert(lang, desc).is_some() { + return Err(Error::ParseError("Desc element present twice for the same xml:lang.")); + } + } else if child.is("size", ns::JINGLE_FT) { + if file.size.is_some() { + return Err(Error::ParseError("File must not have more than one size.")); + } + file.size = Some(child.text().parse()?); + } else if child.is("range", ns::JINGLE_FT) { + if file.range.is_some() { + return Err(Error::ParseError("File must not have more than one range.")); + } + let offset = get_attr!(child, "offset", default); + let length = get_attr!(child, "length", optional); + let mut range_hashes = vec!(); + for hash_element in child.children() { + if !hash_element.is("hash", ns::HASHES) { + return Err(Error::ParseError("Unknown element in JingleFT range.")); } - range = Some(Range { - offset: offset, - length: length, - hashes: range_hashes, - }); - } else if file_payload.is("hash", ns::HASHES) { - hashes.push(Hash::try_from(file_payload.clone())?); - } else { - return Err(Error::ParseError("Unknown element in JingleFT file.")); + range_hashes.push(Hash::try_from(hash_element.clone())?); } + file.range = Some(Range { + offset: offset, + length: length, + hashes: range_hashes, + }); + } else if child.is("hash", ns::HASHES) { + file.hashes.push(Hash::try_from(child.clone())?); + } else { + return Err(Error::ParseError("Unknown element in JingleFT file.")); } } - Ok(Description { - file: File { - date: date, - media_type: media_type, - name: name, - descs: descs, - size: size, - range: range, - hashes: hashes, - }, - }) + Ok(file) } } @@ -233,6 +175,32 @@ impl From for Element { root } } +#[derive(Debug, Clone)] +pub struct Description { + pub file: File, +} + +impl TryFrom for Description { + type Err = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "description", ns::JINGLE_FT, "JingleFT description"); + check_no_attributes!(elem, "JingleFT description"); + let mut file = None; + for child in elem.children() { + if file.is_some() { + return Err(Error::ParseError("JingleFT description element must have exactly one child.")); + } + file = Some(File::try_from(child.clone())?); + } + if file.is_none() { + return Err(Error::ParseError("JingleFT description element must have exactly one child.")); + } + Ok(Description { + file: file.unwrap(), + }) + } +} impl From for Element { fn from(description: Description) -> Element { @@ -244,6 +212,43 @@ impl From for Element { } } +#[derive(Debug, Clone)] +pub struct Checksum { + pub name: ContentId, + pub creator: Creator, + pub file: File, +} + +#[derive(Debug, Clone)] +pub struct Received { + pub name: ContentId, + pub creator: Creator, +} + +impl TryFrom for Received { + type Err = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "received", ns::JINGLE_FT); + check_no_children!(elem, "received"); + check_no_unknown_attributes!(elem, "received", ["name", "creator"]); + Ok(Received { + name: get_attr!(elem, "name", required), + creator: get_attr!(elem, "creator", required), + }) + } +} + +impl From for Element { + fn from(received: Received) -> Element { + Element::builder("received") + .ns(ns::JINGLE_FT) + .attr("name", received.name) + .attr("creator", received.creator) + .build() + } +} + #[cfg(test)] mod tests { use super::*; From 667ae4c40f52f798a3b7c7a8d3ea60a0e7d78cb1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 16:41:22 +0000 Subject: [PATCH 347/698] jingle_ft: Implement checksum parsers. --- src/jingle_ft.rs | 82 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 5cc739c878327aa84e903bf1efcf983cc30a9772..3ee4e4a9d7975997e2b8f1874c14b0c47908446d 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -219,6 +219,41 @@ pub struct Checksum { pub file: File, } +impl TryFrom for Checksum { + type Err = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "checksum", ns::JINGLE_FT); + check_no_unknown_attributes!(elem, "checksum", ["name", "creator"]); + let mut file = None; + for child in elem.children() { + if file.is_some() { + return Err(Error::ParseError("JingleFT checksum element must have exactly one child.")); + } + file = Some(File::try_from(child.clone())?); + } + if file.is_none() { + return Err(Error::ParseError("JingleFT checksum element must have exactly one child.")); + } + Ok(Checksum { + name: get_attr!(elem, "name", required), + creator: get_attr!(elem, "creator", required), + file: file.unwrap(), + }) + } +} + +impl From for Element { + fn from(checksum: Checksum) -> Element { + Element::builder("checksum") + .ns(ns::JINGLE_FT) + .attr("name", checksum.name) + .attr("creator", checksum.creator) + .append(checksum.file) + .build() + } +} + #[derive(Debug, Clone)] pub struct Received { pub name: ContentId, @@ -381,4 +416,51 @@ mod tests { }; assert_eq!(message, "Unknown value for 'creator' attribute."); } + + #[test] + fn test_checksum() { + let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); + let hash = vec!(195, 73, 156, 39, 41, 115, 10, 127, 128, 126, 251, 134, 118, 169, 45, 203, 111, 138, 63, 143); + let checksum = Checksum::try_from(elem).unwrap(); + assert_eq!(checksum.name, ContentId(String::from("coucou"))); + assert_eq!(checksum.creator, Creator::Initiator); + assert_eq!(checksum.file.hashes, vec!(Hash { algo: Algo::Sha_1, hash: hash.clone() })); + let elem2 = Element::from(checksum); + let checksum2 = Checksum::try_from(elem2).unwrap(); + assert_eq!(checksum2.name, ContentId(String::from("coucou"))); + assert_eq!(checksum2.creator, Creator::Initiator); + assert_eq!(checksum2.file.hashes, vec!(Hash { algo: Algo::Sha_1, hash: hash.clone() })); + + let elem: Element = "".parse().unwrap(); + let error = Checksum::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "This is not a file element."); + + let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); + let error = Checksum::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in checksum element."); + + let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); + let error = Checksum::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'name' missing."); + + let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); + let error = Checksum::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown value for 'creator' attribute."); + } } From 97abc37f1a0a78bdcf77d9a416a05ed2f547a817 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 17:02:24 +0000 Subject: [PATCH 348/698] jingle_ft: Improve parsing. --- src/jingle_ft.rs | 81 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 56 insertions(+), 25 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 3ee4e4a9d7975997e2b8f1874c14b0c47908446d..7e13351f8a96de92f578241d0197d8e624aeaa95 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -12,7 +12,7 @@ use std::str::FromStr; use hashes::Hash; use jingle::{Creator, ContentId}; -use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; +use minidom::{Element, IntoAttributeValue}; use chrono::{DateTime, FixedOffset}; use error::Error; @@ -25,17 +25,32 @@ pub struct Range { pub hashes: Vec, } -impl IntoElements for Range { - fn into_elements(self, emitter: &mut ElementEmitter) { - let mut elem = Element::builder("range") - .ns(ns::JINGLE_FT) - .attr("offset", if self.offset == 0 { None } else { Some(self.offset) }) - .attr("length", self.length) - .build(); - for hash in self.hashes { - elem.append_child(hash.into()); +impl TryFrom for Range { + type Err = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "range", ns::JINGLE_FT); + check_no_unknown_attributes!(elem, "range", ["offset", "length"]); + let mut hashes = vec!(); + for child in elem.children() { + hashes.push(Hash::try_from(child.clone())?); } - emitter.append_child(elem); + Ok(Range { + offset: get_attr!(elem, "offset", default), + length: get_attr!(elem, "length", optional), + hashes: hashes, + }) + } +} + +impl From for Element { + fn from(range: Range) -> Element { + Element::builder("range") + .ns(ns::JINGLE_FT) + .attr("offset", if range.offset == 0 { None } else { Some(range.offset) }) + .attr("length", range.length) + .append(range.hashes) + .build() } } @@ -102,20 +117,7 @@ impl TryFrom for File { if file.range.is_some() { return Err(Error::ParseError("File must not have more than one range.")); } - let offset = get_attr!(child, "offset", default); - let length = get_attr!(child, "length", optional); - let mut range_hashes = vec!(); - for hash_element in child.children() { - if !hash_element.is("hash", ns::HASHES) { - return Err(Error::ParseError("Unknown element in JingleFT range.")); - } - range_hashes.push(Hash::try_from(hash_element.clone())?); - } - file.range = Some(Range { - offset: offset, - length: length, - hashes: range_hashes, - }); + file.range = Some(Range::try_from(child.clone())?); } else if child.is("hash", ns::HASHES) { file.hashes.push(Hash::try_from(child.clone())?); } else { @@ -463,4 +465,33 @@ mod tests { }; assert_eq!(message, "Unknown value for 'creator' attribute."); } + + #[test] + fn test_range() { + let elem: Element = "".parse().unwrap(); + let range = Range::try_from(elem).unwrap(); + assert_eq!(range.offset, 0); + assert_eq!(range.length, None); + assert_eq!(range.hashes, vec!()); + + let elem: Element = "kHp5RSzW/h7Gm1etSf90Mr5PC/k=".parse().unwrap(); + let hashes = vec!(Hash { algo: Algo::Sha_1, hash: vec!(144, 122, 121, 69, 44, 214, 254, 30, 198, 155, 87, 173, 73, 255, 116, 50, 190, 79, 11, 249) }); + let range = Range::try_from(elem).unwrap(); + assert_eq!(range.offset, 2048); + assert_eq!(range.length, Some(1024)); + assert_eq!(range.hashes, hashes); + let elem2 = Element::from(range); + let range2 = Range::try_from(elem2).unwrap(); + assert_eq!(range2.offset, 2048); + assert_eq!(range2.length, Some(1024)); + assert_eq!(range2.hashes, hashes); + + let elem: Element = "".parse().unwrap(); + let error = Range::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in range element."); + } } From 7fa889f46eab947914388749feec85f29065d5e9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 17:17:06 +0000 Subject: [PATCH 349/698] jingle: Import the disposition attribute values. --- src/jingle.rs | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index 9d142612e5b74a15606dd8d525556d4a706fe580..c9737b03fd3fd58349b8f5bca5ce901e7b0f738c 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -43,16 +43,27 @@ generate_attribute!(Senders, "senders", { Responder => "responder", }, Default = Both); -generate_id!(ContentId); - -// TODO: the list of values is defined, use an enum! -generate_id!(Disposition); +// From https://www.iana.org/assignments/cont-disp/cont-disp.xhtml +generate_attribute!(Disposition, "disposition", { + Inline => "inline", + Attachment => "attachment", + FormData => "form-data", + Signal => "signal", + Alert => "alert", + Icon => "icon", + Render => "render", + RecipientListHistory => "recipient-list-history", + Session => "session", + Aib => "aib", + EarlySession => "early-session", + RecipientList => "recipient-list", + Notification => "notification", + ByReference => "by-reference", + InfoPackage => "info-package", + RecordingSession => "recording-session", +}, Default = Session); -impl Default for Disposition { - fn default() -> Disposition { - Disposition(String::from("session")) - } -} +generate_id!(ContentId); #[derive(Debug, Clone)] pub struct Content { @@ -353,7 +364,7 @@ mod tests { assert_eq!(jingle.contents[0].creator, Creator::Initiator); assert_eq!(jingle.contents[0].name, ContentId(String::from("coucou"))); assert_eq!(jingle.contents[0].senders, Senders::Both); - assert_eq!(jingle.contents[0].disposition, Disposition(String::from("session"))); + assert_eq!(jingle.contents[0].disposition, Disposition::Session); let elem: Element = "".parse().unwrap(); let jingle = Jingle::try_from(elem).unwrap(); @@ -361,7 +372,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let jingle = Jingle::try_from(elem).unwrap(); - assert_eq!(jingle.contents[0].disposition, Disposition(String::from("early-session"))); + assert_eq!(jingle.contents[0].disposition, Disposition::EarlySession); } #[test] From b5e41a70742730de5a03bb594f8346260fdf0164 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 17:55:27 +0000 Subject: [PATCH 350/698] Fix the name in create_empty_element. --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 7eba6799152f8e2e59c371fb60d5a8bab0b672b4..ccc84f022575287748020885e952882d986750be 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -195,7 +195,7 @@ macro_rules! generate_empty_element { impl From<$elem> for Element { fn from(_: $elem) -> Element { - Element::builder("attention") + Element::builder($name) .ns($ns) .build() } From ed53f452c37164d3235bf9c24f96cf62f51e94e5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 17:57:49 +0000 Subject: [PATCH 351/698] ping: Add a serialise test. --- src/ping.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ping.rs b/src/ping.rs index d4044f477d8cd534dbe7ce68356cbe81e010126a..fa696d08bbc472cff36cc40a83d83eb98f3af2d7 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -25,6 +25,13 @@ mod tests { Ping::try_from(elem).unwrap(); } + #[test] + fn test_serialise() { + let elem1 = Element::from(Ping); + let elem2: Element = "".parse().unwrap(); + assert_eq!(elem1, elem2); + } + #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); From 929bd577f328bf5fa3866ff56195732038e6b626 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 17:58:11 +0000 Subject: [PATCH 352/698] Add a generate_element_with_only_attributes macro, and use it wherever it makes sense. --- src/eme.rs | 39 ++++------------------- src/ibb.rs | 72 +++++------------------------------------- src/jingle_ft.rs | 33 +++---------------- src/jingle_ibb.rs | 40 +++-------------------- src/jingle_s5b.rs | 31 +++++------------- src/lib.rs | 40 +++++++++++++++++++++++ src/message_correct.rs | 28 ++-------------- src/muc/user.rs | 39 +++-------------------- src/receipts.rs | 65 +++----------------------------------- src/stanza_id.rs | 70 +++++----------------------------------- 10 files changed, 92 insertions(+), 365 deletions(-) diff --git a/src/eme.rs b/src/eme.rs index ad4b437f8098a5cc18551277f7b03ce4eebf4210..540b50730df1036c450f80478b44fd77fa1738ca 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -13,39 +13,14 @@ use error::Error; use ns; /// Structure representing an `` element. -#[derive(Debug, Clone)] -pub struct ExplicitMessageEncryption { - /// Namespace of the encryption scheme used. - pub namespace: String, +generate_element_with_only_attributes!(ExplicitMessageEncryption, "encryption", ns::EME, [ + // Namespace of the encryption scheme used. + namespace: String = "namespace" => required, - /// User-friendly name for the encryption scheme, should be `None` for OTR, - /// legacy OpenPGP and OX. - pub name: Option, -} - -impl TryFrom for ExplicitMessageEncryption { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "encryption", ns::EME); - check_no_children!(elem, "encryption"); - check_no_unknown_attributes!(elem, "encryption", ["namespace", "name"]); - Ok(ExplicitMessageEncryption { - namespace: get_attr!(elem, "namespace", required), - name: get_attr!(elem, "name", optional), - }) - } -} - -impl From for Element { - fn from(eme: ExplicitMessageEncryption) -> Element { - Element::builder("encryption") - .ns(ns::EME) - .attr("namespace", eme.namespace) - .attr("name", eme.name) - .build() - } -} + // User-friendly name for the encryption scheme, should be `None` for OTR, + // legacy OpenPGP and OX. + name: Option = "name" => optional, +]); #[cfg(test)] mod tests { diff --git a/src/ibb.rs b/src/ibb.rs index 2c01e1c741aa9785c5a308471f5bbfea2ed9e9d5..7d7f3e73fa501e4a7ffd63e1586bac52ba8532ee 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -19,41 +19,11 @@ generate_attribute!(Stanza, "stanza", { Message => "message", }, Default = Iq); -#[derive(Debug, Clone)] -pub struct Open { - pub block_size: u16, - pub sid: String, - pub stanza: Stanza, -} - -impl TryFrom for Open { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("open", ns::IBB) { - return Err(Error::ParseError("This is not an open element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in open element.")); - } - Ok(Open { - block_size: get_attr!(elem, "block-size", required), - sid: get_attr!(elem, "sid", required), - stanza: get_attr!(elem, "stanza", default), - }) - } -} - -impl From for Element { - fn from(open: Open) -> Element { - Element::builder("open") - .ns(ns::IBB) - .attr("block-size", open.block_size) - .attr("sid", open.sid) - .attr("stanza", open.stanza) - .build() - } -} +generate_element_with_only_attributes!(Open, "open", ns::IBB, [ + block_size: u16 = "block-size" => required, + sid: String = "sid" => required, + stanza: Stanza = "stanza" => default, +]); #[derive(Debug, Clone)] pub struct Data { @@ -91,35 +61,9 @@ impl From for Element { } } -#[derive(Debug, Clone)] -pub struct Close { - pub sid: String, -} - -impl TryFrom for Close { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("close", ns::IBB) { - return Err(Error::ParseError("This is not a close element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in close element.")); - } - Ok(Close { - sid: get_attr!(elem, "sid", required), - }) - } -} - -impl From for Element { - fn from(close: Close) -> Element { - Element::builder("close") - .ns(ns::IBB) - .attr("sid", close.sid) - .build() - } -} +generate_element_with_only_attributes!(Close, "close", ns::IBB, [ + sid: String = "sid" => required, +]); #[cfg(test)] mod tests { diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 7e13351f8a96de92f578241d0197d8e624aeaa95..15a21c67efa16c70df390cb3774b4d3d505a59de 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -256,35 +256,10 @@ impl From for Element { } } -#[derive(Debug, Clone)] -pub struct Received { - pub name: ContentId, - pub creator: Creator, -} - -impl TryFrom for Received { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "received", ns::JINGLE_FT); - check_no_children!(elem, "received"); - check_no_unknown_attributes!(elem, "received", ["name", "creator"]); - Ok(Received { - name: get_attr!(elem, "name", required), - creator: get_attr!(elem, "creator", required), - }) - } -} - -impl From for Element { - fn from(received: Received) -> Element { - Element::builder("received") - .ns(ns::JINGLE_FT) - .attr("name", received.name) - .attr("creator", received.creator) - .build() - } -} +generate_element_with_only_attributes!(Received, "received", ns::JINGLE_FT, [ + name: ContentId = "name" => required, + creator: Creator = "creator" => required, +]); #[cfg(test)] mod tests { diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index ed1c6d189ec5c5662aa4aab3682bd58cde9223a9..632540ed2cc909d22c3625a5e621a1c6565ab498 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -17,41 +17,11 @@ use ibb::Stanza; generate_id!(StreamId); -#[derive(Debug, Clone)] -pub struct Transport { - pub block_size: u16, - pub sid: StreamId, - pub stanza: Stanza, -} - -impl TryFrom for Transport { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("transport", ns::JINGLE_IBB) { - return Err(Error::ParseError("This is not an JingleIBB element.")) - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in JingleIBB element.")); - } - Ok(Transport { - block_size: get_attr!(elem, "block-size", required), - sid: get_attr!(elem, "sid", required), - stanza: get_attr!(elem, "stanza", default), - }) - } -} - -impl From for Element { - fn from(transport: Transport) -> Element { - Element::builder("transport") - .ns(ns::JINGLE_IBB) - .attr("block-size", transport.block_size) - .attr("sid", transport.sid) - .attr("stanza", transport.stanza) - .build() - } -} +generate_element_with_only_attributes!(Transport, "transport", ns::JINGLE_IBB, [ + block_size: u16 = "block-size" => required, + sid: StreamId = "sid" => required, + stanza: Stanza = "stanza" => default, +]); #[cfg(test)] mod tests { diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 6f8bb902106ebcdbdd1dedae5f00fda9c57d53da..f1396c5df4d1d1c1fdf032fa18c324a17cba229e 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -30,29 +30,14 @@ generate_id!(CandidateId); generate_id!(StreamId); -#[derive(Debug, Clone)] -pub struct Candidate { - pub cid: CandidateId, - pub host: String, - pub jid: Jid, - pub port: Option, - pub priority: u32, - pub type_: Type, -} - -impl From for Element { - fn from(candidate: Candidate) -> Element { - Element::builder("candidate") - .ns(ns::JINGLE_S5B) - .attr("cid", candidate.cid) - .attr("host", candidate.host) - .attr("jid", String::from(candidate.jid)) - .attr("port", candidate.port) - .attr("priority", candidate.priority) - .attr("type", candidate.type_) - .build() - } -} +generate_element_with_only_attributes!(Candidate, "candidate", ns::JINGLE_S5B, [ + cid: CandidateId = "cid" => required, + host: String = "host" => required, + jid: Jid = "jid" => required, + port: Option = "port" => optional, + priority: u32 = "priority" => required, + type_: Type = "type" => default, +]); #[derive(Debug, Clone)] pub enum TransportPayload { diff --git a/src/lib.rs b/src/lib.rs index ccc84f022575287748020885e952882d986750be..5e767fc20acc13ebb29e75c3cc458985324065e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -203,6 +203,46 @@ macro_rules! generate_empty_element { ); } +macro_rules! generate_element_with_only_attributes { + ($elem:ident, $name:tt, $ns:expr, [$($attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => ( + generate_element_with_only_attributes!($elem, $name, $ns, [$($attr: $attr_type = $attr_name => $attr_action),*]); + ); + ($elem:ident, $name:tt, $ns:expr, [$($attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => ( + #[derive(Debug, Clone)] + pub struct $elem { + $( + pub $attr: $attr_type + ),* + } + + impl TryFrom for $elem { + type Err = Error; + + fn try_from(elem: Element) -> Result<$elem, Error> { + check_self!(elem, $name, $ns); + check_no_children!(elem, $name); + check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); + Ok($elem { + $( + $attr: get_attr!(elem, $attr_name, $attr_action) + ),* + }) + } + } + + impl From<$elem> for Element { + fn from(elem: $elem) -> Element { + Element::builder($name) + .ns($ns) + $( + .attr($attr_name, elem.$attr) + )* + .build() + } + } + ); +} + macro_rules! generate_id { ($elem:ident) => ( #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/src/message_correct.rs b/src/message_correct.rs index d8200ae19b07401e21a548a6e0b0018e7e4efa1e..26cdcf99596b8c79d8403dabbe108f46e8800d02 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -12,31 +12,9 @@ use error::Error; use ns; -#[derive(Debug, Clone)] -pub struct Replace { - pub id: String, -} - -impl TryFrom for Replace { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "replace", ns::MESSAGE_CORRECT); - check_no_children!(elem, "replace"); - check_no_unknown_attributes!(elem, "replace", ["id"]); - let id = get_attr!(elem, "id", required); - Ok(Replace { id }) - } -} - -impl From for Element { - fn from(replace: Replace) -> Element { - Element::builder("replace") - .ns(ns::MESSAGE_CORRECT) - .attr("id", replace.id) - .build() - } -} +generate_element_with_only_attributes!(Replace, "replace", ns::MESSAGE_CORRECT, [ + id: String = "id" => required, +]); #[cfg(test)] mod tests { diff --git a/src/muc/user.rs b/src/muc/user.rs index 322525cbe2a3cadbe0a292f23f401947d8c15b3f..fb30967993d0d417959a373a168f9a98232590fd 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -192,38 +192,9 @@ impl From for Element { } } -#[derive(Debug, Clone, PartialEq)] -pub struct Continue { - thread: Option, -} - -impl TryFrom for Continue { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("continue", ns::MUC_USER) { - return Err(Error::ParseError("This is not a continue element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in continue element.")); - } - for (attr, _) in elem.attrs() { - if attr != "thread" { - return Err(Error::ParseError("Unknown attribute in continue element.")); - } - } - Ok(Continue { thread: get_attr!(elem, "thread", optional) }) - } -} - -impl From for Element { - fn from(cont: Continue) -> Element { - Element::builder("continue") - .ns(ns::MUC_USER) - .attr("thread", cont.thread) - .build() - } -} +generate_element_with_only_attributes!(Continue, "continue", ns::MUC_USER, [ + thread: Option = "thread" => optional, +]); #[derive(Debug, Clone, PartialEq)] pub struct Reason(String); @@ -576,7 +547,7 @@ mod tests { thread='foo'/> ".parse().unwrap(); let continue_ = Continue::try_from(elem).unwrap(); - assert_eq!(continue_, Continue { thread: Some("foo".to_owned()) }); + assert_eq!(continue_.thread, Some("foo".to_owned())); } #[test] @@ -725,7 +696,7 @@ mod tests { let item = Item::try_from(elem).unwrap(); let continue_1 = Continue { thread: Some("foobar".to_owned()) }; match item { - Item { continue_: Some(continue_2), .. } => assert_eq!(continue_2, continue_1), + Item { continue_: Some(continue_2), .. } => assert_eq!(continue_2.thread, continue_1.thread), _ => panic!(), } } diff --git a/src/receipts.rs b/src/receipts.rs index 6ccec7eae1f500062175bfdc776eb5e3ac178106..470a46474dde9241e8fc74570fa186429aa0ace9 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -12,68 +12,11 @@ use error::Error; use ns; -#[derive(Debug, Clone)] -pub struct Request; +generate_empty_element!(Request, "request", ns::RECEIPTS); -impl TryFrom for Request { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("request", ns::RECEIPTS) { - return Err(Error::ParseError("This is not a request element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in request element.")); - } - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown attribute in request element.")); - } - Ok(Request) - } -} - -impl From for Element { - fn from(_: Request) -> Element { - Element::builder("request") - .ns(ns::RECEIPTS) - .build() - } -} - -#[derive(Debug, Clone)] -pub struct Received { - pub id: Option, -} - -impl TryFrom for Received { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("received", ns::RECEIPTS) { - return Err(Error::ParseError("This is not a received element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in received element.")); - } - for (attr, _) in elem.attrs() { - if attr != "id" { - return Err(Error::ParseError("Unknown attribute in received element.")); - } - } - Ok(Received { - id: get_attr!(elem, "id", optional), - }) - } -} - -impl From for Element { - fn from(received: Received) -> Element { - Element::builder("received") - .ns(ns::RECEIPTS) - .attr("id", received.id) - .build() - } -} +generate_element_with_only_attributes!(Received, "received", ns::RECEIPTS, [ + id: Option = "id" => optional, +]); #[cfg(test)] mod tests { diff --git a/src/stanza_id.rs b/src/stanza_id.rs index feeeadbe0328d00411e776d557227ed9dc7439c4..6e65e012b885d13e0b1458b3687f710743a21746 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -13,68 +13,14 @@ use error::Error; use ns; -#[derive(Debug, Clone)] -pub struct StanzaId { - pub id: String, - pub by: Jid, -} - -impl TryFrom for StanzaId { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("stanza-id", ns::SID) { - return Err(Error::ParseError("This is not a stanza-id element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in stanza-id element.")); - } - Ok(StanzaId { - id: get_attr!(elem, "id", required), - by: get_attr!(elem, "by", required), - }) - } -} - -impl From for Element { - fn from(stanza_id: StanzaId) -> Element { - Element::builder("stanza-id") - .ns(ns::SID) - .attr("id", stanza_id.id) - .attr("by", stanza_id.by) - .build() - } -} - -#[derive(Debug, Clone)] -pub struct OriginId { - pub id: String, -} - -impl TryFrom for OriginId { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("origin-id", ns::SID) { - return Err(Error::ParseError("This is not an origin-id element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in origin-id element.")); - } - Ok(OriginId { - id: get_attr!(elem, "id", required), - }) - } -} - -impl From for Element { - fn from(origin_id: OriginId) -> Element { - Element::builder("origin-id") - .ns(ns::SID) - .attr("id", origin_id.id) - .build() - } -} +generate_element_with_only_attributes!(StanzaId, "stanza-id", ns::SID, [ + id: String = "id" => required, + by: Jid = "by" => required, +]); + +generate_element_with_only_attributes!(OriginId, "origin-id", ns::SID, [ + id: String = "id" => required, +]); #[cfg(test)] mod tests { From 61badccf8b07328f2cb0074718c4a69bebbd5f73 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 18:37:32 +0000 Subject: [PATCH 353/698] Add meta support to macros. --- src/lib.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5e767fc20acc13ebb29e75c3cc458985324065e6..2a63302cf100c0adc4697ca0cace696d17ebc0b2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -174,11 +174,8 @@ macro_rules! check_no_unknown_attributes { } macro_rules! generate_empty_element { - ($elem:ident, $name:tt, $ns:expr) => ( - // TODO: Find a better way to concatenate doc. - #[doc="Structure representing a "] - #[doc=$name] - #[doc=" element."] + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr) => ( + $(#[$meta])* #[derive(Debug, Clone)] pub struct $elem; @@ -204,13 +201,15 @@ macro_rules! generate_empty_element { } macro_rules! generate_element_with_only_attributes { - ($elem:ident, $name:tt, $ns:expr, [$($attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => ( - generate_element_with_only_attributes!($elem, $name, $ns, [$($attr: $attr_type = $attr_name => $attr_action),*]); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => ( + generate_element_with_only_attributes!($(#[$meta])* $elem, $name, $ns, [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*]); ); - ($elem:ident, $name:tt, $ns:expr, [$($attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => ( + $(#[$meta])* #[derive(Debug, Clone)] pub struct $elem { $( + $(#[$attr_meta])* pub $attr: $attr_type ),* } From 649286d59e701c5d836f3daa3a9d71ba52413be2 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 18:49:22 +0000 Subject: [PATCH 354/698] disco: Use generate_element_with_only_attributes. --- src/disco.rs | 129 ++++++++------------------------------------------- 1 file changed, 19 insertions(+), 110 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index 2c3189f06f42d542537d80024c1923d33aacfe78..9964cd8c9e625e8592b3ba4587db60c3ac6a0ae3 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -16,66 +16,23 @@ use ns; use data_forms::{DataForm, DataFormType}; +generate_element_with_only_attributes!( /// Structure representing a `` element. /// /// It should only be used in an ``, as it can only represent /// the request, and not a result. -#[derive(Debug, Clone)] -pub struct DiscoInfoQuery { +DiscoInfoQuery, "query", ns::DISCO_INFO, [ /// Node on which we are doing the discovery. - pub node: Option, -} - -impl TryFrom for DiscoInfoQuery { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "query", ns::DISCO_INFO); - check_no_children!(elem, "query"); - check_no_unknown_attributes!(elem, "query", ["node"]); - Ok(DiscoInfoQuery { - node: get_attr!(elem, "node", optional), - }) - } -} - -impl From for Element { - fn from(disco: DiscoInfoQuery) -> Element { - Element::builder("query") - .ns(ns::DISCO_INFO) - .attr("node", disco.node) - .build() - } -} + node: Option = "node" => optional, +]); +generate_element_with_only_attributes!( /// Structure representing a `` element. -#[derive(Debug, Clone, PartialEq)] -pub struct Feature { +#[derive(PartialEq)] +Feature, "feature", ns::DISCO_INFO, [ /// Namespace of the feature we want to represent. - pub var: String, -} - -impl TryFrom for Feature { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "feature", ns::DISCO_INFO, "disco#info feature"); - check_no_children!(elem, "disco#info feature"); - check_no_unknown_attributes!(elem, "disco#info feature", ["var"]); - Ok(Feature { - var: get_attr!(elem, "var", required) - }) - } -} - -impl From for Element { - fn from(feature: Feature) -> Element { - Element::builder("feature") - .ns(ns::DISCO_INFO) - .attr("var", feature.var) - .build() - } -} + var: String = "var" => required, +]); /// Structure representing an `` element. #[derive(Debug, Clone)] @@ -223,74 +180,26 @@ impl From for Element { } } +generate_element_with_only_attributes!( /// Structure representing a `` element. /// /// It should only be used in an ``, as it can only represent /// the request, and not a result. -#[derive(Debug, Clone)] -pub struct DiscoItemsQuery { +DiscoItemsQuery, "query", ns::DISCO_ITEMS, [ /// Node on which we are doing the discovery. - pub node: Option, -} - -impl TryFrom for DiscoItemsQuery { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "query", ns::DISCO_ITEMS, "disco#items query"); - check_no_children!(elem, "disco#items query"); - check_no_unknown_attributes!(elem, "disco#items query", ["node"]); - Ok(DiscoItemsQuery { - node: get_attr!(elem, "node", optional), - }) - } -} - -impl From for Element { - fn from(disco: DiscoItemsQuery) -> Element { - Element::builder("query") - .ns(ns::DISCO_ITEMS) - .attr("node", disco.node) - .build() - } -} + node: Option = "node" => optional, +]); +generate_element_with_only_attributes!( /// Structure representing an `` element. -#[derive(Debug, Clone)] -pub struct Item { +Item, "item", ns::DISCO_ITEMS, [ /// JID of the entity pointed by this item. - pub jid: Jid, + jid: Jid = "jid" => required, /// Node of the entity pointed by this item. - pub node: Option, + node: Option = "node" => optional, /// Name of the entity pointed by this item. - pub name: Option, -} - -impl TryFrom for Item { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "item", ns::DISCO_ITEMS); - check_no_children!(elem, "item"); - check_no_unknown_attributes!(elem, "item", ["jid", "node", "name"]); - Ok(Item { - jid: get_attr!(elem, "jid", required), - node: get_attr!(elem, "node", optional), - name: get_attr!(elem, "name", optional), - }) - } -} - -impl From for Element { - fn from(item: Item) -> Element { - Element::builder("item") - .ns(ns::DISCO_ITEMS) - .attr("jid", item.jid) - .attr("node", item.node) - .attr("name", item.name) - .build() - } -} + name: Option = "name" => optional, +]); /// Structure representing a `` element. From 47c9263b8685688373e6338b322133c7d1a0bcec Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 18:53:51 +0000 Subject: [PATCH 355/698] blocking: Merge get_children_items into the try_from. --- src/blocking.rs | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/blocking.rs b/src/blocking.rs index 084844e67b732d4bd9c41f530ef7eab76ee1f574..997e24111ff536f32396a4befd46ffb2e6dbe850 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -15,17 +15,6 @@ use ns; generate_empty_element!(BlocklistRequest, "blocklist", ns::BLOCKING); -fn get_children_items(elem: Element) -> Result, Error> { - let mut items = vec!(); - for child in elem.children() { - check_self!(child, "item", ns::BLOCKING); - check_no_unknown_attributes!(child, "item", ["jid"]); - check_no_children!(child, "item"); - items.push(get_attr!(child, "jid", required)); - } - Ok(items) -} - macro_rules! generate_blocking_element { ($elem:ident, $name:tt) => ( #[derive(Debug, Clone)] @@ -39,9 +28,14 @@ macro_rules! generate_blocking_element { fn try_from(elem: Element) -> Result<$elem, Error> { check_self!(elem, $name, ns::BLOCKING); check_no_attributes!(elem, $name); - Ok($elem { - items: get_children_items(elem)?, - }) + let mut items = vec!(); + for child in elem.children() { + check_self!(child, "item", ns::BLOCKING); + check_no_unknown_attributes!(child, "item", ["jid"]); + check_no_children!(child, "item"); + items.push(get_attr!(child, "jid", required)); + } + Ok($elem { items }) } } From 82fcf3bad5f614cb3e014c3208bab442c019d551 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 19:07:49 +0000 Subject: [PATCH 356/698] ecaps2: Simplify parsing. --- src/ecaps2.rs | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 8166e5b0f3e525f4019c7628fd4d9f24ce0c3200..347d1dac8e71074fcee18191804a54f6fab64aad 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -30,17 +30,11 @@ impl TryFrom for ECaps2 { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("c", ns::ECAPS2) { - return Err(Error::ParseError("This is not an ecaps2 element.")); - } + check_self!(elem, "c", ns::ECAPS2, "ecaps2"); + check_no_attributes!(elem, "ecaps2"); let mut hashes = vec!(); for child in elem.children() { - if child.is("hash", ns::HASHES) { - let hash = Hash::try_from(child.clone())?; - hashes.push(hash); - } else { - return Err(Error::ParseError("Unknown child in ecaps2 element.")); - } + hashes.push(Hash::try_from(child.clone())?); } Ok(ECaps2 { hashes: hashes, @@ -52,9 +46,7 @@ impl From for Element { fn from(ecaps2: ECaps2) -> Element { Element::builder("c") .ns(ns::ECAPS2) - .append(ecaps2.hashes.into_iter() - .map(Element::from) - .collect::>()) + .append(ecaps2.hashes) .build() } } @@ -207,7 +199,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown child in ecaps2 element."); + assert_eq!(message, "This is not a hash element."); } #[test] From 5cdb2d77ab27b0b496a8506f2d7f19f451b909d1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 19:41:45 +0000 Subject: [PATCH 357/698] Split out DateTime parsing into its own module (implement XEP-0082). --- src/date.rs | 116 ++++++++++++++++++++++++++++++++++++++++++++ src/delay.rs | 22 +++------ src/idle.rs | 36 +++----------- src/jingle_ft.rs | 8 +-- src/lib.rs | 3 ++ src/pubsub/event.rs | 6 +-- 6 files changed, 139 insertions(+), 52 deletions(-) create mode 100644 src/date.rs diff --git a/src/date.rs b/src/date.rs new file mode 100644 index 0000000000000000000000000000000000000000..a344eb58d97dc4ad297de5fd471811c8594fd2f9 --- /dev/null +++ b/src/date.rs @@ -0,0 +1,116 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::str::FromStr; + +use minidom::{IntoAttributeValue, IntoElements, ElementEmitter}; +use chrono::{DateTime as ChronoDateTime, FixedOffset}; + +use error::Error; + +/// Implements the DateTime profile of XEP-0082, which represents a +/// non-recurring moment in time, with an accuracy of seconds or fraction of +/// seconds, and includes a timezone. +#[derive(Debug, Clone, PartialEq)] +pub struct DateTime(ChronoDateTime); + +impl FromStr for DateTime { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(DateTime(ChronoDateTime::parse_from_rfc3339(s)?)) + } +} + +impl IntoAttributeValue for DateTime { + fn into_attribute_value(self) -> Option { + Some(self.0.to_rfc3339()) + } +} + +impl IntoElements for DateTime { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_text_node(self.0.to_rfc3339()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use chrono::{Datelike, Timelike}; + use std::error::Error as StdError; + + #[test] + fn test_simple() { + let date: DateTime = "2002-09-10T23:08:25Z".parse().unwrap(); + assert_eq!(date.0.year(), 2002); + assert_eq!(date.0.month(), 9); + assert_eq!(date.0.day(), 10); + assert_eq!(date.0.hour(), 23); + assert_eq!(date.0.minute(), 08); + assert_eq!(date.0.second(), 25); + assert_eq!(date.0.nanosecond(), 0); + assert_eq!(date.0.timezone(), FixedOffset::east(0)); + } + + #[test] + fn test_invalid_date() { + // There is no thirteenth month. + let error = DateTime::from_str("2017-13-01T12:23:34Z").unwrap_err(); + let message = match error { + Error::ChronoParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message.description(), "input is out of range"); + + // Timezone ≥24:00 aren’t allowed. + let error = DateTime::from_str("2017-05-27T12:11:02+25:00").unwrap_err(); + let message = match error { + Error::ChronoParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message.description(), "input is out of range"); + + // Timezone without the : separator aren’t allowed. + let error = DateTime::from_str("2017-05-27T12:11:02+0100").unwrap_err(); + let message = match error { + Error::ChronoParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message.description(), "input contains invalid characters"); + + // No seconds, error message could be improved. + let error = DateTime::from_str("2017-05-27T12:11+01:00").unwrap_err(); + let message = match error { + Error::ChronoParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message.description(), "input contains invalid characters"); + + // TODO: maybe we’ll want to support this one, as per XEP-0082 §4. + let error = DateTime::from_str("20170527T12:11:02+01:00").unwrap_err(); + let message = match error { + Error::ChronoParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message.description(), "input contains invalid characters"); + + // No timezone. + let error = DateTime::from_str("2017-05-27T12:11:02").unwrap_err(); + let message = match error { + Error::ChronoParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message.description(), "premature end of input"); + } + + #[test] + fn test_serialise() { + let date = DateTime(ChronoDateTime::parse_from_rfc3339("2017-05-21T20:19:55+01:00").unwrap()); + let attr = date.into_attribute_value(); + assert_eq!(attr, Some(String::from("2017-05-21T20:19:55+01:00"))); + } +} diff --git a/src/delay.rs b/src/delay.rs index 97c10ad76423132acb7d86782cccb0e4bd02f819..53462792680991e7575a084a596a8e23af2f91dd 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -7,7 +7,7 @@ use try_from::TryFrom; use minidom::Element; -use chrono::{DateTime, FixedOffset}; +use date::DateTime; use error::Error; use jid::Jid; @@ -17,7 +17,7 @@ use ns; #[derive(Debug, Clone)] pub struct Delay { pub from: Option, - pub stamp: DateTime, + pub stamp: DateTime, pub data: Option, } @@ -32,7 +32,7 @@ impl TryFrom for Delay { return Err(Error::ParseError("Unknown child in delay element.")); } let from = get_attr!(elem, "from", optional); - let stamp = get_attr!(elem, "stamp", required, stamp, DateTime::parse_from_rfc3339(stamp)?); + let stamp = get_attr!(elem, "stamp", required); let data = match elem.text().as_ref() { "" => None, text => Some(text.to_owned()), @@ -50,7 +50,7 @@ impl From for Element { Element::builder("delay") .ns(ns::DELAY) .attr("from", delay.from) - .attr("stamp", delay.stamp.to_rfc3339()) + .attr("stamp", delay.stamp) .append(delay.data) .build() } @@ -60,21 +60,13 @@ impl From for Element { mod tests { use super::*; use std::str::FromStr; - use chrono::{Datelike, Timelike}; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); let delay = Delay::try_from(elem).unwrap(); assert_eq!(delay.from, Some(Jid::from_str("capulet.com").unwrap())); - assert_eq!(delay.stamp.year(), 2002); - assert_eq!(delay.stamp.month(), 9); - assert_eq!(delay.stamp.day(), 10); - assert_eq!(delay.stamp.hour(), 23); - assert_eq!(delay.stamp.minute(), 08); - assert_eq!(delay.stamp.second(), 25); - assert_eq!(delay.stamp.nanosecond(), 0); - assert_eq!(delay.stamp.timezone(), FixedOffset::east(0)); + assert_eq!(delay.stamp, DateTime::from_str("2002-09-10T23:08:25Z").unwrap()); assert_eq!(delay.data, None); } @@ -105,7 +97,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let delay = Delay { from: None, - stamp: DateTime::parse_from_rfc3339("2002-09-10T23:08:25Z").unwrap(), + stamp: DateTime::from_str("2002-09-10T23:08:25Z").unwrap(), data: None, }; let elem2 = delay.into(); @@ -117,7 +109,7 @@ mod tests { let elem: Element = "Reason".parse().unwrap(); let delay = Delay { from: Some(Jid::from_str("juliet@example.org").unwrap()), - stamp: DateTime::parse_from_rfc3339("2002-09-10T23:08:25Z").unwrap(), + stamp: DateTime::from_str("2002-09-10T23:08:25Z").unwrap(), data: Some(String::from("Reason")), }; let elem2 = delay.into(); diff --git a/src/idle.rs b/src/idle.rs index 51c203934056755d7a07bc771aea6837ced92d63..e4b09924580777f39746e0d25a743a24279fdfb0 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -7,44 +7,20 @@ use try_from::TryFrom; use minidom::Element; -use chrono::{DateTime, FixedOffset}; +use date::DateTime; use error::Error; use ns; -#[derive(Debug, Clone)] -pub struct Idle { - pub since: DateTime, -} - -impl TryFrom for Idle { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("idle", ns::IDLE) { - return Err(Error::ParseError("This is not an idle element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in idle element.")); - } - let since = get_attr!(elem, "since", required, since, DateTime::parse_from_rfc3339(since)?); - Ok(Idle { since: since }) - } -} - -impl From for Element { - fn from(idle: Idle) -> Element { - Element::builder("idle") - .ns(ns::IDLE) - .attr("since", idle.since.to_rfc3339()) - .build() - } -} +generate_element_with_only_attributes!(Idle, "idle", ns::IDLE, [ + since: DateTime = "since" => required, +]); #[cfg(test)] mod tests { use super::*; + use std::str::FromStr; use std::error::Error as StdError; #[test] @@ -135,7 +111,7 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let idle = Idle { since: DateTime::parse_from_rfc3339("2017-05-21T20:19:55+01:00").unwrap() }; + let idle = Idle { since: DateTime::from_str("2017-05-21T20:19:55+01:00").unwrap() }; let elem2 = idle.into(); assert_eq!(elem, elem2); } diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 15a21c67efa16c70df390cb3774b4d3d505a59de..fda8dbeffd0819f9150e00bb63c96c5269cc01a8 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -11,9 +11,9 @@ use std::str::FromStr; use hashes::Hash; use jingle::{Creator, ContentId}; +use date::DateTime; use minidom::{Element, IntoAttributeValue}; -use chrono::{DateTime, FixedOffset}; use error::Error; use ns; @@ -60,7 +60,7 @@ generate_id!(Desc); #[derive(Debug, Clone)] pub struct File { - pub date: Option>, + pub date: Option, pub media_type: Option, pub name: Option, pub descs: BTreeMap, @@ -137,7 +137,7 @@ impl From for Element { if let Some(date) = file.date { root.append_child(Element::builder("date") .ns(ns::JINGLE_FT) - .append(date.to_rfc3339()) + .append(date) .build()); } if let Some(media_type) = file.media_type { @@ -285,7 +285,7 @@ mod tests { assert_eq!(desc.file.media_type, Some(String::from("text/plain"))); assert_eq!(desc.file.name, Some(String::from("test.txt"))); assert_eq!(desc.file.descs, BTreeMap::new()); - assert_eq!(desc.file.date, Some(DateTime::parse_from_rfc3339("2015-07-26T21:46:00+01:00").unwrap())); + assert_eq!(desc.file.date, DateTime::from_str("2015-07-26T21:46:00+01:00").ok()); assert_eq!(desc.file.size, Some(6144u64)); assert_eq!(desc.file.range, None); assert_eq!(desc.file.hashes[0].algo, Algo::Sha_1); diff --git a/src/lib.rs b/src/lib.rs index 2a63302cf100c0adc4697ca0cace696d17ebc0b2..68302afe8f6a8ed199aa0cbe7af1a617d8335cc9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -325,6 +325,9 @@ pub mod pubsub; /// XEP-0077: In-Band Registration pub mod ibr; +/// XEP-0082: XMPP Date and Time Profiles +pub mod date; + /// XEP-0085: Chat State Notifications pub mod chatstates; diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 74b59ae6cdf32baf92705de5a9f5796d5fd3f511..cbeb6ef759b519fd07b082a92f555b3b28838329 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -9,7 +9,7 @@ use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; use jid::Jid; -use chrono::{DateTime, FixedOffset}; +use date::DateTime; use error::Error; @@ -98,7 +98,7 @@ pub enum PubSubEvent { }, Subscription { node: NodeName, - expiry: Option>, + expiry: Option, jid: Option, subid: Option, subscription: Option, @@ -256,7 +256,7 @@ impl From for Element { Element::builder("subscription") .ns(ns::PUBSUB_EVENT) .attr("node", node) - .attr("expiry", expiry.map(|expiry| expiry.to_rfc3339())) + .attr("expiry", expiry) .attr("jid", jid) .attr("subid", subid) .attr("subscription", subscription) From 6eba0e7d8738137e29e2b206474ca50b18287359 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 20:35:25 +0000 Subject: [PATCH 358/698] Simplify check_self macro. --- src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 68302afe8f6a8ed199aa0cbe7af1a617d8335cc9..db57ce2698cc459f1d381889d6672d749bff18c0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -127,9 +127,7 @@ macro_rules! generate_attribute { macro_rules! check_self { ($elem:ident, $name:tt, $ns:expr) => ( - if !$elem.is($name, $ns) { - return Err(Error::ParseError(concat!("This is not a ", $name, " element."))); - } + check_self!($elem, $name, $ns, $name); ); ($elem:ident, $name:tt, $ns:expr, $pretty_name:tt) => ( if !$elem.is($name, $ns) { From df069ac6c6346381e9ba267b5b6e0cd2c90e6fed Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 21:10:04 +0000 Subject: [PATCH 359/698] mam: Simplify parsing. --- src/mam.rs | 118 ++++++++++++++++++++++++++--------------------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/src/mam.rs b/src/mam.rs index 65299b700be715c8efe2d0ee7685fff300fd759c..4a2872b45eac20a90ea7e80480fc45b4b53702fe 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -56,9 +56,8 @@ impl TryFrom for Query { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("query", ns::MAM) { - return Err(Error::ParseError("This is not a query element.")); - } + check_self!(elem, "query", ns::MAM); + check_no_unknown_attributes!(elem, "query", ["queryid", "node"]); let mut form = None; let mut set = None; for child in elem.children() { @@ -80,9 +79,8 @@ impl TryFrom for Result_ { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("result", ns::MAM) { - return Err(Error::ParseError("This is not a result element.")); - } + check_self!(elem, "result", ns::MAM); + check_no_unknown_attributes!(elem, "result", ["queryid", "id"]); let mut forwarded = None; for child in elem.children() { if child.is("forwarded", ns::FORWARD) { @@ -106,9 +104,8 @@ impl TryFrom for Fin { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("fin", ns::MAM) { - return Err(Error::ParseError("This is not a fin element.")); - } + check_self!(elem, "fin", ns::MAM); + check_no_unknown_attributes!(elem, "fin", ["complete"]); let mut set = None; for child in elem.children() { if child.is("set", ns::RSM) { @@ -132,9 +129,8 @@ impl TryFrom for Prefs { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("prefs", ns::MAM) { - return Err(Error::ParseError("This is not a prefs element.")); - } + check_self!(elem, "prefs", ns::MAM); + check_no_unknown_attributes!(elem, "prefs", ["default"]); let mut always = vec!(); let mut never = vec!(); for child in elem.children() { @@ -167,66 +163,57 @@ impl From for Element { .ns(ns::MAM) .attr("queryid", query.queryid) .attr("node", query.node) - //.append(query.form.map(Element::from)) - .append(query.set.map(Element::from)) + //.append(query.form) + .append(query.set) .build() } } impl From for Element { fn from(result: Result_) -> Element { - let mut elem = Element::builder("result") - .ns(ns::MAM) - .attr("queryid", result.queryid) - .attr("id", result.id) - .build(); - elem.append_child(result.forwarded.into()); - elem + Element::builder("result") + .ns(ns::MAM) + .attr("queryid", result.queryid) + .attr("id", result.id) + .append(result.forwarded) + .build() } } impl From for Element { fn from(fin: Fin) -> Element { - let mut elem = Element::builder("fin") - .ns(ns::MAM) - .attr("complete", if fin.complete { Some("true") } else { None }) - .build(); - elem.append_child(fin.set.into()); - elem + Element::builder("fin") + .ns(ns::MAM) + .attr("complete", if fin.complete { Some("true") } else { None }) + .append(fin.set) + .build() + } +} + +fn serialise_jid_list(name: &str, jids: Vec) -> Option { + if jids.is_empty() { + None + } else { + Some(Element::builder(name) + .ns(ns::MAM) + .append(jids.into_iter() + .map(|jid| Element::builder("jid") + .ns(ns::MAM) + .append(String::from(jid)) + .build()) + .collect::>()) + .build()) } } impl From for Element { fn from(prefs: Prefs) -> Element { - let mut elem = Element::builder("prefs") - .ns(ns::MAM) - .attr("default", prefs.default_) - .build(); - if !prefs.always.is_empty() { - let mut always = Element::builder("always") - .ns(ns::RSM) - .build(); - for jid in prefs.always { - always.append_child(Element::builder("jid") - .ns(ns::RSM) - .append(String::from(jid)) - .build()); - } - elem.append_child(always); - } - if !prefs.never.is_empty() { - let mut never = Element::builder("never") - .ns(ns::RSM) - .build(); - for jid in prefs.never { - never.append_child(Element::builder("jid") - .ns(ns::RSM) - .append(String::from(jid)) - .build()); - } - elem.append_child(never); - } - elem + Element::builder("prefs") + .ns(ns::MAM) + .attr("default", prefs.default_) + .append(serialise_jid_list("always", prefs.always)) + .append(serialise_jid_list("never", prefs.never)) + .build() } } @@ -320,7 +307,9 @@ mod tests { #[test] fn test_prefs_get() { let elem: Element = "".parse().unwrap(); - Prefs::try_from(elem).unwrap(); + let prefs = Prefs::try_from(elem).unwrap(); + assert_eq!(prefs.always, vec!()); + assert_eq!(prefs.never, vec!()); let elem: Element = r#" @@ -328,7 +317,9 @@ mod tests { "#.parse().unwrap(); - Prefs::try_from(elem).unwrap(); + let prefs = Prefs::try_from(elem).unwrap(); + assert_eq!(prefs.always, vec!()); + assert_eq!(prefs.never, vec!()); } #[test] @@ -343,7 +334,16 @@ mod tests { "#.parse().unwrap(); - Prefs::try_from(elem).unwrap(); + let prefs = Prefs::try_from(elem).unwrap(); + assert_eq!(prefs.always, vec!(Jid::from_str("romeo@montague.lit").unwrap())); + assert_eq!(prefs.never, vec!(Jid::from_str("montague@montague.lit").unwrap())); + + let elem2 = Element::from(prefs.clone()); + println!("{:?}", elem2); + let prefs2 = Prefs::try_from(elem2).unwrap(); + assert_eq!(prefs.default_, prefs2.default_); + assert_eq!(prefs.always, prefs2.always); + assert_eq!(prefs.never, prefs2.never); } #[test] From bd9aa01ad54c9a320fc84fd3cba1329304be8e9c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 21:17:24 +0000 Subject: [PATCH 360/698] Split macros into their own module. --- src/lib.rs | 251 +------------------------------------------------ src/macros.rs | 253 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 256 insertions(+), 248 deletions(-) create mode 100644 src/macros.rs diff --git a/src/lib.rs b/src/lib.rs index db57ce2698cc459f1d381889d6672d749bff18c0..4a40ae119f69218f7f0f7a3a20a677c5377eac71 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,258 +33,13 @@ extern crate blake2; extern crate chrono; extern crate try_from; -macro_rules! get_attr { - ($elem:ident, $attr:tt, $type:tt) => ( - get_attr!($elem, $attr, $type, value, value.parse()?) - ); - ($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => ( - match $elem.attr($attr) { - Some($value) => Some($func), - None => None, - } - ); - ($elem:ident, $attr:tt, required, $value:ident, $func:expr) => ( - match $elem.attr($attr) { - Some($value) => $func, - None => return Err(Error::ParseError(concat!("Required attribute '", $attr, "' missing."))), - } - ); - ($elem:ident, $attr:tt, default, $value:ident, $func:expr) => ( - match $elem.attr($attr) { - Some($value) => $func, - None => Default::default(), - } - ); -} - -macro_rules! generate_attribute { - ($elem:ident, $name:tt, {$($a:ident => $b:tt),+,}) => ( - generate_attribute!($elem, $name, {$($a => $b),+}); - ); - ($elem:ident, $name:tt, {$($a:ident => $b:tt),+,}, Default = $default:ident) => ( - generate_attribute!($elem, $name, {$($a => $b),+}, Default = $default); - ); - ($elem:ident, $name:tt, {$($a:ident => $b:tt),+}) => ( - #[derive(Debug, Clone, PartialEq)] - pub enum $elem { - $( - #[doc=$b] - #[doc="value for this attribute."] - $a - ),+ - } - impl FromStr for $elem { - type Err = Error; - fn from_str(s: &str) -> Result<$elem, Error> { - Ok(match s { - $($b => $elem::$a),+, - _ => return Err(Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), - }) - } - } - impl IntoAttributeValue for $elem { - fn into_attribute_value(self) -> Option { - Some(String::from(match self { - $($elem::$a => $b),+ - })) - } - } - ); - ($elem:ident, $name:tt, {$($a:ident => $b:tt),+}, Default = $default:ident) => ( - #[derive(Debug, Clone, PartialEq)] - pub enum $elem { - $( - #[doc=$b] - #[doc="value for this attribute."] - $a - ),+ - } - impl FromStr for $elem { - type Err = Error; - fn from_str(s: &str) -> Result<$elem, Error> { - Ok(match s { - $($b => $elem::$a),+, - _ => return Err(Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), - }) - } - } - impl IntoAttributeValue for $elem { - #[allow(unreachable_patterns)] - fn into_attribute_value(self) -> Option { - Some(String::from(match self { - $elem::$default => return None, - $($elem::$a => $b),+ - })) - } - } - impl Default for $elem { - fn default() -> $elem { - $elem::$default - } - } - ); -} - -macro_rules! check_self { - ($elem:ident, $name:tt, $ns:expr) => ( - check_self!($elem, $name, $ns, $name); - ); - ($elem:ident, $name:tt, $ns:expr, $pretty_name:tt) => ( - if !$elem.is($name, $ns) { - return Err(Error::ParseError(concat!("This is not a ", $pretty_name, " element."))); - } - ); -} - -macro_rules! check_ns_only { - ($elem:ident, $name:tt, $ns:expr) => ( - if !$elem.has_ns($ns) { - return Err(Error::ParseError(concat!("This is not a ", $name, " element."))); - } - ); -} - -macro_rules! check_no_children { - ($elem:ident, $name:tt) => ( - for _ in $elem.children() { - return Err(Error::ParseError(concat!("Unknown child in ", $name, " element."))); - } - ); -} - -macro_rules! check_no_attributes { - ($elem:ident, $name:tt) => ( - check_no_unknown_attributes!($elem, $name, []); - ); -} - -macro_rules! check_no_unknown_attributes { - ($elem:ident, $name:tt, [$($attr:tt),*]) => ( - for (_attr, _) in $elem.attrs() { - $( - if _attr == $attr { - continue; - } - )* - return Err(Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); - } - ); -} - -macro_rules! generate_empty_element { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr) => ( - $(#[$meta])* - #[derive(Debug, Clone)] - pub struct $elem; - - impl TryFrom for $elem { - type Err = Error; - - fn try_from(elem: Element) -> Result<$elem, Error> { - check_self!(elem, $name, $ns); - check_no_children!(elem, $name); - check_no_attributes!(elem, $name); - Ok($elem) - } - } - - impl From<$elem> for Element { - fn from(_: $elem) -> Element { - Element::builder($name) - .ns($ns) - .build() - } - } - ); -} - -macro_rules! generate_element_with_only_attributes { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => ( - generate_element_with_only_attributes!($(#[$meta])* $elem, $name, $ns, [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*]); - ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => ( - $(#[$meta])* - #[derive(Debug, Clone)] - pub struct $elem { - $( - $(#[$attr_meta])* - pub $attr: $attr_type - ),* - } - - impl TryFrom for $elem { - type Err = Error; - - fn try_from(elem: Element) -> Result<$elem, Error> { - check_self!(elem, $name, $ns); - check_no_children!(elem, $name); - check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); - Ok($elem { - $( - $attr: get_attr!(elem, $attr_name, $attr_action) - ),* - }) - } - } - - impl From<$elem> for Element { - fn from(elem: $elem) -> Element { - Element::builder($name) - .ns($ns) - $( - .attr($attr_name, elem.$attr) - )* - .build() - } - } - ); -} - -macro_rules! generate_id { - ($elem:ident) => ( - #[derive(Debug, Clone, PartialEq, Eq, Hash)] - pub struct $elem(pub String); - impl FromStr for $elem { - type Err = Error; - fn from_str(s: &str) -> Result<$elem, Error> { - // TODO: add a way to parse that differently when needed. - Ok($elem(String::from(s))) - } - } - impl IntoAttributeValue for $elem { - fn into_attribute_value(self) -> Option { - Some(self.0) - } - } - ); -} - -macro_rules! generate_elem_id { - ($elem:ident, $name:tt, $ns:expr) => ( - #[derive(Debug, Clone, PartialEq, Eq, Hash)] - pub struct $elem(pub String); - impl FromStr for $elem { - type Err = Error; - fn from_str(s: &str) -> Result<$elem, Error> { - // TODO: add a way to parse that differently when needed. - Ok($elem(String::from(s))) - } - } - impl From<$elem> for Element { - fn from(elem: $elem) -> Element { - Element::builder($name) - .ns($ns) - .append(elem.0) - .build() - } - } - ); -} - /// Error type returned by every parser on failure. pub mod error; /// XML namespace definitions used through XMPP. pub mod ns; +/// Helper macros to parse and serialise more easily. +#[macro_use] +pub mod macros; #[cfg(test)] /// Namespace-aware comparison for tests diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000000000000000000000000000000000000..97f0adcdc684aa774aac7dedd433e739d766d40f --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,253 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +macro_rules! get_attr { + ($elem:ident, $attr:tt, $type:tt) => ( + get_attr!($elem, $attr, $type, value, value.parse()?) + ); + ($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => ( + match $elem.attr($attr) { + Some($value) => Some($func), + None => None, + } + ); + ($elem:ident, $attr:tt, required, $value:ident, $func:expr) => ( + match $elem.attr($attr) { + Some($value) => $func, + None => return Err(Error::ParseError(concat!("Required attribute '", $attr, "' missing."))), + } + ); + ($elem:ident, $attr:tt, default, $value:ident, $func:expr) => ( + match $elem.attr($attr) { + Some($value) => $func, + None => Default::default(), + } + ); +} + +macro_rules! generate_attribute { + ($elem:ident, $name:tt, {$($a:ident => $b:tt),+,}) => ( + generate_attribute!($elem, $name, {$($a => $b),+}); + ); + ($elem:ident, $name:tt, {$($a:ident => $b:tt),+,}, Default = $default:ident) => ( + generate_attribute!($elem, $name, {$($a => $b),+}, Default = $default); + ); + ($elem:ident, $name:tt, {$($a:ident => $b:tt),+}) => ( + #[derive(Debug, Clone, PartialEq)] + pub enum $elem { + $( + #[doc=$b] + #[doc="value for this attribute."] + $a + ),+ + } + impl FromStr for $elem { + type Err = Error; + fn from_str(s: &str) -> Result<$elem, Error> { + Ok(match s { + $($b => $elem::$a),+, + _ => return Err(Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + }) + } + } + impl IntoAttributeValue for $elem { + fn into_attribute_value(self) -> Option { + Some(String::from(match self { + $($elem::$a => $b),+ + })) + } + } + ); + ($elem:ident, $name:tt, {$($a:ident => $b:tt),+}, Default = $default:ident) => ( + #[derive(Debug, Clone, PartialEq)] + pub enum $elem { + $( + #[doc=$b] + #[doc="value for this attribute."] + $a + ),+ + } + impl FromStr for $elem { + type Err = Error; + fn from_str(s: &str) -> Result<$elem, Error> { + Ok(match s { + $($b => $elem::$a),+, + _ => return Err(Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + }) + } + } + impl IntoAttributeValue for $elem { + #[allow(unreachable_patterns)] + fn into_attribute_value(self) -> Option { + Some(String::from(match self { + $elem::$default => return None, + $($elem::$a => $b),+ + })) + } + } + impl Default for $elem { + fn default() -> $elem { + $elem::$default + } + } + ); +} + +macro_rules! check_self { + ($elem:ident, $name:tt, $ns:expr) => ( + check_self!($elem, $name, $ns, $name); + ); + ($elem:ident, $name:tt, $ns:expr, $pretty_name:tt) => ( + if !$elem.is($name, $ns) { + return Err(Error::ParseError(concat!("This is not a ", $pretty_name, " element."))); + } + ); +} + +macro_rules! check_ns_only { + ($elem:ident, $name:tt, $ns:expr) => ( + if !$elem.has_ns($ns) { + return Err(Error::ParseError(concat!("This is not a ", $name, " element."))); + } + ); +} + +macro_rules! check_no_children { + ($elem:ident, $name:tt) => ( + for _ in $elem.children() { + return Err(Error::ParseError(concat!("Unknown child in ", $name, " element."))); + } + ); +} + +macro_rules! check_no_attributes { + ($elem:ident, $name:tt) => ( + check_no_unknown_attributes!($elem, $name, []); + ); +} + +macro_rules! check_no_unknown_attributes { + ($elem:ident, $name:tt, [$($attr:tt),*]) => ( + for (_attr, _) in $elem.attrs() { + $( + if _attr == $attr { + continue; + } + )* + return Err(Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); + } + ); +} + +macro_rules! generate_empty_element { + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr) => ( + $(#[$meta])* + #[derive(Debug, Clone)] + pub struct $elem; + + impl TryFrom for $elem { + type Err = Error; + + fn try_from(elem: Element) -> Result<$elem, Error> { + check_self!(elem, $name, $ns); + check_no_children!(elem, $name); + check_no_attributes!(elem, $name); + Ok($elem) + } + } + + impl From<$elem> for Element { + fn from(_: $elem) -> Element { + Element::builder($name) + .ns($ns) + .build() + } + } + ); +} + +macro_rules! generate_element_with_only_attributes { + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => ( + generate_element_with_only_attributes!($(#[$meta])* $elem, $name, $ns, [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*]); + ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => ( + $(#[$meta])* + #[derive(Debug, Clone)] + pub struct $elem { + $( + $(#[$attr_meta])* + pub $attr: $attr_type + ),* + } + + impl TryFrom for $elem { + type Err = Error; + + fn try_from(elem: Element) -> Result<$elem, Error> { + check_self!(elem, $name, $ns); + check_no_children!(elem, $name); + check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); + Ok($elem { + $( + $attr: get_attr!(elem, $attr_name, $attr_action) + ),* + }) + } + } + + impl From<$elem> for Element { + fn from(elem: $elem) -> Element { + Element::builder($name) + .ns($ns) + $( + .attr($attr_name, elem.$attr) + )* + .build() + } + } + ); +} + +macro_rules! generate_id { + ($elem:ident) => ( + #[derive(Debug, Clone, PartialEq, Eq, Hash)] + pub struct $elem(pub String); + impl FromStr for $elem { + type Err = Error; + fn from_str(s: &str) -> Result<$elem, Error> { + // TODO: add a way to parse that differently when needed. + Ok($elem(String::from(s))) + } + } + impl IntoAttributeValue for $elem { + fn into_attribute_value(self) -> Option { + Some(self.0) + } + } + ); +} + +macro_rules! generate_elem_id { + ($elem:ident, $name:tt, $ns:expr) => ( + #[derive(Debug, Clone, PartialEq, Eq, Hash)] + pub struct $elem(pub String); + impl FromStr for $elem { + type Err = Error; + fn from_str(s: &str) -> Result<$elem, Error> { + // TODO: add a way to parse that differently when needed. + Ok($elem(String::from(s))) + } + } + impl From<$elem> for Element { + fn from(elem: $elem) -> Element { + Element::builder($name) + .ns($ns) + .append(elem.0) + .build() + } + } + ); +} From 655fc9ae682bc2b9730a622f3b9cb9586a3f0816 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 21:35:07 +0000 Subject: [PATCH 361/698] Cargo.toml: Bump base64 to 0.7.0. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 9db6539d876dba3d09a6c8dc03ea3a1294647394..3a447e9add42647eafb6027e1589748f88d2c73e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ license = "MPL-2.0" [dependencies] minidom = "0.6.2" jid = { version = "0.3.0", features = ["minidom"] } -base64 = "0.6.0" +base64 = "0.7.0" digest = "0.6.0" sha-1 = "0.4.0" sha2 = "0.6.0" From fb27b6225c1e28c2acaceaab44ef2be91e54809b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 21:44:43 +0000 Subject: [PATCH 362/698] jingle_ft: Correctly insert in . --- src/jingle_ft.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index fda8dbeffd0819f9150e00bb63c96c5269cc01a8..3339e015553ffc80c34b9b77ad66ea431db0dc11 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -166,10 +166,7 @@ impl From for Element { .build()); } if let Some(range) = file.range { - root.append_child(Element::builder("range") - .ns(ns::JINGLE_FT) - .append(range) - .build()); + root.append_child(range.into()); } for hash in file.hashes { root.append_child(hash.into()); From 4875f15201f687d0a0e22e735b35507fb49d5108 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 22:01:52 +0000 Subject: [PATCH 363/698] media_element: Parse URI separately from MediaElement. --- src/media_element.rs | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/media_element.rs b/src/media_element.rs index b2e5857d449630ab36956cd4b2bcc46f736b558f..478dbb3117603a5dd4b0a5d7c169930f78fff114 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -18,6 +18,24 @@ pub struct URI { pub uri: String, } +impl TryFrom for URI { + type Err = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "uri", ns::MEDIA_ELEMENT); + check_no_unknown_attributes!(elem, "uri", ["type"]); + check_no_children!(elem, "uri"); + let uri = elem.text().trim().to_owned(); + if uri == "" { + return Err(Error::ParseError("URI missing in uri.")); + } + Ok(URI { + type_: get_attr!(elem, "type", required), + uri: uri, + }) + } +} + impl From for Element { fn from(uri: URI) -> Element { Element::builder("uri") @@ -48,14 +66,9 @@ impl TryFrom for MediaElement { height: get_attr!(elem, "height", optional), uris: vec!(), }; - for uri in elem.children() { - if uri.is("uri", ns::MEDIA_ELEMENT) { - let type_ = get_attr!(uri, "type", required); - let text = uri.text().trim().to_owned(); - if text == "" { - return Err(Error::ParseError("URI missing in uri.")); - } - media.uris.push(URI { type_: type_, uri: text }); + for child in elem.children() { + if child.is("uri", ns::MEDIA_ELEMENT) { + media.uris.push(URI::try_from(child.clone())?); } else { return Err(Error::ParseError("Unknown child in media element.")); } From 6e48d0f65a424c60348ba8bae0c4f7e554f5dae7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 22:04:13 +0000 Subject: [PATCH 364/698] media_element: Also check for unknown attributes. --- src/media_element.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/media_element.rs b/src/media_element.rs index 478dbb3117603a5dd4b0a5d7c169930f78fff114..b700eec1af6e5fd512a5055f5472493ea636ca01 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -57,9 +57,8 @@ impl TryFrom for MediaElement { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("media", ns::MEDIA_ELEMENT) { - return Err(Error::ParseError("This is not a media element.")); - } + check_self!(elem, "media", ns::MEDIA_ELEMENT); + check_no_unknown_attributes!(elem, "media", ["width", "height"]); let mut media = MediaElement { width: get_attr!(elem, "width", optional), From 86eb65b8291a31877379a8dfc480885e20dbe2aa Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 22:21:27 +0000 Subject: [PATCH 365/698] mam: Bump jid to 0.3.1, to serialise it directly to a text node. --- Cargo.toml | 2 +- src/mam.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3a447e9add42647eafb6027e1589748f88d2c73e..cd55dfbf7f894e03f8ba0c87fd1e6da1e275d21c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ license = "MPL-2.0" [dependencies] minidom = "0.6.2" -jid = { version = "0.3.0", features = ["minidom"] } +jid = { version = "0.3.1", features = ["minidom"] } base64 = "0.7.0" digest = "0.6.0" sha-1 = "0.4.0" diff --git a/src/mam.rs b/src/mam.rs index 4a2872b45eac20a90ea7e80480fc45b4b53702fe..82158a40e01db7fc082cba3b8dbadc14f636fb5e 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -199,7 +199,7 @@ fn serialise_jid_list(name: &str, jids: Vec) -> Option { .append(jids.into_iter() .map(|jid| Element::builder("jid") .ns(ns::MAM) - .append(String::from(jid)) + .append(jid) .build()) .collect::>()) .build()) From 7950fe8f5222c5afe443ad4d1e15e88738d991d5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 22:21:50 +0000 Subject: [PATCH 366/698] rsm: Simplify serialisation. --- src/rsm.rs | 44 ++++++++++++++++---------------------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/src/rsm.rs b/src/rsm.rs index 97e7abc6182852a55f2ec36f2d732e3452d6e191..17642986488e1688e81b956688c8d0f5cb453b6e 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -88,34 +88,22 @@ impl TryFrom for Set { impl From for Element { fn from(set: Set) -> Element { - let mut elem = Element::builder("set") - .ns(ns::RSM) - .build(); - if set.after.is_some() { - elem.append_child(Element::builder("after").ns(ns::RSM).append(set.after).build()); - } - if set.before.is_some() { - elem.append_child(Element::builder("before").ns(ns::RSM).append(set.before).build()); - } - if let Some(count) = set.count { - elem.append_child(Element::builder("count").ns(ns::RSM).append(format!("{}", count)).build()); - } - if set.first.is_some() { - elem.append_child(Element::builder("first") - .ns(ns::RSM) - .attr("index", set.first_index) - .append(set.first).build()); - } - if let Some(index) = set.index { - elem.append_child(Element::builder("index").ns(ns::RSM).append(format!("{}", index)).build()); - } - if set.last.is_some() { - elem.append_child(Element::builder("last").ns(ns::RSM).append(set.last).build()); - } - if let Some(max) = set.max { - elem.append_child(Element::builder("max").ns(ns::RSM).append(format!("{}", max)).build()); - } - elem + let first = set.first.clone() + .map(|first| Element::builder("first") + .ns(ns::RSM) + .attr("index", set.first_index) + .append(first) + .build()); + Element::builder("set") + .ns(ns::RSM) + .append(set.after.map(|after| Element::builder("after").ns(ns::RSM).append(after).build())) + .append(set.before.map(|before| Element::builder("before").ns(ns::RSM).append(before).build())) + .append(set.count.map(|count| Element::builder("count").ns(ns::RSM).append(format!("{}", count)).build())) + .append(first) + .append(set.index.map(|index| Element::builder("index").ns(ns::RSM).append(format!("{}", index)).build())) + .append(set.last.map(|last| Element::builder("last").ns(ns::RSM).append(last).build())) + .append(set.max.map(|max| Element::builder("max").ns(ns::RSM).append(format!("{}", max)).build())) + .build() } } From 35258482ba6738f8cf02af254eb58d822028dfe8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 22:27:13 +0000 Subject: [PATCH 367/698] delay: Simplify parsing and add more checks. --- src/delay.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index 53462792680991e7575a084a596a8e23af2f91dd..e95ed4c2f81791880fe29ba048be15ba3d4195e2 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -25,21 +25,16 @@ impl TryFrom for Delay { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("delay", ns::DELAY) { - return Err(Error::ParseError("This is not a delay element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in delay element.")); - } - let from = get_attr!(elem, "from", optional); - let stamp = get_attr!(elem, "stamp", required); + check_self!(elem, "delay", ns::DELAY); + check_no_children!(elem, "delay"); + check_no_unknown_attributes!(elem, "delay", ["from", "stamp"]); let data = match elem.text().as_ref() { "" => None, text => Some(text.to_owned()), }; Ok(Delay { - from: from, - stamp: stamp, + from: get_attr!(elem, "from", optional), + stamp: get_attr!(elem, "stamp", required), data: data, }) } From 1e39a4cdd7f0e4fe9753e917e318139865eb0291 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 22:31:57 +0000 Subject: [PATCH 368/698] jingle_s5b: Actually use the Candidate parser. --- src/jingle_s5b.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index f1396c5df4d1d1c1fdf032fa18c324a17cba229e..09b3dc56ea7bd6b6c3db874929b8740024497bcc 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -74,14 +74,7 @@ impl TryFrom for Transport { Some(_) => return Err(Error::ParseError("Non-candidate child already present in JingleS5B transport element.")), None => vec!(), }; - candidates.push(Candidate { - cid: get_attr!(child, "cid", required), - host: get_attr!(child, "host", required), - jid: get_attr!(child, "jid", required), - port: get_attr!(child, "port", optional), - priority: get_attr!(child, "priority", required), - type_: get_attr!(child, "type", default), - }); + candidates.push(Candidate::try_from(child.clone())?); TransportPayload::Candidates(candidates) } else if child.is("activated", ns::JINGLE_S5B) { if payload.is_some() { From 4fb909cfd1e5d77278132b76d1bff62ed34afe07 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 23:33:34 +0000 Subject: [PATCH 369/698] data_forms: Simplify serialisation using .map(). --- src/data_forms.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index a54be9c2e7a3139915be92b22140c9b2eb58672e..9b173b8aee979c6cf3f2f9b64889e01ed4a3e1ef 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -218,8 +218,8 @@ impl From for Element { Element::builder("x") .ns(ns::DATA_FORMS) .attr("type", form.type_) - .append(if form.title.is_some() { Some(Element::builder("title").ns(ns::DATA_FORMS).append(form.title)) } else { None }) - .append(if form.instructions.is_some() { Some(Element::builder("instructions").ns(ns::DATA_FORMS).append(form.instructions)) } else { None }) + .append(form.title.map(|title| Element::builder("title").ns(ns::DATA_FORMS).append(title))) + .append(form.instructions.map(|text| Element::builder("instructions").ns(ns::DATA_FORMS).append(text))) .append(form.fields) .build() } From 8aa51bd04742916f097130e945b19bc1c9e97dd8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 1 Nov 2017 00:24:23 +0000 Subject: [PATCH 370/698] eme: Restore docstrings. --- src/eme.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/eme.rs b/src/eme.rs index 540b50730df1036c450f80478b44fd77fa1738ca..5d6e7111ffabfd5060d2b752dd75a65df6ce78e8 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -12,13 +12,14 @@ use error::Error; use ns; +generate_element_with_only_attributes!( /// Structure representing an `` element. -generate_element_with_only_attributes!(ExplicitMessageEncryption, "encryption", ns::EME, [ - // Namespace of the encryption scheme used. +ExplicitMessageEncryption, "encryption", ns::EME, [ + /// Namespace of the encryption scheme used. namespace: String = "namespace" => required, - // User-friendly name for the encryption scheme, should be `None` for OTR, - // legacy OpenPGP and OX. + /// User-friendly name for the encryption scheme, should be `None` for OTR, + /// legacy OpenPGP and OX. name: Option = "name" => optional, ]); From c1ff291c11b78a153298201d893f630e5e185a37 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 15 Nov 2017 18:37:28 +0000 Subject: [PATCH 371/698] Update all crypto crates to their latest release; un-break blake2b. --- Cargo.toml | 10 +++++----- src/caps.rs | 2 +- src/ecaps2.rs | 10 ++-------- src/lib.rs | 2 +- 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cd55dfbf7f894e03f8ba0c87fd1e6da1e275d21c..4cf04e7430fb0e36ab5b639b7a40f804a7b5ec0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,11 +16,11 @@ license = "MPL-2.0" minidom = "0.6.2" jid = { version = "0.3.1", features = ["minidom"] } base64 = "0.7.0" -digest = "0.6.0" -sha-1 = "0.4.0" -sha2 = "0.6.0" -sha3 = "0.6.0" -blake2 = "0.6.1" +digest = "0.7.1" +sha-1 = "0.7.0" +sha2 = "0.7.0" +sha3 = "0.7.0" +blake2 = "0.7.0" chrono = "0.4.0" try_from = "0.2.2" diff --git a/src/caps.rs b/src/caps.rs index 2b986795daa7ab7b9c243fdd6e1a0654353d27bb..27d511ca1ea6054482e23e5eeeb6465ec4d40369 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -16,7 +16,7 @@ use ns; use base64; use digest::Digest; -use sha_1::Sha1; +use sha1::Sha1; use sha2::{Sha256, Sha512}; use sha3::{Sha3_256, Sha3_512}; //use blake2::Blake2b; diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 347d1dac8e71074fcee18191804a54f6fab64aad..b4c407c29c8bb89c0d4ee39222fbc68ff2d76530 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -15,11 +15,10 @@ use error::Error; use ns; use base64; -use digest::Digest; use sha2::{Sha256, Sha512}; use sha3::{Sha3_256, Sha3_512}; -//use blake2::Blake2b; -//use digest::{Digest, VariableOutput}; +use blake2::Blake2b; +use digest::{Digest, VariableOutput}; #[derive(Debug, Clone)] pub struct ECaps2 { @@ -144,9 +143,6 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { let hash = hasher.result(); get_hash_vec(hash.as_slice()) }, - Algo::Blake2b_256 - | Algo::Blake2b_512 => panic!("See https://github.com/RustCrypto/hashes/issues/34"), - /* Algo::Blake2b_256 => { let mut hasher = Blake2b::default(); hasher.input(data); @@ -161,7 +157,6 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { let hash = hasher.variable_result(&mut buf).unwrap(); get_hash_vec(hash) }, - */ Algo::Sha_1 => return Err(String::from("Disabled algorithm sha-1: unsafe.")), Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)), }, @@ -452,7 +447,6 @@ mod tests { } #[test] - #[ignore] fn test_blake2b_512() { let hash = ecaps2::hash_ecaps2("abc".as_bytes(), Algo::Blake2b_512).unwrap(); let known_hash: Vec = vec!( diff --git a/src/lib.rs b/src/lib.rs index 4a40ae119f69218f7f0f7a3a20a677c5377eac71..e5b5439c1c9f04b0926a4225624ec48ad2a61078 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,7 +26,7 @@ extern crate minidom; extern crate jid; extern crate base64; extern crate digest; -extern crate sha_1; +extern crate sha1; extern crate sha2; extern crate sha3; extern crate blake2; From 702c4d2932adf188be9a0273ae85c4e2586e9cd6 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 15 Nov 2017 23:15:12 +0000 Subject: [PATCH 372/698] ecaps2: Use the digest() facility to simplify hashing. --- src/ecaps2.rs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index b4c407c29c8bb89c0d4ee39222fbc68ff2d76530..587418b88c0dc7831670247c1ecebebe4dad9abd 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -120,27 +120,19 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { Ok(Hash { hash: match algo { Algo::Sha_256 => { - let mut hasher = Sha256::default(); - hasher.input(data); - let hash = hasher.result(); + let hash = Sha256::digest(data); get_hash_vec(hash.as_slice()) }, Algo::Sha_512 => { - let mut hasher = Sha512::default(); - hasher.input(data); - let hash = hasher.result(); + let hash = Sha512::digest(data); get_hash_vec(hash.as_slice()) }, Algo::Sha3_256 => { - let mut hasher = Sha3_256::default(); - hasher.input(data); - let hash = hasher.result(); + let hash = Sha3_256::digest(data); get_hash_vec(hash.as_slice()) }, Algo::Sha3_512 => { - let mut hasher = Sha3_512::default(); - hasher.input(data); - let hash = hasher.result(); + let hash = Sha3_512::digest(data); get_hash_vec(hash.as_slice()) }, Algo::Blake2b_256 => { From 9c9ffe70a75d6f5a165a397bc1e4c2918a3ab9d1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 15 Nov 2017 23:16:37 +0000 Subject: [PATCH 373/698] caps: Do the same fixes, unbreak blake2 and simplify code. --- src/caps.rs | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/src/caps.rs b/src/caps.rs index 27d511ca1ea6054482e23e5eeeb6465ec4d40369..05f84e1e3a393cab83893b217252a59d8cb4f179 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -15,12 +15,11 @@ use error::Error; use ns; use base64; -use digest::Digest; use sha1::Sha1; use sha2::{Sha256, Sha512}; use sha3::{Sha3_256, Sha3_512}; -//use blake2::Blake2b; -//use digest::{Digest, VariableOutput}; +use blake2::Blake2b; +use digest::{Digest, VariableOutput}; #[derive(Debug, Clone)] pub struct Caps { @@ -141,38 +140,25 @@ pub fn hash_caps(data: &[u8], algo: Algo) -> Result { Ok(Hash { hash: match algo { Algo::Sha_1 => { - let mut hasher = Sha1::default(); - hasher.input(data); - let hash = hasher.result(); + let hash = Sha1::digest(data); get_hash_vec(hash.as_slice()) }, Algo::Sha_256 => { - let mut hasher = Sha256::default(); - hasher.input(data); - let hash = hasher.result(); + let hash = Sha256::digest(data); get_hash_vec(hash.as_slice()) }, Algo::Sha_512 => { - let mut hasher = Sha512::default(); - hasher.input(data); - let hash = hasher.result(); + let hash = Sha512::digest(data); get_hash_vec(hash.as_slice()) }, Algo::Sha3_256 => { - let mut hasher = Sha3_256::default(); - hasher.input(data); - let hash = hasher.result(); + let hash = Sha3_256::digest(data); get_hash_vec(hash.as_slice()) }, Algo::Sha3_512 => { - let mut hasher = Sha3_512::default(); - hasher.input(data); - let hash = hasher.result(); + let hash = Sha3_512::digest(data); get_hash_vec(hash.as_slice()) }, - Algo::Blake2b_256 - | Algo::Blake2b_512 => panic!("See https://github.com/RustCrypto/hashes/issues/34"), - /* Algo::Blake2b_256 => { let mut hasher = Blake2b::default(); hasher.input(data); @@ -187,7 +173,6 @@ pub fn hash_caps(data: &[u8], algo: Algo) -> Result { let hash = hasher.variable_result(&mut buf).unwrap(); get_hash_vec(hash) }, - */ Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)), }, algo: algo, From 77e150c63d81c448088e43bb1208bb013aa05f47 Mon Sep 17 00:00:00 2001 From: Rust Cambridge Mob Date: Thu, 16 Nov 2017 20:17:11 +0000 Subject: [PATCH 374/698] Implement macro for elements containing text --- src/ibb.rs | 45 +++++++++++++++------------------------------ src/macros.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 30 deletions(-) diff --git a/src/ibb.rs b/src/ibb.rs index 7d7f3e73fa501e4a7ffd63e1586bac52ba8532ee..cc2d89c3c11fba3eb704ee87da6ed11c674a5f76 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -25,42 +25,27 @@ generate_element_with_only_attributes!(Open, "open", ns::IBB, [ stanza: Stanza = "stanza" => default, ]); -#[derive(Debug, Clone)] -pub struct Data { - pub seq: u16, - pub sid: String, - pub data: Vec, -} +/// Codec wrapping base64 encode/decode +struct Base64; -impl TryFrom for Data { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("data", ns::IBB) { - return Err(Error::ParseError("This is not a data element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in data element.")); - } - Ok(Data { - seq: get_attr!(elem, "seq", required), - sid: get_attr!(elem, "sid", required), - data: base64::decode(&elem.text())?, - }) +impl Base64 { + fn decode(s: &str) -> Result, Error> { + Ok(base64::decode(s)?) } -} -impl From for Element { - fn from(data: Data) -> Element { - Element::builder("data") - .ns(ns::IBB) - .attr("seq", data.seq) - .attr("sid", data.sid) - .append(base64::encode(&data.data)) - .build() + fn encode(b: &Vec) -> String { + base64::encode(b) } } +generate_element_with_text!(Data, "data", ns::IBB, + [ + seq: u16 = "seq" => required, + sid: String = "sid" => required + ], + data: Base64> +); + generate_element_with_only_attributes!(Close, "close", ns::IBB, [ sid: String = "sid" => required, ]); diff --git a/src/macros.rs b/src/macros.rs index 97f0adcdc684aa774aac7dedd433e739d766d40f..155767ac0bb5b5169bae421d31426a1c61c553f5 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -251,3 +251,45 @@ macro_rules! generate_elem_id { } ); } + +macro_rules! generate_element_with_text { + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+], $text_ident:ident: $codec:ident < $text_type:ty >) => ( + $(#[$meta])* + #[derive(Debug, Clone)] + pub struct $elem { + $( + $(#[$attr_meta])* + pub $attr: $attr_type + ),*, + pub $text_ident: $text_type, + } + + impl TryFrom for $elem { + type Err = Error; + + fn try_from(elem: Element) -> Result<$elem, Error> { + check_self!(elem, $name, $ns); + check_no_children!(elem, $name); + check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); + Ok($elem { + $( + $attr: get_attr!(elem, $attr_name, $attr_action) + ),*, + $text_ident: $codec::decode(&elem.text())?, + }) + } + } + + impl From<$elem> for Element { + fn from(elem: $elem) -> Element { + Element::builder($name) + .ns($ns) + $( + .attr($attr_name, elem.$attr) + )* + .append($codec::encode(&elem.$text_ident)) + .build() + } + } + ); +} From 3e37beffe25307a31815a2c7ea21c82f2a9d1ad8 Mon Sep 17 00:00:00 2001 From: Rust Cambridge Mob Date: Thu, 16 Nov 2017 21:00:01 +0000 Subject: [PATCH 375/698] Implement macro for elements containing children --- src/disco.rs | 62 +++++++++++++-------------------------------------- src/macros.rs | 52 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 46 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index 9964cd8c9e625e8592b3ba4587db60c3ac6a0ae3..b4efeee9d3ad9ed1db0b43a6010547478498f91f 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -201,52 +201,22 @@ Item, "item", ns::DISCO_ITEMS, [ name: Option = "name" => optional, ]); -/// Structure representing a `` element. -/// -/// It should only be used in an ``, as it can only -/// represent the result, and not a request. -#[derive(Debug, Clone)] -pub struct DiscoItemsResult { - /// Node on which we have done this discovery. - pub node: Option, - - /// List of items pointed by this entity. - pub items: Vec, -} - -impl TryFrom for DiscoItemsResult { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "query", ns::DISCO_ITEMS, "disco#items query"); - check_no_unknown_attributes!(elem, "disco#items query", ["node"]); - - let mut items: Vec = vec!(); - for child in elem.children() { - if child.is("item", ns::DISCO_ITEMS) { - items.push(Item::try_from(child.clone())?); - } else { - return Err(Error::ParseError("Unknown element in disco#items.")); - } - } - - Ok(DiscoItemsResult { - node: get_attr!(elem, "node", optional), - items: items, - }) - } -} - -impl From for Element { - fn from(disco: DiscoItemsResult) -> Element { - Element::builder("query") - .ns(ns::DISCO_ITEMS) - .attr("node", disco.node) - .append(disco.items) - .build() - } -} +generate_element_with_children!( + /// Structure representing a `` element. + /// + /// It should only be used in an ``, as it can only + /// represent the result, and not a request. + DiscoItemsResult, "query", ns::DISCO_ITEMS, + attributes: [ + /// Node on which we have done this discovery. + node: Option = "node" => optional + ], + children: [ + /// List of items pointed by this entity. + items: Vec = "item" => Item + ] +); #[cfg(test)] mod tests { diff --git a/src/macros.rs b/src/macros.rs index 155767ac0bb5b5169bae421d31426a1c61c553f5..136b32f97ce4b1fdd7667b5e53eb8e6d417fe49b 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -293,3 +293,55 @@ macro_rules! generate_element_with_text { } ); } + +macro_rules! generate_element_with_children { + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = $child_name:tt => $child_constructor:ident),+]) => ( + $(#[$meta])* + #[derive(Debug, Clone)] + pub struct $elem { + $( + $(#[$attr_meta])* + pub $attr: $attr_type + ),*, + $( + $(#[$child_meta])* + pub $child_ident: Vec<$child_type> + ),* + } + + impl TryFrom for $elem { + type Err = Error; + + fn try_from(elem: Element) -> Result<$elem, Error> { + check_self!(elem, $name, $ns); + check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); + let mut parsed_children = vec!(); + for child in elem.children() { + $( + let parsed_child = $child_constructor::try_from(child.clone())?; + parsed_children.push(parsed_child); + )* + } + Ok($elem { + $( + $attr: get_attr!(elem, $attr_name, $attr_action) + ),*, + $( + $child_ident: parsed_children + )* + }) + } + } + + impl From<$elem> for Element { + fn from(elem: $elem) -> Element { + Element::builder($name) + .ns($ns) + $( + .attr($attr_name, elem.$attr) + )* + .build() + } + } + ); +} From ad17c877f5c5fe82d3457d2fd129a9be01a69ed0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 23 Nov 2017 16:00:47 +0000 Subject: [PATCH 376/698] Move Base64 codec into a helper module. --- src/helpers.rs | 21 +++++++++++++++++++++ src/ibb.rs | 15 +-------------- src/lib.rs | 2 ++ 3 files changed, 24 insertions(+), 14 deletions(-) create mode 100644 src/helpers.rs diff --git a/src/helpers.rs b/src/helpers.rs new file mode 100644 index 0000000000000000000000000000000000000000..e12000ac9e85540b88839c0cf60b2ea425aab4c5 --- /dev/null +++ b/src/helpers.rs @@ -0,0 +1,21 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use base64; +use error::Error; + +/// Codec wrapping base64 encode/decode +pub struct Base64; + +impl Base64 { + pub fn decode(s: &str) -> Result, Error> { + Ok(base64::decode(s)?) + } + + pub fn encode(b: &Vec) -> String { + base64::encode(b) + } +} diff --git a/src/ibb.rs b/src/ibb.rs index cc2d89c3c11fba3eb704ee87da6ed11c674a5f76..d7717a7b7105cc23258c0d0f36faec4af7b4b089 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -8,11 +8,11 @@ use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; -use base64; use error::Error; use ns; +use helpers::Base64; generate_attribute!(Stanza, "stanza", { Iq => "iq", @@ -25,19 +25,6 @@ generate_element_with_only_attributes!(Open, "open", ns::IBB, [ stanza: Stanza = "stanza" => default, ]); -/// Codec wrapping base64 encode/decode -struct Base64; - -impl Base64 { - fn decode(s: &str) -> Result, Error> { - Ok(base64::decode(s)?) - } - - fn encode(b: &Vec) -> String { - base64::encode(b) - } -} - generate_element_with_text!(Data, "data", ns::IBB, [ seq: u16 = "seq" => required, diff --git a/src/lib.rs b/src/lib.rs index e5b5439c1c9f04b0926a4225624ec48ad2a61078..2bc12e05e40ff2e0ba15a3053fb48f483204ff97 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,6 +37,8 @@ extern crate try_from; pub mod error; /// XML namespace definitions used through XMPP. pub mod ns; +/// Various helpers. +pub mod helpers; /// Helper macros to parse and serialise more easily. #[macro_use] pub mod macros; From 3f57edfc277c81d1d83b7f0447b33163dda95ef2 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 23 Nov 2017 15:52:06 +0000 Subject: [PATCH 377/698] hashes: Use the new helpers. --- src/hashes.rs | 49 ++++++++++--------------------------------------- 1 file changed, 10 insertions(+), 39 deletions(-) diff --git a/src/hashes.rs b/src/hashes.rs index bcaf94ac1e874ca641239c74a8987711364bd500..535f99c536b5ebb4d4a0232cc0eb4af2a0350a88 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -12,8 +12,7 @@ use minidom::{Element, IntoAttributeValue}; use error::Error; use ns; - -use base64; +use helpers::Base64; #[allow(non_camel_case_types)] #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -68,47 +67,19 @@ impl IntoAttributeValue for Algo { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Hash { - pub algo: Algo, - pub hash: Vec, -} - -impl TryFrom for Hash { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("hash", ns::HASHES) { - return Err(Error::ParseError("This is not a hash element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in hash element.")); - } - let algo = get_attr!(elem, "algo", required); - let hash = match elem.text().as_ref() { - "" => return Err(Error::ParseError("Hash element shouldn’t be empty.")), - text => base64::decode(text)?, - }; - Ok(Hash { - algo: algo, - hash: hash, - }) - } -} - -impl From for Element { - fn from(hash: Hash) -> Element { - Element::builder("hash") - .ns(ns::HASHES) - .attr("algo", hash.algo) - .append(base64::encode(&hash.hash)) - .build() - } -} +generate_element_with_text!( + #[derive(PartialEq)] + Hash, "hash", ns::HASHES, + [ + algo: Algo = "algo" => required + ], + hash: Base64> +); #[cfg(test)] mod tests { use super::*; + use base64; #[test] fn test_simple() { From cb6eb55686c07200c2bac2a2c98b87a5fda075d8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 23 Nov 2017 16:06:35 +0000 Subject: [PATCH 378/698] macros: Uniformise trailing commas handling. --- src/macros.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index 136b32f97ce4b1fdd7667b5e53eb8e6d417fe49b..2d02a0330d0f7cf16c76abdd9542d0397ad40f6c 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -179,8 +179,8 @@ macro_rules! generate_element_with_only_attributes { pub struct $elem { $( $(#[$attr_meta])* - pub $attr: $attr_type - ),* + pub $attr: $attr_type, + )* } impl TryFrom for $elem { @@ -192,8 +192,8 @@ macro_rules! generate_element_with_only_attributes { check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); Ok($elem { $( - $attr: get_attr!(elem, $attr_name, $attr_action) - ),* + $attr: get_attr!(elem, $attr_name, $attr_action), + )* }) } } @@ -259,8 +259,8 @@ macro_rules! generate_element_with_text { pub struct $elem { $( $(#[$attr_meta])* - pub $attr: $attr_type - ),*, + pub $attr: $attr_type, + )* pub $text_ident: $text_type, } @@ -273,8 +273,8 @@ macro_rules! generate_element_with_text { check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); Ok($elem { $( - $attr: get_attr!(elem, $attr_name, $attr_action) - ),*, + $attr: get_attr!(elem, $attr_name, $attr_action), + )* $text_ident: $codec::decode(&elem.text())?, }) } @@ -301,12 +301,12 @@ macro_rules! generate_element_with_children { pub struct $elem { $( $(#[$attr_meta])* - pub $attr: $attr_type - ),*, + pub $attr: $attr_type, + )* $( $(#[$child_meta])* - pub $child_ident: Vec<$child_type> - ),* + pub $child_ident: Vec<$child_type>, + )* } impl TryFrom for $elem { @@ -324,10 +324,10 @@ macro_rules! generate_element_with_children { } Ok($elem { $( - $attr: get_attr!(elem, $attr_name, $attr_action) - ),*, + $attr: get_attr!(elem, $attr_name, $attr_action), + )* $( - $child_ident: parsed_children + $child_ident: parsed_children, )* }) } From 32e373be03d78449a3ee76929ff19514ee6a43f4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 23 Nov 2017 16:19:04 +0000 Subject: [PATCH 379/698] helpers: Add a plain text codec. --- src/helpers.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/helpers.rs b/src/helpers.rs index e12000ac9e85540b88839c0cf60b2ea425aab4c5..a457081db560b7f5018ff91fcbffa0eb776e5c26 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -7,6 +7,24 @@ use base64; use error::Error; +/// Codec for plain text content. +pub struct PlainText; + +impl PlainText { + pub fn decode(s: &str) -> Result, Error> { + Ok(match s { + "" => None, + text => Some(text.to_owned()), + }) + } + + pub fn encode(string: &Option) -> Option { + string.as_ref().map(|text| { + text.to_owned() + }) + } +} + /// Codec wrapping base64 encode/decode pub struct Base64; @@ -15,7 +33,7 @@ impl Base64 { Ok(base64::decode(s)?) } - pub fn encode(b: &Vec) -> String { - base64::encode(b) + pub fn encode(b: &Vec) -> Option { + Some(base64::encode(b)) } } From 682bb34b0bf76f1664337e5d1dd639dfca0c86fe Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 23 Nov 2017 16:19:24 +0000 Subject: [PATCH 380/698] delay: Use the new helper macro. --- src/delay.rs | 44 ++++++++------------------------------------ 1 file changed, 8 insertions(+), 36 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index e95ed4c2f81791880fe29ba048be15ba3d4195e2..6904ca838edc35b415f8ad6218d16f9887476b7b 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -13,43 +13,15 @@ use error::Error; use jid::Jid; use ns; +use helpers::PlainText; -#[derive(Debug, Clone)] -pub struct Delay { - pub from: Option, - pub stamp: DateTime, - pub data: Option, -} - -impl TryFrom for Delay { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "delay", ns::DELAY); - check_no_children!(elem, "delay"); - check_no_unknown_attributes!(elem, "delay", ["from", "stamp"]); - let data = match elem.text().as_ref() { - "" => None, - text => Some(text.to_owned()), - }; - Ok(Delay { - from: get_attr!(elem, "from", optional), - stamp: get_attr!(elem, "stamp", required), - data: data, - }) - } -} - -impl From for Element { - fn from(delay: Delay) -> Element { - Element::builder("delay") - .ns(ns::DELAY) - .attr("from", delay.from) - .attr("stamp", delay.stamp) - .append(delay.data) - .build() - } -} +generate_element_with_text!(Delay, "delay", ns::DELAY, + [ + from: Option = "from" => optional, + stamp: DateTime = "stamp" => required + ], + data: PlainText> +); #[cfg(test)] mod tests { From d78a0e6daebdcfce7bc32b02d1a218189cac5686 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 23 Nov 2017 16:30:53 +0000 Subject: [PATCH 381/698] helpers, disco: Add missing serialisation, and add a test. --- src/disco.rs | 2 ++ src/macros.rs | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/disco.rs b/src/disco.rs index b4efeee9d3ad9ed1db0b43a6010547478498f91f..72dc934a499d1e34af95f83720388811084352e0 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -377,6 +377,8 @@ mod tests { fn test_answers_items_result() { let elem: Element = "".parse().unwrap(); let query = DiscoItemsResult::try_from(elem).unwrap(); + let elem2 = Element::from(query); + let query = DiscoItemsResult::try_from(elem2).unwrap(); assert_eq!(query.items.len(), 2); assert_eq!(query.items[0].jid, Jid::from_str("component").unwrap()); assert_eq!(query.items[0].node, None); diff --git a/src/macros.rs b/src/macros.rs index 2d02a0330d0f7cf16c76abdd9542d0397ad40f6c..9177009dcf00129e31fe331a03cfc850a83e1277 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -340,6 +340,9 @@ macro_rules! generate_element_with_children { $( .attr($attr_name, elem.$attr) )* + $( + .append(elem.$child_ident) + )* .build() } } From 0ead24a04189754f08b64a261cb5d7fed9126654 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 23 Nov 2017 16:32:18 +0000 Subject: [PATCH 382/698] helpers, disco: Parse children based on their name and namespace. --- src/disco.rs | 2 +- src/macros.rs | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index 72dc934a499d1e34af95f83720388811084352e0..c48c863c8ce963c6f6bf8d6cb3ebebdd60587b6e 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -214,7 +214,7 @@ generate_element_with_children!( ], children: [ /// List of items pointed by this entity. - items: Vec = "item" => Item + items: Vec = ("item", ns::DISCO_ITEMS) => Item ] ); diff --git a/src/macros.rs b/src/macros.rs index 9177009dcf00129e31fe331a03cfc850a83e1277..b1d9374cceb4598144021fc3319e30f675993045 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -295,7 +295,7 @@ macro_rules! generate_element_with_text { } macro_rules! generate_element_with_children { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = $child_name:tt => $child_constructor:ident),+]) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:expr) => $child_constructor:ident),+]) => ( $(#[$meta])* #[derive(Debug, Clone)] pub struct $elem { @@ -318,9 +318,13 @@ macro_rules! generate_element_with_children { let mut parsed_children = vec!(); for child in elem.children() { $( - let parsed_child = $child_constructor::try_from(child.clone())?; - parsed_children.push(parsed_child); + if child.is($child_name, $child_ns) { + let parsed_child = $child_constructor::try_from(child.clone())?; + parsed_children.push(parsed_child); + continue; + } )* + return Err(Error::ParseError(concat!("Unknown child in ", $name, " element."))); } Ok($elem { $( From 988b6a61601d27bf3fe4bf3b9847647be30e35d4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 23 Nov 2017 16:33:08 +0000 Subject: [PATCH 383/698] jingle_ft: Parse using the new helper. --- src/jingle_ft.rs | 46 +++++++++++----------------------------------- 1 file changed, 11 insertions(+), 35 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 3339e015553ffc80c34b9b77ad66ea431db0dc11..53730a328c756c521838adb4911bf173649e1d01 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -18,41 +18,17 @@ use minidom::{Element, IntoAttributeValue}; use error::Error; use ns; -#[derive(Debug, Clone, PartialEq)] -pub struct Range { - pub offset: u64, - pub length: Option, - pub hashes: Vec, -} - -impl TryFrom for Range { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "range", ns::JINGLE_FT); - check_no_unknown_attributes!(elem, "range", ["offset", "length"]); - let mut hashes = vec!(); - for child in elem.children() { - hashes.push(Hash::try_from(child.clone())?); - } - Ok(Range { - offset: get_attr!(elem, "offset", default), - length: get_attr!(elem, "length", optional), - hashes: hashes, - }) - } -} - -impl From for Element { - fn from(range: Range) -> Element { - Element::builder("range") - .ns(ns::JINGLE_FT) - .attr("offset", if range.offset == 0 { None } else { Some(range.offset) }) - .attr("length", range.length) - .append(range.hashes) - .build() - } -} +generate_element_with_children!( + #[derive(PartialEq)] + Range, "range", ns::JINGLE_FT, + attributes: [ + offset: u64 = "offset" => default, + length: Option = "length" => optional + ], + children: [ + hashes: Vec = ("hash", ns::HASHES) => Hash + ] +); type Lang = String; From e4cfc1b8671cdbc75e75beef4f3ed9b719105e1f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 23 Nov 2017 16:37:04 +0000 Subject: [PATCH 384/698] jingle_ft: Simplify serialisation. --- src/jingle_ft.rs | 54 +++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 53730a328c756c521838adb4911bf173649e1d01..bf584380d40b5c30510bafd78542c62c44a47df1 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -108,46 +108,45 @@ impl TryFrom for File { impl From for Element { fn from(file: File) -> Element { let mut root = Element::builder("file") - .ns(ns::JINGLE_FT) - .build(); + .ns(ns::JINGLE_FT); if let Some(date) = file.date { - root.append_child(Element::builder("date") - .ns(ns::JINGLE_FT) - .append(date) - .build()); + root = root.append(Element::builder("date") + .ns(ns::JINGLE_FT) + .append(date) + .build()); } if let Some(media_type) = file.media_type { - root.append_child(Element::builder("media-type") - .ns(ns::JINGLE_FT) - .append(media_type) - .build()); + root = root.append(Element::builder("media-type") + .ns(ns::JINGLE_FT) + .append(media_type) + .build()); } if let Some(name) = file.name { - root.append_child(Element::builder("name") - .ns(ns::JINGLE_FT) - .append(name) - .build()); + root = root.append(Element::builder("name") + .ns(ns::JINGLE_FT) + .append(name) + .build()); } for (lang, desc) in file.descs.into_iter() { - root.append_child(Element::builder("desc") - .ns(ns::JINGLE_FT) - .attr("xml:lang", lang) - .append(desc.0) - .build()); + root = root.append(Element::builder("desc") + .ns(ns::JINGLE_FT) + .attr("xml:lang", lang) + .append(desc.0) + .build()); } if let Some(size) = file.size { - root.append_child(Element::builder("size") - .ns(ns::JINGLE_FT) - .append(format!("{}", size)) - .build()); + root = root.append(Element::builder("size") + .ns(ns::JINGLE_FT) + .append(format!("{}", size)) + .build()); } if let Some(range) = file.range { - root.append_child(range.into()); + root = root.append(range); } for hash in file.hashes { - root.append_child(hash.into()); + root = root.append(hash); } - root + root.build() } } #[derive(Debug, Clone)] @@ -179,10 +178,9 @@ impl TryFrom for Description { impl From for Element { fn from(description: Description) -> Element { - let file: Element = description.file.into(); Element::builder("description") .ns(ns::JINGLE_FT) - .append(file) + .append(description.file) .build() } } From cec581baa02e3b13505ef4a01b35de33c11fd47a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 23 Nov 2017 16:45:05 +0000 Subject: [PATCH 385/698] roster: Parse query using the new helpers. --- src/roster.rs | 71 +++++++++++++-------------------------------------- 1 file changed, 18 insertions(+), 53 deletions(-) diff --git a/src/roster.rs b/src/roster.rs index 38b183f1d29b6c4dda1a1fb7a750c14e05612724..cb57ed3fde0d0c0847cff03cbcb7fe5a071b3cf5 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -82,57 +82,22 @@ impl From for Element { } } -/// The contact list of the user. -#[derive(Debug, Clone)] -pub struct Roster { - /// Version of the contact list. - /// - /// This is an opaque string that should only be sent back to the server on - /// a new connection, if this client is storing the contact list between - /// connections. - pub ver: Option, - - /// List of the contacts of the user. - pub items: Vec, -} - -impl TryFrom for Roster { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("query", ns::ROSTER) { - return Err(Error::ParseError("This is not a roster element.")); - } - for (attr, _) in elem.attrs() { - if attr != "ver" { - return Err(Error::ParseError("Unknown attribute in roster element.")); - } - } - - let mut roster = Roster { - ver: get_attr!(elem, "ver", optional), - items: vec!(), - }; - for child in elem.children() { - if !child.is("item", ns::ROSTER) { - return Err(Error::ParseError("Unknown element in roster element.")); - } - let item = Item::try_from(child.clone())?; - roster.items.push(item); - } - Ok(roster) - } -} - -impl From for Element { - fn from(roster: Roster) -> Element { - Element::builder("query") - .ns(ns::ROSTER) - .attr("ver", roster.ver) - .append(roster.items) - .build() - } -} +generate_element_with_children!( + /// The contact list of the user. + Roster, "query", ns::ROSTER, + attributes: [ + /// Version of the contact list. + /// + /// This is an opaque string that should only be sent back to the server on + /// a new connection, if this client is storing the contact list between + /// connections. + ver: Option = "ver" => optional + ], + children: [ + /// List of the contacts of the user. + items: Vec = ("item", ns::ROSTER) => Item + ] +); #[cfg(test)] mod tests { @@ -256,7 +221,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown element in roster element."); + assert_eq!(message, "Unknown child in query element."); let elem: Element = "".parse().unwrap(); let error = Roster::try_from(elem).unwrap_err(); @@ -264,7 +229,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown attribute in roster element."); + assert_eq!(message, "Unknown attribute in query element."); } #[test] From 2661259e9aaac6f2bef98a5c3528c18e8d20edfc Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 24 Nov 2017 04:27:35 +0000 Subject: [PATCH 386/698] chatstates: Generate ChatState automatically. --- src/chatstates.rs | 60 ++++++++++++----------------------------------- src/macros.rs | 36 ++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 45 deletions(-) diff --git a/src/chatstates.rs b/src/chatstates.rs index e226f21d3bb72cc620bb3b7485fad57d89602986..6858c1147a012896e0afe467e7d31beabcd1c814 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -14,56 +14,26 @@ use error::Error; use ns; -/// Enum representing chatstate elements part of the -/// `http://jabber.org/protocol/chatstates` namespace. -#[derive(Debug, Clone)] -pub enum ChatState { - /// `` - Active, +generate_element_enum!( + /// Enum representing chatstate elements part of the + /// `http://jabber.org/protocol/chatstates` namespace. + ChatState, "chatstate", ns::CHATSTATES, { + /// `` + Active => "active", - /// `` - Composing, + /// `` + Composing => "composing", - /// `` - Gone, + /// `` + Gone => "gone", - /// `` - Inactive, + /// `` + Inactive => "inactive", - /// `` - Paused, -} - -impl TryFrom for ChatState { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_ns_only!(elem, "chatstate", ns::CHATSTATES); - check_no_children!(elem, "chatstate"); - check_no_attributes!(elem, "chatstate"); - Ok(match elem.name() { - "active" => ChatState::Active, - "composing" => ChatState::Composing, - "gone" => ChatState::Gone, - "inactive" => ChatState::Inactive, - "paused" => ChatState::Paused, - _ => return Err(Error::ParseError("This is not a chatstate element.")), - }) - } -} - -impl From for Element { - fn from(chatstate: ChatState) -> Element { - Element::builder(match chatstate { - ChatState::Active => "active", - ChatState::Composing => "composing", - ChatState::Gone => "gone", - ChatState::Inactive => "inactive", - ChatState::Paused => "paused", - }).ns(ns::CHATSTATES) - .build() + /// `` + Paused => "paused", } -} +); #[cfg(test)] mod tests { diff --git a/src/macros.rs b/src/macros.rs index b1d9374cceb4598144021fc3319e30f675993045..1569470a12ffa4bdece6b75c8859c347518d5408 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -96,6 +96,42 @@ macro_rules! generate_attribute { ); } +macro_rules! generate_element_enum { + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+,}) => ( + generate_element_enum!($(#[$meta])* $elem, $name, $ns, {$($(#[$enum_meta])* $enum => $enum_name),+}); + ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+}) => ( + $(#[$meta])* + #[derive(Debug, Clone, PartialEq)] + pub enum $elem { + $( + $(#[$enum_meta])* + $enum + ),+ + } + impl TryFrom for $elem { + type Err = Error; + fn try_from(elem: Element) -> Result<$elem, Error> { + check_ns_only!(elem, $name, $ns); + check_no_children!(elem, $name); + check_no_attributes!(elem, $name); + Ok(match elem.name() { + $($enum_name => $elem::$enum,)+ + _ => return Err(Error::ParseError(concat!("This is not a ", $name, " element."))), + }) + } + } + impl From<$elem> for Element { + fn from(elem: $elem) -> Element { + Element::builder(match elem { + $($elem::$enum => $enum_name,)+ + }).ns($ns) + .build() + } + } + ); +} + macro_rules! check_self { ($elem:ident, $name:tt, $ns:expr) => ( check_self!($elem, $name, $ns, $name); From ef04d2e52441e2e86053e955de63b982ee8d0bee Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 24 Nov 2017 04:30:33 +0000 Subject: [PATCH 387/698] stanza_error: Generate DefinedCondition automatically. --- src/stanza_error.rs | 115 ++++++++++---------------------------------- 1 file changed, 26 insertions(+), 89 deletions(-) diff --git a/src/stanza_error.rs b/src/stanza_error.rs index bbc31623bcf98827e438b2470255887de90c1096..4548864cf4e59116ee4c6c63b0e17f86ef78e8a0 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -8,7 +8,7 @@ use try_from::TryFrom; use std::str::FromStr; use std::collections::BTreeMap; -use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; +use minidom::{Element, IntoAttributeValue}; use error::Error; use jid::Jid; @@ -22,93 +22,30 @@ generate_attribute!(ErrorType, "type", { Wait => "wait", }); -#[derive(Debug, Clone, PartialEq)] -pub enum DefinedCondition { - BadRequest, - Conflict, - FeatureNotImplemented, - Forbidden, - Gone, - InternalServerError, - ItemNotFound, - JidMalformed, - NotAcceptable, - NotAllowed, - NotAuthorized, - PolicyViolation, - RecipientUnavailable, - Redirect, - RegistrationRequired, - RemoteServerNotFound, - RemoteServerTimeout, - ResourceConstraint, - ServiceUnavailable, - SubscriptionRequired, - UndefinedCondition, - UnexpectedRequest, -} - -impl FromStr for DefinedCondition { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "bad-request" => DefinedCondition::BadRequest, - "conflict" => DefinedCondition::Conflict, - "feature-not-implemented" => DefinedCondition::FeatureNotImplemented, - "forbidden" => DefinedCondition::Forbidden, - "gone" => DefinedCondition::Gone, - "internal-server-error" => DefinedCondition::InternalServerError, - "item-not-found" => DefinedCondition::ItemNotFound, - "jid-malformed" => DefinedCondition::JidMalformed, - "not-acceptable" => DefinedCondition::NotAcceptable, - "not-allowed" => DefinedCondition::NotAllowed, - "not-authorized" => DefinedCondition::NotAuthorized, - "policy-violation" => DefinedCondition::PolicyViolation, - "recipient-unavailable" => DefinedCondition::RecipientUnavailable, - "redirect" => DefinedCondition::Redirect, - "registration-required" => DefinedCondition::RegistrationRequired, - "remote-server-not-found" => DefinedCondition::RemoteServerNotFound, - "remote-server-timeout" => DefinedCondition::RemoteServerTimeout, - "resource-constraint" => DefinedCondition::ResourceConstraint, - "service-unavailable" => DefinedCondition::ServiceUnavailable, - "subscription-required" => DefinedCondition::SubscriptionRequired, - "undefined-condition" => DefinedCondition::UndefinedCondition, - "unexpected-request" => DefinedCondition::UnexpectedRequest, - - _ => return Err(Error::ParseError("Unknown defined-condition.")), - }) - } -} - -impl IntoElements for DefinedCondition { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(Element::builder(match self { - DefinedCondition::BadRequest => "bad-request", - DefinedCondition::Conflict => "conflict", - DefinedCondition::FeatureNotImplemented => "feature-not-implemented", - DefinedCondition::Forbidden => "forbidden", - DefinedCondition::Gone => "gone", - DefinedCondition::InternalServerError => "internal-server-error", - DefinedCondition::ItemNotFound => "item-not-found", - DefinedCondition::JidMalformed => "jid-malformed", - DefinedCondition::NotAcceptable => "not-acceptable", - DefinedCondition::NotAllowed => "not-allowed", - DefinedCondition::NotAuthorized => "not-authorized", - DefinedCondition::PolicyViolation => "policy-violation", - DefinedCondition::RecipientUnavailable => "recipient-unavailable", - DefinedCondition::Redirect => "redirect", - DefinedCondition::RegistrationRequired => "registration-required", - DefinedCondition::RemoteServerNotFound => "remote-server-not-found", - DefinedCondition::RemoteServerTimeout => "remote-server-timeout", - DefinedCondition::ResourceConstraint => "resource-constraint", - DefinedCondition::ServiceUnavailable => "service-unavailable", - DefinedCondition::SubscriptionRequired => "subscription-required", - DefinedCondition::UndefinedCondition => "undefined-condition", - DefinedCondition::UnexpectedRequest => "unexpected-request", - }).ns(ns::XMPP_STANZAS).build()); - } -} +generate_element_enum!(DefinedCondition, "condition", ns::XMPP_STANZAS, { + BadRequest => "bad-request", + Conflict => "conflict", + FeatureNotImplemented => "feature-not-implemented", + Forbidden => "forbidden", + Gone => "gone", + InternalServerError => "internal-server-error", + ItemNotFound => "item-not-found", + JidMalformed => "jid-malformed", + NotAcceptable => "not-acceptable", + NotAllowed => "not-allowed", + NotAuthorized => "not-authorized", + PolicyViolation => "policy-violation", + RecipientUnavailable => "recipient-unavailable", + Redirect => "redirect", + RegistrationRequired => "registration-required", + RemoteServerNotFound => "remote-server-not-found", + RemoteServerTimeout => "remote-server-timeout", + ResourceConstraint => "resource-constraint", + ServiceUnavailable => "service-unavailable", + SubscriptionRequired => "subscription-required", + UndefinedCondition => "undefined-condition", + UnexpectedRequest => "unexpected-request", +}); pub type Lang = String; @@ -151,7 +88,7 @@ impl TryFrom for StanzaError { for _ in child.children() { return Err(Error::ParseError("Unknown element in defined-condition.")); } - let condition = DefinedCondition::from_str(child.name())?; + let condition = DefinedCondition::try_from(child.clone())?; defined_condition = Some(condition); } else { if other.is_some() { From 6533395ec3d8d5adb9a40b036f0d45f3f4cbce79 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 24 Nov 2017 04:46:08 +0000 Subject: [PATCH 388/698] media_element: Simplify parsing of URI. --- src/helpers.rs | 16 ++++++++++++++++ src/media_element.rs | 42 ++++++++---------------------------------- 2 files changed, 24 insertions(+), 34 deletions(-) diff --git a/src/helpers.rs b/src/helpers.rs index a457081db560b7f5018ff91fcbffa0eb776e5c26..4db2d2c1c729512d6772a9bd2a075b0024ed28b5 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -25,6 +25,22 @@ impl PlainText { } } +/// Codec for trimmed plain text content. +pub struct TrimmedPlainText; + +impl TrimmedPlainText { + pub fn decode(s: &str) -> Result { + Ok(match s.trim() { + "" => return Err(Error::ParseError("URI missing in uri.")), + text => text.to_owned(), + }) + } + + pub fn encode(string: &String) -> String { + string.to_owned() + } +} + /// Codec wrapping base64 encode/decode pub struct Base64; diff --git a/src/media_element.rs b/src/media_element.rs index b700eec1af6e5fd512a5055f5472493ea636ca01..0ef386a4f8d9c33b6347849e6f9fe592ac7b2008 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -11,40 +11,14 @@ use minidom::Element; use error::Error; use ns; - -#[derive(Debug, Clone)] -pub struct URI { - pub type_: String, - pub uri: String, -} - -impl TryFrom for URI { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "uri", ns::MEDIA_ELEMENT); - check_no_unknown_attributes!(elem, "uri", ["type"]); - check_no_children!(elem, "uri"); - let uri = elem.text().trim().to_owned(); - if uri == "" { - return Err(Error::ParseError("URI missing in uri.")); - } - Ok(URI { - type_: get_attr!(elem, "type", required), - uri: uri, - }) - } -} - -impl From for Element { - fn from(uri: URI) -> Element { - Element::builder("uri") - .ns(ns::MEDIA_ELEMENT) - .attr("type", uri.type_) - .append(uri.uri) - .build() - } -} +use helpers::TrimmedPlainText; + +generate_element_with_text!(URI, "uri", ns::MEDIA_ELEMENT, + [ + type_: String = "type" => required + ], + uri: TrimmedPlainText +); #[derive(Debug, Clone)] pub struct MediaElement { From 80a2f425e2fbab76d97619e9d89ec18777f0571c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 24 Nov 2017 04:48:31 +0000 Subject: [PATCH 389/698] media_element: Simplify parsing of MediaElement. --- src/media_element.rs | 49 ++++++++------------------------------------ 1 file changed, 9 insertions(+), 40 deletions(-) diff --git a/src/media_element.rs b/src/media_element.rs index 0ef386a4f8d9c33b6347849e6f9fe592ac7b2008..db91c44f44128c6d20bf0b5a9f47929f61486c74 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -20,46 +20,15 @@ generate_element_with_text!(URI, "uri", ns::MEDIA_ELEMENT, uri: TrimmedPlainText ); -#[derive(Debug, Clone)] -pub struct MediaElement { - pub width: Option, - pub height: Option, - pub uris: Vec, -} - -impl TryFrom for MediaElement { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "media", ns::MEDIA_ELEMENT); - check_no_unknown_attributes!(elem, "media", ["width", "height"]); - - let mut media = MediaElement { - width: get_attr!(elem, "width", optional), - height: get_attr!(elem, "height", optional), - uris: vec!(), - }; - for child in elem.children() { - if child.is("uri", ns::MEDIA_ELEMENT) { - media.uris.push(URI::try_from(child.clone())?); - } else { - return Err(Error::ParseError("Unknown child in media element.")); - } - } - Ok(media) - } -} - -impl From for Element { - fn from(media: MediaElement) -> Element { - Element::builder("media") - .ns(ns::MEDIA_ELEMENT) - .attr("width", media.width) - .attr("height", media.height) - .append(media.uris) - .build() - } -} +generate_element_with_children!(MediaElement, "media", ns::MEDIA_ELEMENT, + attributes: [ + width: Option = "width" => optional, + height: Option = "height" => optional + ], + children: [ + uris: Vec = ("uri", ns::MEDIA_ELEMENT) => URI + ] +); #[cfg(test)] mod tests { From 42a3e425337c9b0b3bee78f43c03486825106650 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 24 Nov 2017 05:09:25 +0000 Subject: [PATCH 390/698] roster: Simplify parsing of Item. --- src/macros.rs | 17 ++++++++++++ src/roster.rs | 74 +++++++++++++-------------------------------------- 2 files changed, 35 insertions(+), 56 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index 1569470a12ffa4bdece6b75c8859c347518d5408..b084207aaff47a84960b5e7eb36041f07748f372 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -8,6 +8,13 @@ macro_rules! get_attr { ($elem:ident, $attr:tt, $type:tt) => ( get_attr!($elem, $attr, $type, value, value.parse()?) ); + ($elem:ident, $attr:tt, optional_empty, $value:ident, $func:expr) => ( + match $elem.attr($attr) { + Some("") => None, + Some($value) => Some($func), + None => None, + } + ); ($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => ( match $elem.attr($attr) { Some($value) => Some($func), @@ -277,6 +284,16 @@ macro_rules! generate_elem_id { Ok($elem(String::from(s))) } } + impl TryFrom for $elem { + type Err = Error; + fn try_from(elem: Element) -> Result<$elem, Error> { + check_self!(elem, $name, $ns); + check_no_children!(elem, $name); + check_no_attributes!(elem, $name); + // TODO: add a way to parse that differently when needed. + Ok($elem(elem.text())) + } + } impl From<$elem> for Element { fn from(elem: $elem) -> Element { Element::builder($name) diff --git a/src/roster.rs b/src/roster.rs index cb57ed3fde0d0c0847cff03cbcb7fe5a071b3cf5..cb78e4ef5a2f1c60f5b00bba931b913ecd9fdc44 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -23,64 +23,26 @@ generate_attribute!(Subscription, "subscription", { Remove => "remove", }, Default = None); -/// Contact from the user’s contact list. -#[derive(Debug, Clone, PartialEq)] -pub struct Item { - /// JID of this contact. - pub jid: Jid, - - /// Name of this contact. - pub name: Option, - - /// Subscription status of this contact. - pub subscription: Subscription, - - /// Groups this contact is part of. - pub groups: Vec, -} - -impl TryFrom for Item { - type Err = Error; +generate_element_with_children!( + /// Contact from the user’s contact list. + #[derive(PartialEq)] + Item, "item", ns::ROSTER, + attributes: [ + /// JID of this contact. + jid: Jid = "jid" => required, - fn try_from(elem: Element) -> Result { - if !elem.is("item", ns::ROSTER) { - return Err(Error::ParseError("This is not a roster item element.")); - } + /// Name of this contact. + name: Option = "name" => optional_empty, - let mut item = Item { - jid: get_attr!(elem, "jid", required), - name: get_attr!(elem, "name", optional).and_then(|name| if name == "" { None } else { Some(name) }), - subscription: get_attr!(elem, "subscription", default), - groups: vec!(), - }; - for child in elem.children() { - if !child.is("group", ns::ROSTER) { - return Err(Error::ParseError("Unknown element in roster item element.")); - } - for _ in child.children() { - return Err(Error::ParseError("Roster item group can’t have children.")); - } - for _ in child.attrs() { - return Err(Error::ParseError("Roster item group can’t have attributes.")); - } - let group = Group(child.text()); - item.groups.push(group); - } - Ok(item) - } -} + /// Subscription status of this contact. + subscription: Subscription = "subscription" => default + ], -impl From for Element { - fn from(item: Item) -> Element { - Element::builder("item") - .ns(ns::ROSTER) - .attr("jid", item.jid) - .attr("name", item.name) - .attr("subscription", item.subscription) - .append(item.groups) - .build() - } -} + children: [ + /// Groups this contact is part of. + groups: Vec = ("group", ns::ROSTER) => Group + ] +); generate_element_with_children!( /// The contact list of the user. @@ -258,6 +220,6 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown element in roster item element."); + assert_eq!(message, "Unknown child in item element."); } } From 5a2f4859cffed6ff00cd1f30676a30bbc590fdf2 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 24 Nov 2017 05:20:36 +0000 Subject: [PATCH 391/698] Add a mood parser and serialiser. --- src/lib.rs | 3 + src/mood.rs | 284 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/ns.rs | 3 + 3 files changed, 290 insertions(+) create mode 100644 src/mood.rs diff --git a/src/lib.rs b/src/lib.rs index 2bc12e05e40ff2e0ba15a3053fb48f483204ff97..415572c0f4092d811e57ae196363a9714a532da1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,6 +89,9 @@ pub mod chatstates; /// XEP-0092: Software Version pub mod version; +/// XEP-0107: User Mood +pub mod mood; + /// XEP-0115: Entity Capabilities pub mod caps; diff --git a/src/mood.rs b/src/mood.rs new file mode 100644 index 0000000000000000000000000000000000000000..c83915be62d0d68c595c3352c76bf4bcc802f73d --- /dev/null +++ b/src/mood.rs @@ -0,0 +1,284 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#![deny(missing_docs)] + +use try_from::TryFrom; + +use minidom::Element; + +use error::Error; + +use ns; + +generate_element_enum!( + /// Enum representing all of the possible values of the XEP-0107 moods. + MoodEnum, "mood", ns::MOOD, { + /// Impressed with fear or apprehension; in fear; apprehensive. + Afraid => "afraid", + + /// Astonished; confounded with fear, surprise or wonder. + Amazed => "amazed", + + /// Inclined to love; having a propensity to love, or to sexual enjoyment; loving, fond, affectionate, passionate, lustful, sexual, etc. + Amorous => "amorous", + + /// Displaying or feeling anger, i.e., a strong feeling of displeasure, hostility or antagonism towards someone or something, usually combined with an urge to harm. + Angry => "angry", + + /// To be disturbed or irritated, especially by continued or repeated acts. + Annoyed => "annoyed", + + /// Full of anxiety or disquietude; greatly concerned or solicitous, esp. respecting something future or unknown; being in painful suspense. + Anxious => "anxious", + + /// To be stimulated in one's feelings, especially to be sexually stimulated. + Aroused => "aroused", + + /// Feeling shame or guilt. + Ashamed => "ashamed", + + /// Suffering from boredom; uninterested, without attention. + Bored => "bored", + + /// Strong in the face of fear; courageous. + Brave => "brave", + + /// Peaceful, quiet. + Calm => "calm", + + /// Taking care or caution; tentative. + Cautious => "cautious", + + /// Feeling the sensation of coldness, especially to the point of discomfort. + Cold => "cold", + + /// Feeling very sure of or positive about something, especially about one's own capabilities. + Confident => "confident", + + /// Chaotic, jumbled or muddled. + Confused => "confused", + + /// Feeling introspective or thoughtful. + Contemplative => "contemplative", + + /// Pleased at the satisfaction of a want or desire; satisfied. + Contented => "contented", + + /// Grouchy, irritable; easily upset. + Cranky => "cranky", + + /// Feeling out of control; feeling overly excited or enthusiastic. + Crazy => "crazy", + + /// Feeling original, expressive, or imaginative. + Creative => "creative", + + /// Inquisitive; tending to ask questions, investigate, or explore. + Curious => "curious", + + /// Feeling sad and dispirited. + Dejected => "dejected", + + /// Severely despondent and unhappy. + Depressed => "depressed", + + /// Defeated of expectation or hope; let down. + Disappointed => "disappointed", + + /// Filled with disgust; irritated and out of patience. + Disgusted => "disgusted", + + /// Feeling a sudden or complete loss of courage in the face of trouble or danger. + Dismayed => "dismayed", + + /// Having one's attention diverted; preoccupied. + Distracted => "distracted", + + /// Having a feeling of shameful discomfort. + Embarrassed => "embarrassed", + + /// Feeling pain by the excellence or good fortune of another. + Envious => "envious", + + /// Having great enthusiasm. + Excited => "excited", + + /// In the mood for flirting. + Flirtatious => "flirtatious", + + /// Suffering from frustration; dissatisfied, agitated, or discontented because one is unable to perform an action or fulfill a desire. + Frustrated => "frustrated", + + /// Feeling appreciation or thanks. + Grateful => "grateful", + + /// Feeling very sad about something, especially something lost; mournful; sorrowful. + Grieving => "grieving", + + /// Unhappy and irritable. + Grumpy => "grumpy", + + /// Feeling responsible for wrongdoing; feeling blameworthy. + Guilty => "guilty", + + /// Experiencing the effect of favourable fortune; having the feeling arising from the consciousness of well-being or of enjoyment; enjoying good of any kind, as peace, tranquillity, comfort; contented; joyous. + Happy => "happy", + + /// Having a positive feeling, belief, or expectation that something wished for can or will happen. + Hopeful => "hopeful", + + /// Feeling the sensation of heat, especially to the point of discomfort. + Hot => "hot", + + /// Having or showing a modest or low estimate of one's own importance; feeling lowered in dignity or importance. + Humbled => "humbled", + + /// Feeling deprived of dignity or self-respect. + Humiliated => "humiliated", + + /// Having a physical need for food. + Hungry => "hungry", + + /// Wounded, injured, or pained, whether physically or emotionally. + Hurt => "hurt", + + /// Favourably affected by something or someone. + Impressed => "impressed", + + /// Feeling amazement at something or someone; or feeling a combination of fear and reverence. + InAwe => "in_awe", + + /// Feeling strong affection, care, liking, or attraction.. + InLove => "in_love", + + /// Showing anger or indignation, especially at something unjust or wrong. + Indignant => "indignant", + + /// Showing great attention to something or someone; having or showing interest. + Interested => "interested", + + /// Under the influence of alcohol; drunk. + Intoxicated => "intoxicated", + + /// Feeling as if one cannot be defeated, overcome or denied. + Invincible => "invincible", + + /// Fearful of being replaced in position or affection. + Jealous => "jealous", + + /// Feeling isolated, empty, or abandoned. + Lonely => "lonely", + + /// Unable to find one's way, either physically or emotionally. + Lost => "lost", + + /// Feeling as if one will be favored by luck. + Lucky => "lucky", + + /// Causing or intending to cause intentional harm; bearing ill will towards another; cruel; malicious. + Mean => "mean", + + /// Given to sudden or frequent changes of mind or feeling; temperamental. + Moody => "moody", + + /// Easily agitated or alarmed; apprehensive or anxious. + Nervous => "nervous", + + /// Not having a strong mood or emotional state. + Neutral => "neutral", + + /// Feeling emotionally hurt, displeased, or insulted. + Offended => "offended", + + /// Feeling resentful anger caused by an extremely violent or vicious attack, or by an offensive, immoral, or indecent act. + Outraged => "outraged", + + /// Interested in play; fun, recreational, unserious, lighthearted; joking, silly. + Playful => "playful", + + /// Feeling a sense of one's own worth or accomplishment. + Proud => "proud", + + /// Having an easy-going mood; not stressed; calm. + Relaxed => "relaxed", + + /// Feeling uplifted because of the removal of stress or discomfort. + Relieved => "relieved", + + /// Feeling regret or sadness for doing something wrong. + Remorseful => "remorseful", + + /// Without rest; unable to be still or quiet; uneasy; continually moving. + Restless => "restless", + + /// Feeling sorrow; sorrowful, mournful. + Sad => "sad", + + /// Mocking and ironical. + Sarcastic => "sarcastic", + + /// Pleased at the fulfillment of a need or desire. + Satisfied => "satisfied", + + /// Without humor or expression of happiness; grave in manner or disposition; earnest; thoughtful; solemn. + Serious => "serious", + + /// Surprised, startled, confused, or taken aback. + Shocked => "shocked", + + /// Feeling easily frightened or scared; timid; reserved or coy. + Shy => "shy", + + /// Feeling in poor health; ill. + Sick => "sick", + + /// Feeling the need for sleep. + Sleepy => "sleepy", + + /// Acting without planning; natural; impulsive. + Spontaneous => "spontaneous", + + /// Suffering emotional pressure. + Stressed => "stressed", + + /// Capable of producing great physical force; or, emotionally forceful, able, determined, unyielding. + Strong => "strong", + + /// Experiencing a feeling caused by something unexpected. + Surprised => "surprised", + + /// Showing appreciation or gratitude. + Thankful => "thankful", + + /// Feeling the need to drink. + Thirsty => "thirsty", + + /// In need of rest or sleep. + Tired => "tired", + + /// [Feeling any emotion not defined here.] + Undefined => "undefined", + + /// Lacking in force or ability, either physical or emotional. + Weak => "weak", + + /// Thinking about unpleasant things that have happened or that might happen; feeling afraid and unhappy. + Worried => "worried", + } +); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let mood = MoodEnum::try_from(elem).unwrap(); + assert_eq!(mood, MoodEnum::Happy); + } +} diff --git a/src/ns.rs b/src/ns.rs index 045957d5b6125648eb2905fdfcb2e850ba292b73..b8a10bca58e0eca69cc60c3445c392dace9b7662 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -50,6 +50,9 @@ pub const CHATSTATES: &str = "http://jabber.org/protocol/chatstates"; /// XEP-0092: Software Version pub const VERSION: &str = "jabber:iq:version"; +/// XEP-0107: User Mood +pub const MOOD: &str = "http://jabber.org/protocol/mood"; + /// XEP-0114: Jabber Component Protocol pub const COMPONENT_ACCEPT: &str = "jabber:component:accept"; From 32f427a73c4aa24d07ccf9d13beefd92e0d36038 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 24 Nov 2017 05:24:12 +0000 Subject: [PATCH 392/698] muc/user: Generate Reason automatically. --- src/muc/user.rs | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/src/muc/user.rs b/src/muc/user.rs index fb30967993d0d417959a373a168f9a98232590fd..6c25209fb12eb2219913d04905729d5a802a5c0b 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -196,34 +196,7 @@ generate_element_with_only_attributes!(Continue, "continue", ns::MUC_USER, [ thread: Option = "thread" => optional, ]); -#[derive(Debug, Clone, PartialEq)] -pub struct Reason(String); - -impl TryFrom for Reason { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("reason", ns::MUC_USER) { - return Err(Error::ParseError("This is not a reason element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in reason element.")); - } - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown attribute in reason element.")); - } - Ok(Reason(elem.text())) - } -} - -impl From for Element { - fn from(reason: Reason) -> Element { - Element::builder("reason") - .ns(ns::MUC_USER) - .append(reason.0) - .build() - } -} +generate_elem_id!(Reason, "reason", ns::MUC_USER); generate_attribute!(Affiliation, "affiliation", { Owner => "owner", From 75625c497c78b5e7a91c345634414fea5e90ecfe Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 24 Nov 2017 05:44:58 +0000 Subject: [PATCH 393/698] muc/user: Simplify Status with a new macro. --- src/macros.rs | 38 ++++++++++++++++ src/muc/user.rs | 112 +++++++++--------------------------------------- 2 files changed, 59 insertions(+), 91 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index b084207aaff47a84960b5e7eb36041f07748f372..8fb573637cede8dd4df0e94d68a0967f41b4488e 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -139,6 +139,44 @@ macro_rules! generate_element_enum { ); } +macro_rules! generate_attribute_enum { + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, $attr:tt, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+,}) => ( + generate_attribute_enum!($(#[$meta])* $elem, $name, $ns, $attr, {$($(#[$enum_meta])* $enum => $enum_name),+}); + ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, $attr:tt, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+}) => ( + $(#[$meta])* + #[derive(Debug, Clone, PartialEq)] + pub enum $elem { + $( + $(#[$enum_meta])* + $enum + ),+ + } + impl TryFrom for $elem { + type Err = Error; + fn try_from(elem: Element) -> Result<$elem, Error> { + check_ns_only!(elem, $name, $ns); + check_no_children!(elem, $name); + check_no_unknown_attributes!(elem, $name, [$attr]); + Ok(match get_attr!(elem, $attr, required) { + $($enum_name => $elem::$enum,)+ + _ => return Err(Error::ParseError(concat!("Invalid ", $name, " ", $attr, " value."))), + }) + } + } + impl From<$elem> for Element { + fn from(elem: $elem) -> Element { + Element::builder($name) + .ns($ns) + .attr($attr, match elem { + $($elem::$enum => $enum_name,)+ + }) + .build() + } + } + ); +} + macro_rules! check_self { ($elem:ident, $name:tt, $ns:expr) => ( check_self!($elem, $name, $ns, $name); diff --git a/src/muc/user.rs b/src/muc/user.rs index 6c25209fb12eb2219913d04905729d5a802a5c0b..deff9452b84a512dcebc9b28650e1d1e6734b260 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -16,131 +16,61 @@ use error::Error; use ns; -#[derive(Debug, Clone, PartialEq)] -pub enum Status { +generate_attribute_enum!(Status, "status", ns::MUC_USER, "code", { /// Status: 100 - NonAnonymousRoom, + NonAnonymousRoom => 100, /// Status: 101 - AffiliationChange, + AffiliationChange => 101, /// Status: 102 - ConfigShowsUnavailableMembers, + ConfigShowsUnavailableMembers => 102, /// Status: 103 - ConfigHidesUnavailableMembers, + ConfigHidesUnavailableMembers => 103, /// Status: 104 - ConfigNonPrivacyRelated, + ConfigNonPrivacyRelated => 104, /// Status: 110 - SelfPresence, + SelfPresence => 110, /// Status: 170 - ConfigRoomLoggingEnabled, + ConfigRoomLoggingEnabled => 170, /// Status: 171 - ConfigRoomLoggingDisabled, + ConfigRoomLoggingDisabled => 171, /// Status: 172 - ConfigRoomNonAnonymous, + ConfigRoomNonAnonymous => 172, /// Status: 173 - ConfigRoomSemiAnonymous, + ConfigRoomSemiAnonymous => 173, /// Status: 201 - RoomHasBeenCreated, + RoomHasBeenCreated => 201, /// Status: 210 - AssignedNick, + AssignedNick => 210, /// Status: 301 - Banned, + Banned => 301, /// Status: 303 - NewNick, + NewNick => 303, /// Status: 307 - Kicked, + Kicked => 307, /// Status: 321 - RemovalFromRoom, + RemovalFromRoom => 321, /// Status: 322 - ConfigMembersOnly, + ConfigMembersOnly => 322, /// Status: 332 - ServiceShutdown, -} - -impl TryFrom for Status { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("status", ns::MUC_USER) { - return Err(Error::ParseError("This is not a status element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in status element.")); - } - for (attr, _) in elem.attrs() { - if attr != "code" { - return Err(Error::ParseError("Unknown attribute in status element.")); - } - } - let code = get_attr!(elem, "code", required); - - Ok(match code { - 100 => Status::NonAnonymousRoom, - 101 => Status::AffiliationChange, - 102 => Status::ConfigShowsUnavailableMembers, - 103 => Status::ConfigHidesUnavailableMembers, - 104 => Status::ConfigNonPrivacyRelated, - 110 => Status::SelfPresence, - 170 => Status::ConfigRoomLoggingEnabled, - 171 => Status::ConfigRoomLoggingDisabled, - 172 => Status::ConfigRoomNonAnonymous, - 173 => Status::ConfigRoomSemiAnonymous, - 201 => Status::RoomHasBeenCreated, - 210 => Status::AssignedNick, - 301 => Status::Banned, - 303 => Status::NewNick, - 307 => Status::Kicked, - 321 => Status::RemovalFromRoom, - 322 => Status::ConfigMembersOnly, - 332 => Status::ServiceShutdown, - _ => return Err(Error::ParseError("Invalid status code.")), - }) - } -} - -impl From for Element { - fn from(status: Status) -> Element { - Element::builder("status") - .ns(ns::MUC_USER) - .attr("code", match status { - Status::NonAnonymousRoom => 100, - Status::AffiliationChange => 101, - Status::ConfigShowsUnavailableMembers => 102, - Status::ConfigHidesUnavailableMembers => 103, - Status::ConfigNonPrivacyRelated => 104, - Status::SelfPresence => 110, - Status::ConfigRoomLoggingEnabled => 170, - Status::ConfigRoomLoggingDisabled => 171, - Status::ConfigRoomNonAnonymous => 172, - Status::ConfigRoomSemiAnonymous => 173, - Status::RoomHasBeenCreated => 201, - Status::AssignedNick => 210, - Status::Banned => 301, - Status::NewNick => 303, - Status::Kicked => 307, - Status::RemovalFromRoom => 321, - Status::ConfigMembersOnly => 322, - Status::ServiceShutdown => 332, - }) - .build() - } -} + ServiceShutdown => 332, +}); /// Optional element used in elements inside presence stanzas of type /// "unavailable" that are sent to users who are kick or banned, as well as within IQs for tracking @@ -434,7 +364,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Invalid status code."); + assert_eq!(message, "Invalid status code value."); } #[test] From 93c7ea69143e30075323fdb9c6c6d4f47938c925 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 24 Nov 2017 05:50:24 +0000 Subject: [PATCH 394/698] muc/user: Improve documentation of Status. --- src/muc/user.rs | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/muc/user.rs b/src/muc/user.rs index deff9452b84a512dcebc9b28650e1d1e6734b260..d09c73c4d6b6d01be32301383003f6cdfa454997 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -16,59 +16,65 @@ use error::Error; use ns; -generate_attribute_enum!(Status, "status", ns::MUC_USER, "code", { - /// Status: 100 +generate_attribute_enum!( +/// Lists all of the possible status codes used in MUC presences. +Status, "status", ns::MUC_USER, "code", { + /// Inform user that any occupant is allowed to see the user's full JID NonAnonymousRoom => 100, - /// Status: 101 + /// Inform user that his or her affiliation changed while not in the room AffiliationChange => 101, - /// Status: 102 + /// Inform occupants that room now shows unavailable members ConfigShowsUnavailableMembers => 102, - /// Status: 103 + /// Inform occupants that room now does not show unavailable members ConfigHidesUnavailableMembers => 103, - /// Status: 104 + /// Inform occupants that a non-privacy-related room configuration change has occurred ConfigNonPrivacyRelated => 104, - /// Status: 110 + /// Inform user that presence refers to itself SelfPresence => 110, - /// Status: 170 + /// Inform occupants that room logging is now enabled ConfigRoomLoggingEnabled => 170, - /// Status: 171 + /// Inform occupants that room logging is now disabled ConfigRoomLoggingDisabled => 171, - /// Status: 172 + /// Inform occupants that the room is now non-anonymous ConfigRoomNonAnonymous => 172, - /// Status: 173 + /// Inform occupants that the room is now semi-anonymous ConfigRoomSemiAnonymous => 173, - /// Status: 201 + /// Inform user that a new room has been created RoomHasBeenCreated => 201, - /// Status: 210 + /// Inform user that service has assigned or modified occupant's roomnick AssignedNick => 210, - /// Status: 301 + /// Inform user that he or she has been banned from the room Banned => 301, - /// Status: 303 + /// Inform all occupants of new room nickname NewNick => 303, - /// Status: 307 + /// Inform user that he or she has been kicked from the room Kicked => 307, - /// Status: 321 + /// Inform user that he or she is being removed from the room + /// because of an affiliation change RemovalFromRoom => 321, - /// Status: 322 + /// Inform user that he or she is being removed from the room + /// because the room has been changed to members-only and the + /// user is not a member ConfigMembersOnly => 322, - /// Status: 332 + /// Inform user that he or she is being removed from the room + /// because the MUC service is being shut down ServiceShutdown => 332, }); From 023ba2b9f0dfc1dc9a96e84d7c820e91caea7580 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 27 Dec 2017 16:52:28 +0100 Subject: [PATCH 395/698] Cargo.toml: Bump minidom and jid. --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4cf04e7430fb0e36ab5b639b7a40f804a7b5ec0c..713b00a0c12885fb81d6226e4626ef8ef9e71274 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,8 +13,8 @@ categories = ["parsing", "network-programming"] license = "MPL-2.0" [dependencies] -minidom = "0.6.2" -jid = { version = "0.3.1", features = ["minidom"] } +minidom = { version = "0.7.0" } +jid = { version = "0.4.0", features = ["minidom"] } base64 = "0.7.0" digest = "0.7.1" sha-1 = "0.7.0" From efdda7404c6f397f5fcfa609189556215c9a5a78 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 27 Dec 2017 17:32:43 +0100 Subject: [PATCH 396/698] ChangeLog: Add imminent vesion 0.9.0. --- ChangeLog | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ChangeLog b/ChangeLog index 3f86f90e3c06d3db4fee2a1f2d365a0201e208af..e07a50a5f26905ffd09212fe1bac79e16b074459 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,23 @@ +Version 0.9.0: +2017-10-31 Emmanuel Gil Peyrot + * New parsers/serialisers: + - Blocking Command (XEP-0191) has been added. + - Date and Time Profiles (XEP-0082) has been added, replacing + ad-hoc use of chrono in various places. + - User Mood (XEP-0107) has been added. + * Breaking changes: + - Fix subscription="none" not being the default. + - Add more type safety to pubsub#event. + - Reuse Jingle’s ContentId type in JingleFT. + - Import the disposition attribute values in Jingle. + * Improvements: + - Refactor a good part of the code using macros. + - Simplify the parsing code wherever it makes sense. + - Check for children ordering in disco#info result. + - Finish implementation of , and + in JingleFT. + - Correctly serialise , and test it. + Version 0.8.0: 2017-08-27 Emmanuel Gil Peyrot * New parsers/serialisers: From 9cd34901002d6405f7746ce11f7e34c757f5fe02 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 27 Dec 2017 17:35:29 +0100 Subject: [PATCH 397/698] Cargo.toml: Bump base64 version. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 713b00a0c12885fb81d6226e4626ef8ef9e71274..f27dae874c44e99509ee92af3d80e1f35db7dc74 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ license = "MPL-2.0" [dependencies] minidom = { version = "0.7.0" } jid = { version = "0.4.0", features = ["minidom"] } -base64 = "0.7.0" +base64 = "0.9.0" digest = "0.7.1" sha-1 = "0.7.0" sha2 = "0.7.0" From bf116941ece0b42bd1f439ff3c85736a6451d5bf Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 27 Dec 2017 17:35:36 +0100 Subject: [PATCH 398/698] Release version 0.9.0. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f27dae874c44e99509ee92af3d80e1f35db7dc74..1f1ab4bfa0e770ed2d4594d391fdd4f3373ca7ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.8.0" +version = "0.9.0" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", From 208e280067f075daf1d061b78992fa8611342386 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 20 Feb 2018 16:20:45 +0100 Subject: [PATCH 400/698] Add a WebSocket parser. --- src/lib.rs | 3 +++ src/ns.rs | 3 +++ src/websocket.rs | 70 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 src/websocket.rs diff --git a/src/lib.rs b/src/lib.rs index 415572c0f4092d811e57ae196363a9714a532da1..36b4b3e73fe1d91036a5a4926a9e77c7a8934ad8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,6 +59,9 @@ pub mod stanza_error; /// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence pub mod roster; +/// RFC 7395: An Extensible Messaging and Presence Protocol (XMPP) Subprotocol for WebSocket +pub mod websocket; + /// XEP-0004: Data Forms pub mod data_forms; diff --git a/src/ns.rs b/src/ns.rs index b8a10bca58e0eca69cc60c3445c392dace9b7662..dcc3946c95b276ca17d3aba8c55061a48e7ac144 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -13,6 +13,9 @@ pub const XMPP_STANZAS: &str = "urn:ietf:params:xml:ns:xmpp-stanzas"; /// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence pub const ROSTER: &str = "jabber:iq:roster"; +/// RFC 7395: An Extensible Messaging and Presence Protocol (XMPP) Subprotocol for WebSocket +pub const WEBSOCKET: &str = "urn:ietf:params:xml:ns:xmpp-framing"; + /// XEP-0004: Data Forms pub const DATA_FORMS: &str = "jabber:x:data"; diff --git a/src/websocket.rs b/src/websocket.rs new file mode 100644 index 0000000000000000000000000000000000000000..163ce3ef4ae91b2699a90b60c1d069f7df470584 --- /dev/null +++ b/src/websocket.rs @@ -0,0 +1,70 @@ +// Copyright (c) 2018 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use try_from::TryFrom; + +use minidom::Element; +use jid::Jid; +use error::Error; +use ns; + +generate_element_with_only_attributes!(Open, "open", ns::WEBSOCKET, [ + from: Option = "from" => optional, + to: Option = "to" => optional, + id: Option = "id" => optional, + version: Option = "version" => optional, + xml_lang: Option = "xml:lang" => optional, +]); + +impl Open { + pub fn new(to: Jid) -> Open { + Open { + from: None, + to: Some(to), + id: None, + version: Some(String::from("1.0")), + xml_lang: None, + } + } + + pub fn with_from(mut self, from: Jid) -> Open { + self.from = Some(from); + self + } + + pub fn with_id(mut self, id: String) -> Open { + self.id = Some(id); + self + } + + pub fn with_lang(mut self, xml_lang: String) -> Open { + self.xml_lang = Some(xml_lang); + self + } + + pub fn is_version(&self, version: &str) -> bool { + match self.version { + None => false, + Some(ref self_version) => self_version == &String::from(version), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let open = Open::try_from(elem).unwrap(); + assert_eq!(open.from, None); + assert_eq!(open.to, None); + assert_eq!(open.id, None); + assert_eq!(open.version, None); + assert_eq!(open.xml_lang, None); + } +} From fab99c90044dcbdc25e1c31b7597e1575554e0b5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 20 Feb 2018 16:53:42 +0100 Subject: [PATCH 401/698] error: Remove std::error::Error implementation as it conflicts with failure. --- Cargo.toml | 4 ++-- src/error.rs | 16 ---------------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1f1ab4bfa0e770ed2d4594d391fdd4f3373ca7ce..12239020669fdd3d71380f6231b0dae51a9fa3d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,8 +13,8 @@ categories = ["parsing", "network-programming"] license = "MPL-2.0" [dependencies] -minidom = { version = "0.7.0" } -jid = { version = "0.4.0", features = ["minidom"] } +minidom = { version = "0.8.0" } +jid = { version = "0.5.0", features = ["minidom"] } base64 = "0.9.0" digest = "0.7.1" sha-1 = "0.7.0" diff --git a/src/error.rs b/src/error.rs index f8d7372b8710384c93b9a56162f693e600f34b01..912ad02543557d337188b9e865003030216a6fc5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -9,7 +9,6 @@ use std::io; use std::num; use std::string; use std::fmt; -use std::error; use base64; use minidom; @@ -43,21 +42,6 @@ impl fmt::Display for Error { } } -impl error::Error for Error { - fn description(&self) -> &str { - match *self { - Error::ParseError(s) => s, - Error::IoError(ref e) => e.description(), - Error::XMLError(ref e) => e.description(), - Error::Base64Error(ref e) => e.description(), - Error::ParseIntError(ref e) => e.description(), - Error::ParseStringError(ref e) => e.description(), - Error::JidParseError(_) => "JID parse error", - Error::ChronoParseError(ref e) => e.description(), - } - } -} - impl From for Error { fn from(err: io::Error) -> Error { Error::IoError(err) From cfaebb4a91c4ac090e8f761a874eb81e8d09cf17 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 20 Feb 2018 17:01:12 +0100 Subject: [PATCH 402/698] Add a SASL parser. --- src/lib.rs | 2 ++ src/macros.rs | 5 ++++- src/ns.rs | 2 ++ src/sasl.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 src/sasl.rs diff --git a/src/lib.rs b/src/lib.rs index 36b4b3e73fe1d91036a5a4926a9e77c7a8934ad8..3f3df913b0aeb917e22944651ed9d0e841d99957 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,6 +55,8 @@ pub mod presence; pub mod iq; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod stanza_error; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core +pub mod sasl; /// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence pub mod roster; diff --git a/src/macros.rs b/src/macros.rs index 8fb573637cede8dd4df0e94d68a0967f41b4488e..24041cb05fdb1da057aa29fa37cce2d7fa6503c2 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -344,7 +344,10 @@ macro_rules! generate_elem_id { } macro_rules! generate_element_with_text { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+], $text_ident:ident: $codec:ident < $text_type:ty >) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, $text_ident:ident: $codec:ident < $text_type:ty >) => ( + generate_element_with_text!($(#[$meta])* $elem, $name, $ns, [], $text_ident: $codec<$text_type>); + ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], $text_ident:ident: $codec:ident < $text_type:ty >) => ( $(#[$meta])* #[derive(Debug, Clone)] pub struct $elem { diff --git a/src/ns.rs b/src/ns.rs index dcc3946c95b276ca17d3aba8c55061a48e7ac144..f17e602bcfb8e19d73ff0a48721ab8b198179c22 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -9,6 +9,8 @@ pub const JABBER_CLIENT: &str = "jabber:client"; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub const XMPP_STANZAS: &str = "urn:ietf:params:xml:ns:xmpp-stanzas"; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core +pub const SASL: &str = "urn:ietf:params:xml:ns:xmpp-sasl"; /// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence pub const ROSTER: &str = "jabber:iq:roster"; diff --git a/src/sasl.rs b/src/sasl.rs new file mode 100644 index 0000000000000000000000000000000000000000..a0ab15bb7d12c3c7edabaee59f9f289d4a8f6df5 --- /dev/null +++ b/src/sasl.rs @@ -0,0 +1,53 @@ +// Copyright (c) 2018 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use try_from::TryFrom; +use std::str::FromStr; + +use minidom::{Element, IntoAttributeValue}; + +use error::Error; + +use ns; +use helpers::Base64; + +generate_attribute!(Mechanism, "mechanism", { + Plain => "PLAIN", + ScramSha1 => "SCRAM-SHA-1", + Anonymous => "ANONYMOUS", +}); + +generate_element_with_text!(Auth, "auth", ns::SASL, + [ + mechanism: Mechanism = "mechanism" => required + ], + data: Base64> +); + +generate_element_with_text!(Challenge, "challenge", ns::SASL, + data: Base64> +); + +generate_element_with_text!(Response, "response", ns::SASL, + data: Base64> +); + +generate_element_with_text!(Success, "success", ns::SASL, + data: Base64> +); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let auth = Auth::try_from(elem).unwrap(); + assert_eq!(auth.mechanism, Mechanism::Plain); + assert!(auth.data.is_empty()); + } +} From 7c0975dd5dcac94deb269fb9bbbd00d7e8de16d7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 20 Feb 2018 17:43:19 +0100 Subject: [PATCH 403/698] Add a Bind parser. --- src/bind.rs | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 ++ src/ns.rs | 2 ++ 3 files changed, 97 insertions(+) create mode 100644 src/bind.rs diff --git a/src/bind.rs b/src/bind.rs new file mode 100644 index 0000000000000000000000000000000000000000..6974ce508d26011028821084be378dd92712776b --- /dev/null +++ b/src/bind.rs @@ -0,0 +1,93 @@ +// Copyright (c) 2018 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::str::FromStr; +use try_from::TryFrom; + +use minidom::Element; + +use error::Error; +use jid::Jid; +use ns; + +#[derive(Debug, Clone)] +pub struct Bind { + pub resource: Option, + pub jid: Option, +} + +impl Bind { + pub fn new(resource: Option) -> Bind { + Bind { + resource, + jid: None, + } + } +} + +impl TryFrom for Bind { + type Err = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "bind", ns::BIND); + check_no_attributes!(elem, "bind"); + + let mut bind = Bind { + resource: None, + jid: None, + }; + let mut already_set = false; + for child in elem.children() { + if already_set { + return Err(Error::ParseError("Bind can only have one child.")); + } + if child.is("resource", ns::BIND) { + check_no_children!(child, "resource"); + bind.resource = Some(child.text()); + already_set = true; + } else if child.is("jid", ns::BIND) { + check_no_children!(child, "jid"); + bind.jid = Some(Jid::from_str(&child.text())?); + already_set = true; + } else { + return Err(Error::ParseError("Unknown element in bind.")); + } + } + + Ok(bind) + } +} + +impl From for Element { + fn from(bind: Bind) -> Element { + Element::builder("bind") + .ns(ns::BIND) + .append(bind.resource.map(|resource| + Element::builder("resource") + .ns(ns::BIND) + .append(resource) + .build())) + .append(bind.jid.map(|jid| + Element::builder("jid") + .ns(ns::BIND) + .append(jid) + .build())) + .build() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let bind = Bind::try_from(elem).unwrap(); + assert_eq!(bind.resource, None); + assert_eq!(bind.jid, None); + } +} diff --git a/src/lib.rs b/src/lib.rs index 3f3df913b0aeb917e22944651ed9d0e841d99957..b7afeac60fd233240b306ed633cb8eec0089bd41 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,6 +57,8 @@ pub mod iq; pub mod stanza_error; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod sasl; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core +pub mod bind; /// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence pub mod roster; diff --git a/src/ns.rs b/src/ns.rs index f17e602bcfb8e19d73ff0a48721ab8b198179c22..b09c9d7e4e1f058896309a14f927f24ab293a135 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -11,6 +11,8 @@ pub const JABBER_CLIENT: &str = "jabber:client"; pub const XMPP_STANZAS: &str = "urn:ietf:params:xml:ns:xmpp-stanzas"; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub const SASL: &str = "urn:ietf:params:xml:ns:xmpp-sasl"; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core +pub const BIND: &str = "urn:ietf:params:xml:ns:xmpp-bind"; /// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence pub const ROSTER: &str = "jabber:iq:roster"; From a484150e4b4cce303d19fcbbc7c33d92e2e25c4b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 23 Feb 2018 12:38:40 +0100 Subject: [PATCH 404/698] bind: Switch to an enum, only three options are possible. --- src/bind.rs | 57 ++++++++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/src/bind.rs b/src/bind.rs index 6974ce508d26011028821084be378dd92712776b..3e76dba01dc39d25c4db8e2d2b910deb2ff0f58f 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -13,17 +13,18 @@ use error::Error; use jid::Jid; use ns; -#[derive(Debug, Clone)] -pub struct Bind { - pub resource: Option, - pub jid: Option, +#[derive(Debug, Clone, PartialEq)] +pub enum Bind { + None, + Resource(String), + Jid(Jid), } impl Bind { pub fn new(resource: Option) -> Bind { - Bind { - resource, - jid: None, + match resource { + None => Bind::None, + Some(resource) => Bind::Resource(resource), } } } @@ -35,23 +36,17 @@ impl TryFrom for Bind { check_self!(elem, "bind", ns::BIND); check_no_attributes!(elem, "bind"); - let mut bind = Bind { - resource: None, - jid: None, - }; - let mut already_set = false; + let mut bind = Bind::None; for child in elem.children() { - if already_set { + if bind != Bind::None { return Err(Error::ParseError("Bind can only have one child.")); } if child.is("resource", ns::BIND) { check_no_children!(child, "resource"); - bind.resource = Some(child.text()); - already_set = true; + bind = Bind::Resource(child.text()); } else if child.is("jid", ns::BIND) { check_no_children!(child, "jid"); - bind.jid = Some(Jid::from_str(&child.text())?); - already_set = true; + bind = Bind::Jid(Jid::from_str(&child.text())?); } else { return Err(Error::ParseError("Unknown element in bind.")); } @@ -65,16 +60,21 @@ impl From for Element { fn from(bind: Bind) -> Element { Element::builder("bind") .ns(ns::BIND) - .append(bind.resource.map(|resource| - Element::builder("resource") - .ns(ns::BIND) - .append(resource) - .build())) - .append(bind.jid.map(|jid| - Element::builder("jid") - .ns(ns::BIND) - .append(jid) - .build())) + .append(match bind { + Bind::None => vec!(), + Bind::Resource(resource) => vec!( + Element::builder("resource") + .ns(ns::BIND) + .append(resource) + .build() + ), + Bind::Jid(jid) => vec!( + Element::builder("jid") + .ns(ns::BIND) + .append(jid) + .build() + ), + }) .build() } } @@ -87,7 +87,6 @@ mod tests { fn test_simple() { let elem: Element = "".parse().unwrap(); let bind = Bind::try_from(elem).unwrap(); - assert_eq!(bind.resource, None); - assert_eq!(bind.jid, None); + assert_eq!(bind, Bind::None); } } From 3de29e1e81897ad0c76ece45e66afb11a6a2de27 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 1 Mar 2018 10:01:35 +0100 Subject: [PATCH 405/698] lib, ns, error, macros: Update the copyright year. --- src/error.rs | 2 +- src/lib.rs | 2 +- src/macros.rs | 2 +- src/ns.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/error.rs b/src/error.rs index 912ad02543557d337188b9e865003030216a6fc5..562e656a2990cdafdf6cf285dbf859431ad3c3b9 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Emmanuel Gil Peyrot +// Copyright (c) 2017-2018 Emmanuel Gil Peyrot // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/src/lib.rs b/src/lib.rs index b7afeac60fd233240b306ed633cb8eec0089bd41..5fde6358ef1b2297b15311d606a5cc0df165ff13 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,7 +15,7 @@ //! [`TryFrom`]: ../try_from/trait.TryFrom.html //! [`Element`]: ../minidom/element/struct.Element.html -// Copyright (c) 2017 Emmanuel Gil Peyrot +// Copyright (c) 2017-2018 Emmanuel Gil Peyrot // Copyright (c) 2017 Maxime “pep” Buquet // // This Source Code Form is subject to the terms of the Mozilla Public diff --git a/src/macros.rs b/src/macros.rs index 24041cb05fdb1da057aa29fa37cce2d7fa6503c2..3eded4587433fb4151c369aebd4f170f1322789c 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Emmanuel Gil Peyrot +// Copyright (c) 2017-2018 Emmanuel Gil Peyrot // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/src/ns.rs b/src/ns.rs index b09c9d7e4e1f058896309a14f927f24ab293a135..9f905e60805a49fecd5691f2b365fd5964200da8 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Emmanuel Gil Peyrot +// Copyright (c) 2017-2018 Emmanuel Gil Peyrot // Copyright (c) 2017 Maxime “pep” Buquet // // This Source Code Form is subject to the terms of the Mozilla Public From 395b64e64440d1fa908c972168f9803c2494d580 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 1 Mar 2018 10:08:30 +0100 Subject: [PATCH 406/698] Add a parser. --- src/lib.rs | 2 ++ src/ns.rs | 2 ++ src/stream.rs | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 src/stream.rs diff --git a/src/lib.rs b/src/lib.rs index 5fde6358ef1b2297b15311d606a5cc0df165ff13..7d9091b67a18ffe07c3082690591268329080f6d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,6 +56,8 @@ pub mod iq; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod stanza_error; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core +pub mod stream; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod sasl; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod bind; diff --git a/src/ns.rs b/src/ns.rs index 9f905e60805a49fecd5691f2b365fd5964200da8..af77fe32b67aa7252d885cf4747e387ff295d2cc 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -10,6 +10,8 @@ pub const JABBER_CLIENT: &str = "jabber:client"; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub const XMPP_STANZAS: &str = "urn:ietf:params:xml:ns:xmpp-stanzas"; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core +pub const STREAM: &str = "http://etherx.jabber.org/streams"; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub const SASL: &str = "urn:ietf:params:xml:ns:xmpp-sasl"; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub const BIND: &str = "urn:ietf:params:xml:ns:xmpp-bind"; diff --git a/src/stream.rs b/src/stream.rs new file mode 100644 index 0000000000000000000000000000000000000000..83335ae2377aa3fd51670595d56a274b4937ac46 --- /dev/null +++ b/src/stream.rs @@ -0,0 +1,70 @@ +// Copyright (c) 2018 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use try_from::TryFrom; + +use minidom::Element; +use jid::Jid; +use error::Error; +use ns; + +generate_element_with_only_attributes!(Stream, "stream", ns::STREAM, [ + from: Option = "from" => optional, + to: Option = "to" => optional, + id: Option = "id" => optional, + version: Option = "version" => optional, + xml_lang: Option = "xml:lang" => optional, +]); + +impl Stream { + pub fn new(to: Jid) -> Stream { + Stream { + from: None, + to: Some(to), + id: None, + version: Some(String::from("1.0")), + xml_lang: None, + } + } + + pub fn with_from(mut self, from: Jid) -> Stream { + self.from = Some(from); + self + } + + pub fn with_id(mut self, id: String) -> Stream { + self.id = Some(id); + self + } + + pub fn with_lang(mut self, xml_lang: String) -> Stream { + self.xml_lang = Some(xml_lang); + self + } + + pub fn is_version(&self, version: &str) -> bool { + match self.version { + None => false, + Some(ref self_version) => self_version == &String::from(version), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let stream = Stream::try_from(elem).unwrap(); + assert_eq!(stream.from, Some(Jid::domain("some-server.example"))); + assert_eq!(stream.to, None); + assert_eq!(stream.id, Some(String::from("abc"))); + assert_eq!(stream.version, Some(String::from("1.0"))); + assert_eq!(stream.xml_lang, Some(String::from("en"))); + } +} From 011308b4b4554dbd8d47e210e64735c4f3d260fa Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 1 Mar 2018 10:57:01 +0100 Subject: [PATCH 407/698] Add a component handshake parser. --- src/component.rs | 32 ++++++++++++++++++++++++++++++++ src/lib.rs | 3 +++ src/ns.rs | 3 +++ 3 files changed, 38 insertions(+) create mode 100644 src/component.rs diff --git a/src/component.rs b/src/component.rs new file mode 100644 index 0000000000000000000000000000000000000000..9c54118dd05d65eaf466d78be60123387ab9854b --- /dev/null +++ b/src/component.rs @@ -0,0 +1,32 @@ +// Copyright (c) 2018 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use try_from::TryFrom; + +use minidom::Element; +use error::Error; +use helpers::Base64; +use ns; + +generate_element_with_text!(Handshake, "handshake", ns::COMPONENT, + data: Base64> +); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let handshake = Handshake::try_from(elem).unwrap(); + assert!(handshake.data.is_empty()); + + let elem: Element = "AAAA".parse().unwrap(); + let handshake = Handshake::try_from(elem).unwrap(); + assert_eq!(handshake.data, b"\0\0\0"); + } +} diff --git a/src/lib.rs b/src/lib.rs index 7d9091b67a18ffe07c3082690591268329080f6d..e420e233e6389dcc7a06d6183c5f4703bfb924b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -101,6 +101,9 @@ pub mod version; /// XEP-0107: User Mood pub mod mood; +/// XEP-0114: Jabber Component Protocol +pub mod component; + /// XEP-0115: Entity Capabilities pub mod caps; diff --git a/src/ns.rs b/src/ns.rs index af77fe32b67aa7252d885cf4747e387ff295d2cc..2227886271a88847628fb82edda6b36faa428163 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -65,6 +65,9 @@ pub const MOOD: &str = "http://jabber.org/protocol/mood"; /// XEP-0114: Jabber Component Protocol pub const COMPONENT_ACCEPT: &str = "jabber:component:accept"; +/// XEP-0114: Jabber Component Protocol +pub const COMPONENT: &str = "jabber:component:accept"; + /// XEP-0115: Entity Capabilities pub const CAPS: &str = "http://jabber.org/protocol/caps"; From 0a057cdfef4549b986f1b531ecc6fa84ce91f025 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 1 Mar 2018 17:31:49 +0100 Subject: [PATCH 408/698] =?UTF-8?q?component:=20Fix=20handshake=20content,?= =?UTF-8?q?=20it=E2=80=99s=20hex,=20not=20base64!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/component.rs b/src/component.rs index 9c54118dd05d65eaf466d78be60123387ab9854b..f9ac62fda1f2f6a914ce9b856f6299021538ef49 100644 --- a/src/component.rs +++ b/src/component.rs @@ -8,11 +8,11 @@ use try_from::TryFrom; use minidom::Element; use error::Error; -use helpers::Base64; +use helpers::PlainText; use ns; generate_element_with_text!(Handshake, "handshake", ns::COMPONENT, - data: Base64> + data: PlainText> ); #[cfg(test)] @@ -23,10 +23,10 @@ mod tests { fn test_simple() { let elem: Element = "".parse().unwrap(); let handshake = Handshake::try_from(elem).unwrap(); - assert!(handshake.data.is_empty()); + assert_eq!(handshake.data, None); - let elem: Element = "AAAA".parse().unwrap(); + let elem: Element = "Coucou".parse().unwrap(); let handshake = Handshake::try_from(elem).unwrap(); - assert_eq!(handshake.data, b"\0\0\0"); + assert_eq!(handshake.data, Some(String::from("Coucou"))); } } From d83624c8a49918f72e9c5c527355961506859668 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 1 Mar 2018 17:32:50 +0100 Subject: [PATCH 409/698] component: Add constructors. --- src/component.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/component.rs b/src/component.rs index f9ac62fda1f2f6a914ce9b856f6299021538ef49..0f42e50ae67b08872579cb0554f7f0a1b66ec594 100644 --- a/src/component.rs +++ b/src/component.rs @@ -11,10 +11,30 @@ use error::Error; use helpers::PlainText; use ns; +use sha1::Sha1; +use digest::Digest; + generate_element_with_text!(Handshake, "handshake", ns::COMPONENT, data: PlainText> ); +impl Handshake { + pub fn new() -> Handshake { + Handshake { + data: None, + } + } + + pub fn from_password_and_stream_id(password: &str, stream_id: &str) -> Handshake { + let input = String::from(stream_id) + password; + let hash = Sha1::digest(input.as_bytes()); + let content = format!("{:x}", hash); + Handshake { + data: Some(content), + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -29,4 +49,13 @@ mod tests { let handshake = Handshake::try_from(elem).unwrap(); assert_eq!(handshake.data, Some(String::from("Coucou"))); } + + #[test] + fn test_constructors() { + let handshake = Handshake::new(); + assert_eq!(handshake.data, None); + + let handshake = Handshake::from_password_and_stream_id("123456", "sid"); + assert_eq!(handshake.data, Some(String::from("9accec263ab84a43c6037ccf7cd48cb1d3f6df8e"))); + } } From c762a03c39875f481db04e2bacbba6f3a398f793 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 4 May 2018 19:10:04 +0200 Subject: [PATCH 410/698] hashes: Add two constructors. --- src/hashes.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/hashes.rs b/src/hashes.rs index 535f99c536b5ebb4d4a0232cc0eb4af2a0350a88..e414b91a4fb100ac86a172553810af0fbe27865f 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -13,6 +13,7 @@ use error::Error; use ns; use helpers::Base64; +use base64; #[allow(non_camel_case_types)] #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -76,10 +77,22 @@ generate_element_with_text!( hash: Base64> ); +impl Hash { + pub fn new(algo: Algo, hash: Vec) -> Hash { + Hash { + algo, + hash, + } + } + + pub fn from_base64(algo: Algo, hash: &str) -> Result { + Ok(Hash::new(algo, base64::decode(hash)?)) + } +} + #[cfg(test)] mod tests { use super::*; - use base64; #[test] fn test_simple() { From a1e95ffb2e4b2cbef73f35e4299f9f04a87be152 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 4 May 2018 19:10:29 +0200 Subject: [PATCH 411/698] jingle: Add constructors and setters. --- src/jingle.rs | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/src/jingle.rs b/src/jingle.rs index c9737b03fd3fd58349b8f5bca5ce901e7b0f738c..ae313766a2b0ec59a61c94fb8e0f44dbaabbcc91 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -76,6 +76,45 @@ pub struct Content { pub security: Option, } +impl Content { + pub fn new(creator: Creator, name: ContentId) -> Content { + Content { + creator, + name, + disposition: Disposition::Session, + senders: Senders::Both, + description: None, + transport: None, + security: None, + } + } + + pub fn with_disposition(mut self, disposition: Disposition) -> Content { + self.disposition = disposition; + self + } + + pub fn with_senders(mut self, senders: Senders) -> Content { + self.senders = senders; + self + } + + pub fn with_description(mut self, description: Element) -> Content { + self.description = Some(description); + self + } + + pub fn with_transport(mut self, transport: Element) -> Content { + self.transport = Some(transport); + self + } + + pub fn with_security(mut self, security: Element) -> Content { + self.security = Some(security); + self + } +} + impl TryFrom for Content { type Err = Error; @@ -267,6 +306,40 @@ pub struct Jingle { pub other: Vec, } +impl Jingle { + pub fn new(action: Action, sid: SessionId) -> Jingle { + Jingle { + action: action, + sid: sid, + initiator: None, + responder: None, + contents: Vec::new(), + reason: None, + other: Vec::new(), + } + } + + pub fn with_initiator(mut self, initiator: Jid) -> Jingle { + self.initiator = Some(initiator); + self + } + + pub fn with_responder(mut self, responder: Jid) -> Jingle { + self.responder = Some(responder); + self + } + + pub fn add_content(mut self, content: Content) -> Jingle { + self.contents.push(content); + self + } + + pub fn set_reason(mut self, content: Content) -> Jingle { + self.contents.push(content); + self + } +} + impl TryFrom for Jingle { type Err = Error; From 927df2fdb12fbe1e711f73f8a9a154f8717a84e5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 4 May 2018 19:10:45 +0200 Subject: [PATCH 412/698] jingle_ft: Add constructors and setters. --- src/jingle_ft.rs | 62 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index bf584380d40b5c30510bafd78542c62c44a47df1..c78e1d45bf4776c1af567e61256849dfeab92732 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -19,7 +19,7 @@ use error::Error; use ns; generate_element_with_children!( - #[derive(PartialEq)] + #[derive(PartialEq, Default)] Range, "range", ns::JINGLE_FT, attributes: [ offset: u64 = "offset" => default, @@ -30,6 +30,12 @@ generate_element_with_children!( ] ); +impl Range { + pub fn new() -> Range { + Default::default() + } +} + type Lang = String; generate_id!(Desc); @@ -45,6 +51,60 @@ pub struct File { pub hashes: Vec, } +impl File { + pub fn new() -> File { + File { + date: None, + media_type: None, + name: None, + descs: BTreeMap::new(), + size: None, + range: None, + hashes: Vec::new(), + } + } + + pub fn with_date(mut self, date: DateTime) -> File { + self.date = Some(date); + self + } + + pub fn with_date_str(mut self, date: &str) -> Result { + self.date = Some(DateTime::from_str(date)?); + Ok(self) + } + + pub fn with_media_type(mut self, media_type: String) -> File { + self.media_type = Some(media_type); + self + } + + pub fn with_name(mut self, name: String) -> File { + self.name = Some(name); + self + } + + pub fn add_desc(mut self, lang: &str, desc: Desc) -> File { + self.descs.insert(Lang::from(lang), desc); + self + } + + pub fn with_size(mut self, size: u64) -> File { + self.size = Some(size); + self + } + + pub fn with_range(mut self, range: Range) -> File { + self.range = Some(range); + self + } + + pub fn add_hash(mut self, hash: Hash) -> File { + self.hashes.push(hash); + self + } +} + impl TryFrom for File { type Err = Error; From 3310f297047a7a5d244d21c090167e1a5bb37314 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 4 May 2018 19:11:03 +0200 Subject: [PATCH 413/698] jingle_s5b: Add constructors and setters. --- src/jingle_s5b.rs | 49 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 09b3dc56ea7bd6b6c3db874929b8740024497bcc..4233d2d9dccd14bb739ca4b8e1468f98632fbd14 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -39,6 +39,29 @@ generate_element_with_only_attributes!(Candidate, "candidate", ns::JINGLE_S5B, [ type_: Type = "type" => default, ]); +impl Candidate { + pub fn new(cid: CandidateId, host: String, jid: Jid, priority: u32) -> Candidate { + Candidate { + cid, + host, + jid, + priority, + port: Default::default(), + type_: Default::default(), + } + } + + pub fn with_port(mut self, port: u16) -> Candidate { + self.port = Some(port); + self + } + + pub fn with_type(mut self, type_: Type) -> Candidate { + self.type_ = type_; + self + } +} + #[derive(Debug, Clone)] pub enum TransportPayload { Activated(String), @@ -57,6 +80,32 @@ pub struct Transport { pub payload: TransportPayload, } +impl Transport { + pub fn new(sid: StreamId) -> Transport { + Transport { + sid, + dstaddr: None, + mode: Default::default(), + payload: TransportPayload::None, + } + } + + pub fn with_dstaddr(mut self, dstaddr: String) -> Transport { + self.dstaddr = Some(dstaddr); + self + } + + pub fn with_mode(mut self, mode: Mode) -> Transport { + self.mode = mode; + self + } + + pub fn with_payload(mut self, payload: TransportPayload) -> Transport { + self.payload = payload; + self + } +} + impl TryFrom for Transport { type Err = Error; From 53e23cbf654f6d9d1bc51f69d2168e513c807944 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 4 May 2018 21:19:40 +0200 Subject: [PATCH 414/698] jingle_s5b: Make Candidate.host an IpAddr instead of a String. --- src/error.rs | 9 +++++++++ src/jingle_s5b.rs | 9 +++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/error.rs b/src/error.rs index 562e656a2990cdafdf6cf285dbf859431ad3c3b9..dbfbd8af082de97e44cae6f93570b97ad3cbe662 100644 --- a/src/error.rs +++ b/src/error.rs @@ -9,6 +9,7 @@ use std::io; use std::num; use std::string; use std::fmt; +use std::net; use base64; use minidom; @@ -23,6 +24,7 @@ pub enum Error { Base64Error(base64::DecodeError), ParseIntError(num::ParseIntError), ParseStringError(string::ParseError), + ParseAddrError(net::AddrParseError), JidParseError(jid::JidParseError), ChronoParseError(chrono::ParseError), } @@ -36,6 +38,7 @@ impl fmt::Display for Error { Error::Base64Error(ref e) => write!(fmt, "{}", e), Error::ParseIntError(ref e) => write!(fmt, "{}", e), Error::ParseStringError(ref e) => write!(fmt, "{}", e), + Error::ParseAddrError(ref e) => write!(fmt, "{}", e), Error::JidParseError(_) => write!(fmt, "JID parse error"), Error::ChronoParseError(ref e) => write!(fmt, "{}", e), } @@ -72,6 +75,12 @@ impl From for Error { } } +impl From for Error { + fn from(err: net::AddrParseError) -> Error { + Error::ParseAddrError(err) + } +} + impl From for Error { fn from(err: jid::JidParseError) -> Error { Error::JidParseError(err) diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 4233d2d9dccd14bb739ca4b8e1468f98632fbd14..c670124d24835b2ff59147f5ebf4919c96311101 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -6,6 +6,7 @@ use try_from::TryFrom; use std::str::FromStr; +use std::net::IpAddr; use minidom::{Element, IntoAttributeValue}; use jid::Jid; @@ -32,7 +33,7 @@ generate_id!(StreamId); generate_element_with_only_attributes!(Candidate, "candidate", ns::JINGLE_S5B, [ cid: CandidateId = "cid" => required, - host: String = "host" => required, + host: IpAddr = "host" => required, jid: Jid = "jid" => required, port: Option = "port" => optional, priority: u32 = "priority" => required, @@ -40,7 +41,7 @@ generate_element_with_only_attributes!(Candidate, "candidate", ns::JINGLE_S5B, [ ]); impl Candidate { - pub fn new(cid: CandidateId, host: String, jid: Jid, priority: u32) -> Candidate { + pub fn new(cid: CandidateId, host: IpAddr, jid: Jid, priority: u32) -> Candidate { Candidate { cid, host, @@ -238,14 +239,14 @@ mod tests { #[test] fn test_serialise_candidate() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); let transport = Transport { sid: StreamId(String::from("coucou")), dstaddr: None, mode: Mode::Tcp, payload: TransportPayload::Candidates(vec!(Candidate { cid: CandidateId(String::from("coucou")), - host: String::from("coucou"), + host: IpAddr::from_str("127.0.0.1").unwrap(), jid: Jid::from_str("coucou@coucou").unwrap(), port: None, priority: 0u32, From 6bafe35d2a3e14b04ba4df0f4e95e16ee10bd083 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 May 2018 17:22:05 +0200 Subject: [PATCH 415/698] message: Add getters for the best body per language list. --- src/message.rs | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/src/message.rs b/src/message.rs index add6d1733adbe62b43e116851867364599c13744..6bbab781931470792cee77a631e76ebf201ec7d1 100644 --- a/src/message.rs +++ b/src/message.rs @@ -146,6 +146,43 @@ impl Message { payloads: vec!(), } } + + fn get_best<'a, T>(map: &'a BTreeMap, preferred_langs: Vec<&str>) -> Option<(Lang, &'a T)> { + if map.is_empty() { + return None; + } + for lang in preferred_langs { + if map.contains_key(lang) { + return Some((Lang::from(lang), &map[lang])); + } + } + if map.contains_key("") { + return Some((Lang::new(), &map[""])); + } + map.iter().map(|(lang, body)| (lang.clone(), body)).next() + } + + /// Returns the best matching body from a list of languages. + /// + /// For instance, if a message contains both an xml:lang='de', an xml:lang='fr' and an English + /// body without an xml:lang attribute, and you pass ["fr", "en"] as your preferred languages, + /// `Some(("fr", the_second_body))` will be returned. + /// + /// If no body matches, an undefined body will be returned. + pub fn get_best_body(&self, preferred_langs: Vec<&str>) -> Option<(Lang, &Body)> { + Message::get_best::(&self.bodies, preferred_langs) + } + + /// Returns the best matching subject from a list of languages. + /// + /// For instance, if a message contains both an xml:lang='de', an xml:lang='fr' and an English + /// subject without an xml:lang attribute, and you pass ["fr", "en"] as your preferred + /// languages, `Some(("fr", the_second_subject))` will be returned. + /// + /// If no subject matches, an undefined subject will be returned. + pub fn get_best_subject(&self, preferred_langs: Vec<&str>) -> Option<(Lang, &Subject)> { + Message::get_best::(&self.subjects, preferred_langs) + } } impl TryFrom for Message { @@ -281,6 +318,12 @@ mod tests { let message = Message::try_from(elem).unwrap(); assert_eq!(message.bodies[""], Body::from_str("Hello world!").unwrap()); + { + let (lang, body) = message.get_best_body(vec!("en")).unwrap(); + assert_eq!(lang, ""); + assert_eq!(body, &Body::from_str("Hello world!").unwrap()); + } + let elem2 = message.into(); assert!(elem1.compare_to(&elem2)); } @@ -307,10 +350,58 @@ mod tests { let message = Message::try_from(elem).unwrap(); assert_eq!(message.subjects[""], Subject::from_str("Hello world!").unwrap()); + { + let (lang, subject) = message.get_best_subject(vec!("en")).unwrap(); + assert_eq!(lang, ""); + assert_eq!(subject, &Subject::from_str("Hello world!").unwrap()); + } + let elem2 = message.into(); assert!(elem1.compare_to(&elem2)); } + #[test] + fn get_best_body() { + #[cfg(not(feature = "component"))] + let elem: Element = "Hallo Welt!Salut le monde !Hello world!".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "Hello world!".parse().unwrap(); + let message = Message::try_from(elem).unwrap(); + + // Tests basic feature. + { + let (lang, body) = message.get_best_body(vec!("fr")).unwrap(); + assert_eq!(lang, "fr"); + assert_eq!(body, &Body::from_str("Salut le monde !").unwrap()); + } + + // Tests order. + { + let (lang, body) = message.get_best_body(vec!("en", "de")).unwrap(); + assert_eq!(lang, "de"); + assert_eq!(body, &Body::from_str("Hallo Welt!").unwrap()); + } + + // Tests fallback. + { + let (lang, body) = message.get_best_body(vec!()).unwrap(); + assert_eq!(lang, ""); + assert_eq!(body, &Body::from_str("Hello world!").unwrap()); + } + + // Tests fallback. + { + let (lang, body) = message.get_best_body(vec!("ja")).unwrap(); + assert_eq!(lang, ""); + assert_eq!(body, &Body::from_str("Hello world!").unwrap()); + } + + let message = Message::new(None); + + // Tests without a body. + assert_eq!(message.get_best_body(vec!("ja")), None); + } + #[test] fn test_attention() { #[cfg(not(feature = "component"))] From c72f5819c7f468b026a73b463d198af4051dac3e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 May 2018 17:42:07 +0200 Subject: [PATCH 416/698] message: Optimise slightly the get_best method. --- src/message.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/message.rs b/src/message.rs index 6bbab781931470792cee77a631e76ebf201ec7d1..ffd8f21915d76d4405b070f7591691709c03ca72 100644 --- a/src/message.rs +++ b/src/message.rs @@ -152,12 +152,12 @@ impl Message { return None; } for lang in preferred_langs { - if map.contains_key(lang) { - return Some((Lang::from(lang), &map[lang])); + if let Some(body) = map.get(lang) { + return Some((Lang::from(lang), body)); } } - if map.contains_key("") { - return Some((Lang::new(), &map[""])); + if let Some(body) = map.get("") { + return Some((Lang::new(), body)); } map.iter().map(|(lang, body)| (lang.clone(), body)).next() } From f2f8de773be619c054e5d8a7828cb0d923c2c962 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 May 2018 17:42:49 +0200 Subject: [PATCH 417/698] message: Rename body into value, since this applies to subject too. --- src/message.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/message.rs b/src/message.rs index ffd8f21915d76d4405b070f7591691709c03ca72..dad3c486582c03f80c20d73086f82dc0a7ec782f 100644 --- a/src/message.rs +++ b/src/message.rs @@ -152,14 +152,14 @@ impl Message { return None; } for lang in preferred_langs { - if let Some(body) = map.get(lang) { - return Some((Lang::from(lang), body)); + if let Some(value) = map.get(lang) { + return Some((Lang::from(lang), value)); } } - if let Some(body) = map.get("") { - return Some((Lang::new(), body)); + if let Some(value) = map.get("") { + return Some((Lang::new(), value)); } - map.iter().map(|(lang, body)| (lang.clone(), body)).next() + map.iter().map(|(lang, value)| (lang.clone(), value)).next() } /// Returns the best matching body from a list of languages. From e0438f9b887108e4dc437b83f9c81904f7a2b32a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 May 2018 17:59:04 +0200 Subject: [PATCH 418/698] jingle_message, muc, presence, version: Always use the check_no_attributes macro. --- src/jingle_message.rs | 6 +----- src/muc/muc.rs | 5 +---- src/muc/user.rs | 17 +++-------------- src/presence.rs | 14 +++----------- src/version.rs | 4 +--- 5 files changed, 9 insertions(+), 37 deletions(-) diff --git a/src/jingle_message.rs b/src/jingle_message.rs index 779af992d0b43f06b7f7b5104301a95966421cdd..159ce597adcbaeb3100d1febc17119c6c454a50a 100644 --- a/src/jingle_message.rs +++ b/src/jingle_message.rs @@ -28,11 +28,7 @@ pub enum JingleMI { } fn get_sid(elem: Element) -> Result { - for (attr, _) in elem.attrs() { - if attr != "id" { - return Err(Error::ParseError("Unknown attribute in Jingle message element.")); - } - } + check_no_unknown_attributes!(elem, "Jingle message", ["id"]); Ok(SessionId(get_attr!(elem, "id", required))) } diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 550cc61e6c603215cbfb94994570ce9eebf7fee7..9758b80071279443f6eec10c8d3e0c69c7cb51d9 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -25,6 +25,7 @@ impl TryFrom for Muc { if !elem.is("x", ns::MUC) { return Err(Error::ParseError("This is not an x element.")); } + check_no_attributes!(elem, "x"); let mut password = None; for child in elem.children() { @@ -35,10 +36,6 @@ impl TryFrom for Muc { } } - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown attribute in x element.")); - } - Ok(Muc { password: password, }) diff --git a/src/muc/user.rs b/src/muc/user.rs index d09c73c4d6b6d01be32301383003f6cdfa454997..236ca8c87934e7bc352db230be12c5a88ec7dfe5 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -96,14 +96,10 @@ impl TryFrom for Actor { if !elem.is("actor", ns::MUC_USER) { return Err(Error::ParseError("This is not a actor element.")); } + check_no_unknown_attributes!(elem, "actor", ["jid", "nick"]); for _ in elem.children() { return Err(Error::ParseError("Unknown child in actor element.")); } - for (attr, _) in elem.attrs() { - if attr != "jid" && attr != "nick" { - return Err(Error::ParseError("Unknown attribute in actor element.")); - } - } let jid: Option = get_attr!(elem, "jid", optional); let nick = get_attr!(elem, "nick", optional); @@ -167,6 +163,7 @@ impl TryFrom for Item { if !elem.is("item", ns::MUC_USER) { return Err(Error::ParseError("This is not a item element.")); } + check_no_unknown_attributes!(elem, "item", ["affiliation", "jid", "nick", "role"]); let mut actor: Option = None; let mut continue_: Option = None; let mut reason: Option = None; @@ -181,12 +178,6 @@ impl TryFrom for Item { return Err(Error::ParseError("Unknown child in item element.")); } } - for (attr, _) in elem.attrs() { - if attr != "affiliation" && attr != "jid" && - attr != "nick" && attr != "role" { - return Err(Error::ParseError("Unknown attribute in item element.")); - } - } let affiliation: Affiliation = get_attr!(elem, "affiliation", required); let jid: Option = get_attr!(elem, "jid", optional); @@ -233,6 +224,7 @@ impl TryFrom for MucUser { if !elem.is("x", ns::MUC_USER) { return Err(Error::ParseError("This is not an x element.")); } + check_no_attributes!(elem, "x"); let mut status = vec!(); let mut items = vec!(); for child in elem.children() { @@ -244,9 +236,6 @@ impl TryFrom for MucUser { return Err(Error::ParseError("Unknown child in x element.")); } } - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown attribute in x element.")); - } Ok(MucUser { status, items, diff --git a/src/presence.rs b/src/presence.rs index e48e68509dbc8c6f79673527b3fee31d9ecc01d1..d25a7b092a5e661c891176818b7022b10daed95d 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -268,22 +268,16 @@ impl TryFrom for Presence { if show.is_some() { return Err(Error::ParseError("More than one show element in a presence.")); } + check_no_attributes!(elem, "show"); for _ in elem.children() { return Err(Error::ParseError("Unknown child in show element.")); } - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown attribute in show element.")); - } show = Some(Show::from_str(elem.text().as_ref())?); } else if elem.is("status", ns::DEFAULT_NS) { + check_no_unknown_attributes!(elem, "status", ["xml:lang"]); for _ in elem.children() { return Err(Error::ParseError("Unknown child in status element.")); } - for (attr, _) in elem.attrs() { - if attr != "xml:lang" { - return Err(Error::ParseError("Unknown attribute in status element.")); - } - } let lang = get_attr!(elem, "xml:lang", default); if presence.statuses.insert(lang, elem.text()).is_some() { return Err(Error::ParseError("Status element present twice for the same xml:lang.")); @@ -292,12 +286,10 @@ impl TryFrom for Presence { if priority.is_some() { return Err(Error::ParseError("More than one priority element in a presence.")); } + check_no_attributes!(elem, "status"); for _ in elem.children() { return Err(Error::ParseError("Unknown child in priority element.")); } - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown attribute in priority element.")); - } priority = Some(Priority::from_str(elem.text().as_ref())?); } else { presence.payloads.push(elem.clone()); diff --git a/src/version.rs b/src/version.rs index 5e7f0abf829d89e4a5b7ed0351657950a0a5aea8..e14bb422ed5a26c733d77d5c5b4fad5a917825ce 100644 --- a/src/version.rs +++ b/src/version.rs @@ -23,9 +23,7 @@ impl TryFrom for Version { if !elem.is("query", ns::VERSION) { return Err(Error::ParseError("This is not a version element.")); } - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown child in version element.")); - } + check_no_attributes!(elem, "version"); let mut name = None; let mut version = None; let mut os = None; From 6bb466eea29cbab051bfd81456db5506cd818f2d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 May 2018 18:31:11 +0200 Subject: [PATCH 419/698] Use check_self!() where it makes sense. --- src/forwarding.rs | 4 +- src/ibr.rs | 4 +- src/iq.rs | 4 +- src/jingle.rs | 12 ++---- src/jingle_s5b.rs | 95 ++++++++++++++++++++++----------------------- src/message.rs | 4 +- src/muc/muc.rs | 4 +- src/muc/user.rs | 12 ++---- src/presence.rs | 4 +- src/rsm.rs | 6 +-- src/stanza_error.rs | 4 +- src/version.rs | 4 +- 12 files changed, 62 insertions(+), 95 deletions(-) diff --git a/src/forwarding.rs b/src/forwarding.rs index 2348068b996e90ecc0f8fbc2902a6d508452ae19..8ab4ead2b534699cd1023f96bf4e3d3aa6453cfa 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -26,9 +26,7 @@ impl TryFrom for Forwarded { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("forwarded", ns::FORWARD) { - return Err(Error::ParseError("This is not a forwarded element.")); - } + check_self!(elem, "forwarded", ns::FORWARD); let mut delay = None; let mut stanza = None; for child in elem.children() { diff --git a/src/ibr.rs b/src/ibr.rs index 05281f333255b2d52b6ecdaf63c5284d116bb238..892c67d9d3fad57c659d4ca577d7b01f1a4cceff 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -29,9 +29,7 @@ impl TryFrom for Query { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("query", ns::REGISTER) { - return Err(Error::ParseError("This is not an ibr element.")); - } + check_self!(elem, "query", ns::REGISTER, "IBR query"); let mut query = Query { registered: false, fields: HashMap::new(), diff --git a/src/iq.rs b/src/iq.rs index 65c13a2411a04894835b873d5199a1be5192e8f1..9a7f3b5933f3ecad76e2e0a97f6b2ea04bc432fa 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -207,9 +207,7 @@ impl TryFrom for Iq { type Err = Error; fn try_from(root: Element) -> Result { - if !root.is("iq", ns::DEFAULT_NS) { - return Err(Error::ParseError("This is not an iq element.")); - } + check_self!(root, "iq", ns::DEFAULT_NS); let from = get_attr!(root, "from", optional); let to = get_attr!(root, "to", optional); let id = get_attr!(root, "id", optional); diff --git a/src/jingle.rs b/src/jingle.rs index ae313766a2b0ec59a61c94fb8e0f44dbaabbcc91..08002bcf61ed5e4830a18f55f810126af2386818 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -119,9 +119,7 @@ impl TryFrom for Content { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("content", ns::JINGLE) { - return Err(Error::ParseError("This is not a content element.")); - } + check_self!(elem, "content", ns::JINGLE); let mut content = Content { creator: get_attr!(elem, "creator", required), @@ -252,9 +250,7 @@ impl TryFrom for ReasonElement { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("reason", ns::JINGLE) { - return Err(Error::ParseError("This is not a reason element.")); - } + check_self!(elem, "reason", ns::JINGLE); let mut reason = None; let mut text = None; for child in elem.children() { @@ -344,9 +340,7 @@ impl TryFrom for Jingle { type Err = Error; fn try_from(root: Element) -> Result { - if !root.is("jingle", ns::JINGLE) { - return Err(Error::ParseError("This is not a Jingle element.")); - } + check_self!(root, "jingle", ns::JINGLE, "Jingle"); let mut jingle = Jingle { action: get_attr!(root, "action", required), diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index c670124d24835b2ff59147f5ebf4919c96311101..558eb560e92e22cc0f9ab5ca17c25912faf42219 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -111,57 +111,54 @@ impl TryFrom for Transport { type Err = Error; fn try_from(elem: Element) -> Result { - if elem.is("transport", ns::JINGLE_S5B) { - let sid = get_attr!(elem, "sid", required); - let dstaddr = get_attr!(elem, "dstaddr", optional); - let mode = get_attr!(elem, "mode", default); + check_self!(elem, "transport", ns::JINGLE_S5B); + let sid = get_attr!(elem, "sid", required); + let dstaddr = get_attr!(elem, "dstaddr", optional); + let mode = get_attr!(elem, "mode", default); - let mut payload = None; - for child in elem.children() { - payload = Some(if child.is("candidate", ns::JINGLE_S5B) { - let mut candidates = match payload { - Some(TransportPayload::Candidates(candidates)) => candidates, - Some(_) => return Err(Error::ParseError("Non-candidate child already present in JingleS5B transport element.")), - None => vec!(), - }; - candidates.push(Candidate::try_from(child.clone())?); - TransportPayload::Candidates(candidates) - } else if child.is("activated", ns::JINGLE_S5B) { - if payload.is_some() { - return Err(Error::ParseError("Non-activated child already present in JingleS5B transport element.")); - } - let cid = get_attr!(child, "cid", required); - TransportPayload::Activated(cid) - } else if child.is("candidate-error", ns::JINGLE_S5B) { - if payload.is_some() { - return Err(Error::ParseError("Non-candidate-error child already present in JingleS5B transport element.")); - } - TransportPayload::CandidateError - } else if child.is("candidate-used", ns::JINGLE_S5B) { - if payload.is_some() { - return Err(Error::ParseError("Non-candidate-used child already present in JingleS5B transport element.")); - } - let cid = get_attr!(child, "cid", required); - TransportPayload::CandidateUsed(cid) - } else if child.is("proxy-error", ns::JINGLE_S5B) { - if payload.is_some() { - return Err(Error::ParseError("Non-proxy-error child already present in JingleS5B transport element.")); - } - TransportPayload::ProxyError - } else { - return Err(Error::ParseError("Unknown child in JingleS5B transport element.")); - }); - } - let payload = payload.unwrap_or(TransportPayload::None); - Ok(Transport { - sid: sid, - dstaddr: dstaddr, - mode: mode, - payload: payload, - }) - } else { - Err(Error::ParseError("This is not an JingleS5B transport element.")) + let mut payload = None; + for child in elem.children() { + payload = Some(if child.is("candidate", ns::JINGLE_S5B) { + let mut candidates = match payload { + Some(TransportPayload::Candidates(candidates)) => candidates, + Some(_) => return Err(Error::ParseError("Non-candidate child already present in JingleS5B transport element.")), + None => vec!(), + }; + candidates.push(Candidate::try_from(child.clone())?); + TransportPayload::Candidates(candidates) + } else if child.is("activated", ns::JINGLE_S5B) { + if payload.is_some() { + return Err(Error::ParseError("Non-activated child already present in JingleS5B transport element.")); + } + let cid = get_attr!(child, "cid", required); + TransportPayload::Activated(cid) + } else if child.is("candidate-error", ns::JINGLE_S5B) { + if payload.is_some() { + return Err(Error::ParseError("Non-candidate-error child already present in JingleS5B transport element.")); + } + TransportPayload::CandidateError + } else if child.is("candidate-used", ns::JINGLE_S5B) { + if payload.is_some() { + return Err(Error::ParseError("Non-candidate-used child already present in JingleS5B transport element.")); + } + let cid = get_attr!(child, "cid", required); + TransportPayload::CandidateUsed(cid) + } else if child.is("proxy-error", ns::JINGLE_S5B) { + if payload.is_some() { + return Err(Error::ParseError("Non-proxy-error child already present in JingleS5B transport element.")); + } + TransportPayload::ProxyError + } else { + return Err(Error::ParseError("Unknown child in JingleS5B transport element.")); + }); } + let payload = payload.unwrap_or(TransportPayload::None); + Ok(Transport { + sid: sid, + dstaddr: dstaddr, + mode: mode, + payload: payload, + }) } } diff --git a/src/message.rs b/src/message.rs index dad3c486582c03f80c20d73086f82dc0a7ec782f..c4c4a17fb23d5a82a5cb718e11db8341dfcc6bf6 100644 --- a/src/message.rs +++ b/src/message.rs @@ -189,9 +189,7 @@ impl TryFrom for Message { type Err = Error; fn try_from(root: Element) -> Result { - if !root.is("message", ns::DEFAULT_NS) { - return Err(Error::ParseError("This is not a message element.")); - } + check_self!(root, "message", ns::DEFAULT_NS); let from = get_attr!(root, "from", optional); let to = get_attr!(root, "to", optional); let id = get_attr!(root, "id", optional); diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 9758b80071279443f6eec10c8d3e0c69c7cb51d9..14a65302322f41ad0a10fce208893ccc63c269b0 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -22,9 +22,7 @@ impl TryFrom for Muc { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("x", ns::MUC) { - return Err(Error::ParseError("This is not an x element.")); - } + check_self!(elem, "x", ns::MUC); check_no_attributes!(elem, "x"); let mut password = None; diff --git a/src/muc/user.rs b/src/muc/user.rs index 236ca8c87934e7bc352db230be12c5a88ec7dfe5..a6614a63af1c4bd6ada54c41583394bc5102b109 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -93,9 +93,7 @@ impl TryFrom for Actor { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("actor", ns::MUC_USER) { - return Err(Error::ParseError("This is not a actor element.")); - } + check_self!(elem, "actor", ns::MUC_USER); check_no_unknown_attributes!(elem, "actor", ["jid", "nick"]); for _ in elem.children() { return Err(Error::ParseError("Unknown child in actor element.")); @@ -160,9 +158,7 @@ impl TryFrom for Item { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("item", ns::MUC_USER) { - return Err(Error::ParseError("This is not a item element.")); - } + check_self!(elem, "item", ns::MUC_USER); check_no_unknown_attributes!(elem, "item", ["affiliation", "jid", "nick", "role"]); let mut actor: Option = None; let mut continue_: Option = None; @@ -221,9 +217,7 @@ impl TryFrom for MucUser { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("x", ns::MUC_USER) { - return Err(Error::ParseError("This is not an x element.")); - } + check_self!(elem, "x", ns::MUC_USER); check_no_attributes!(elem, "x"); let mut status = vec!(); let mut items = vec!(); diff --git a/src/presence.rs b/src/presence.rs index d25a7b092a5e661c891176818b7022b10daed95d..f4a4f11fb810a53147ee262031e156b6b5243c52 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -248,9 +248,7 @@ impl TryFrom for Presence { type Err = Error; fn try_from(root: Element) -> Result { - if !root.is("presence", ns::DEFAULT_NS) { - return Err(Error::ParseError("This is not a presence element.")); - } + check_self!(root, "presence", ns::DEFAULT_NS); let mut show = None; let mut priority = None; let mut presence = Presence { diff --git a/src/rsm.rs b/src/rsm.rs index 17642986488e1688e81b956688c8d0f5cb453b6e..6b9f54390f4f83fdbb368676b4dcd3534e8124c8 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -28,9 +28,7 @@ impl TryFrom for Set { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("set", ns::RSM) { - return Err(Error::ParseError("This is not a RSM element.")); - } + check_self!(elem, "set", ns::RSM, "RSM set"); let mut set = Set { after: None, before: None, @@ -136,7 +134,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "This is not a RSM element."); + assert_eq!(message, "This is not a RSM set element."); } #[test] diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 4548864cf4e59116ee4c6c63b0e17f86ef78e8a0..4977739d3238592180914f85939f616002844893 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -62,9 +62,7 @@ impl TryFrom for StanzaError { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("error", ns::DEFAULT_NS) { - return Err(Error::ParseError("This is not an error element.")); - } + check_self!(elem, "error", ns::DEFAULT_NS); let type_ = get_attr!(elem, "type", required); let by = get_attr!(elem, "by", optional); diff --git a/src/version.rs b/src/version.rs index e14bb422ed5a26c733d77d5c5b4fad5a917825ce..9521dc990b19267f17eac60b715c4121dd5ce403 100644 --- a/src/version.rs +++ b/src/version.rs @@ -20,9 +20,7 @@ impl TryFrom for Version { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("query", ns::VERSION) { - return Err(Error::ParseError("This is not a version element.")); - } + check_self!(elem, "query", ns::VERSION, "version"); check_no_attributes!(elem, "version"); let mut name = None; let mut version = None; From 84355f9e1dd7173133a0e2b9799499cc683352c3 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 May 2018 20:25:59 +0200 Subject: [PATCH 420/698] macros: Simplify generated code for check_no_attributes!(). --- src/macros.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/macros.rs b/src/macros.rs index 3eded4587433fb4151c369aebd4f170f1322789c..8fd057705355898475fdba3e6995e21b926b66e4 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -206,7 +206,9 @@ macro_rules! check_no_children { macro_rules! check_no_attributes { ($elem:ident, $name:tt) => ( - check_no_unknown_attributes!($elem, $name, []); + for _ in $elem.attrs() { + return Err(Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); + } ); } From 292cdd059c0177cebad8f8e16d4392bf99319882 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 14 May 2018 16:07:15 +0200 Subject: [PATCH 421/698] macros: Remove use requirement on try_from::TryFrom. --- src/attention.rs | 3 +-- src/chatstates.rs | 3 +-- src/component.rs | 3 +-- src/delay.rs | 3 +-- src/eme.rs | 3 +-- src/hashes.rs | 2 +- src/ibb.rs | 2 +- src/idle.rs | 3 +-- src/jingle_ibb.rs | 2 +- src/macros.rs | 14 +++++++------- src/media_element.rs | 3 +-- src/message_correct.rs | 3 +-- src/mood.rs | 3 +-- src/ping.rs | 3 +-- src/receipts.rs | 3 +-- src/roster.rs | 2 +- src/sasl.rs | 2 +- src/stanza_id.rs | 3 +-- src/stream.rs | 3 +-- src/websocket.rs | 3 +-- 20 files changed, 26 insertions(+), 40 deletions(-) diff --git a/src/attention.rs b/src/attention.rs index da2e6f6494becee18aba3bea136b8e46cf9b19a8..9a7ed98c618124fd9e0688d0a6187cae15b5cb24 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - use minidom::Element; use error::Error; @@ -17,6 +15,7 @@ generate_empty_element!(Attention, "attention", ns::ATTENTION); #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; #[test] fn test_simple() { diff --git a/src/chatstates.rs b/src/chatstates.rs index 6858c1147a012896e0afe467e7d31beabcd1c814..83ac6f55b5b6973270429381e65802b24f7e2937 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -6,8 +6,6 @@ #![deny(missing_docs)] -use try_from::TryFrom; - use minidom::Element; use error::Error; @@ -38,6 +36,7 @@ generate_element_enum!( #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; #[test] fn test_simple() { diff --git a/src/component.rs b/src/component.rs index 0f42e50ae67b08872579cb0554f7f0a1b66ec594..a93fc40ba9bfdbbca5453aa86ef4ea0c39020f33 100644 --- a/src/component.rs +++ b/src/component.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - use minidom::Element; use error::Error; use helpers::PlainText; @@ -38,6 +36,7 @@ impl Handshake { #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; #[test] fn test_simple() { diff --git a/src/delay.rs b/src/delay.rs index 6904ca838edc35b415f8ad6218d16f9887476b7b..8aba744ee5ce46b11a9c41e7919f63724cb06813 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - use minidom::Element; use date::DateTime; @@ -26,6 +24,7 @@ generate_element_with_text!(Delay, "delay", ns::DELAY, #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; use std::str::FromStr; #[test] diff --git a/src/eme.rs b/src/eme.rs index 5d6e7111ffabfd5060d2b752dd75a65df6ce78e8..9016123f8a5aee0e1e8a4d8fdfa57eb765dd2120 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - use minidom::Element; use error::Error; @@ -26,6 +24,7 @@ ExplicitMessageEncryption, "encryption", ns::EME, [ #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; #[test] fn test_simple() { diff --git a/src/hashes.rs b/src/hashes.rs index e414b91a4fb100ac86a172553810af0fbe27865f..47843cea1ef89fba99c482780704a536aa02f007 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; @@ -93,6 +92,7 @@ impl Hash { #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; #[test] fn test_simple() { diff --git a/src/ibb.rs b/src/ibb.rs index d7717a7b7105cc23258c0d0f36faec4af7b4b089..2c0497cc7b32d6ec82729d403e18577575655ede 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; @@ -40,6 +39,7 @@ generate_element_with_only_attributes!(Close, "close", ns::IBB, [ #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; use std::error::Error as StdError; #[test] diff --git a/src/idle.rs b/src/idle.rs index e4b09924580777f39746e0d25a743a24279fdfb0..f2831ea1813c18909016c244a5c6504b91fef201 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - use minidom::Element; use date::DateTime; @@ -20,6 +18,7 @@ generate_element_with_only_attributes!(Idle, "idle", ns::IDLE, [ #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; use std::str::FromStr; use std::error::Error as StdError; diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 632540ed2cc909d22c3625a5e621a1c6565ab498..dca426681a0852258fa6f559ca1ddc91e0fb9f2d 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; @@ -26,6 +25,7 @@ generate_element_with_only_attributes!(Transport, "transport", ns::JINGLE_IBB, [ #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; use std::error::Error as StdError; #[test] diff --git a/src/macros.rs b/src/macros.rs index 8fd057705355898475fdba3e6995e21b926b66e4..5ea49210ed35ff48214f0e0b733e4a97fcec6015 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -116,7 +116,7 @@ macro_rules! generate_element_enum { $enum ),+ } - impl TryFrom for $elem { + impl ::try_from::TryFrom for $elem { type Err = Error; fn try_from(elem: Element) -> Result<$elem, Error> { check_ns_only!(elem, $name, $ns); @@ -152,7 +152,7 @@ macro_rules! generate_attribute_enum { $enum ),+ } - impl TryFrom for $elem { + impl ::try_from::TryFrom for $elem { type Err = Error; fn try_from(elem: Element) -> Result<$elem, Error> { check_ns_only!(elem, $name, $ns); @@ -231,7 +231,7 @@ macro_rules! generate_empty_element { #[derive(Debug, Clone)] pub struct $elem; - impl TryFrom for $elem { + impl ::try_from::TryFrom for $elem { type Err = Error; fn try_from(elem: Element) -> Result<$elem, Error> { @@ -266,7 +266,7 @@ macro_rules! generate_element_with_only_attributes { )* } - impl TryFrom for $elem { + impl ::try_from::TryFrom for $elem { type Err = Error; fn try_from(elem: Element) -> Result<$elem, Error> { @@ -324,7 +324,7 @@ macro_rules! generate_elem_id { Ok($elem(String::from(s))) } } - impl TryFrom for $elem { + impl ::try_from::TryFrom for $elem { type Err = Error; fn try_from(elem: Element) -> Result<$elem, Error> { check_self!(elem, $name, $ns); @@ -360,7 +360,7 @@ macro_rules! generate_element_with_text { pub $text_ident: $text_type, } - impl TryFrom for $elem { + impl ::try_from::TryFrom for $elem { type Err = Error; fn try_from(elem: Element) -> Result<$elem, Error> { @@ -405,7 +405,7 @@ macro_rules! generate_element_with_children { )* } - impl TryFrom for $elem { + impl ::try_from::TryFrom for $elem { type Err = Error; fn try_from(elem: Element) -> Result<$elem, Error> { diff --git a/src/media_element.rs b/src/media_element.rs index db91c44f44128c6d20bf0b5a9f47929f61486c74..b3e1aa5716c06dabc1c22cd7e8b00ad522de2014 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - use minidom::Element; use error::Error; @@ -33,6 +31,7 @@ generate_element_with_children!(MediaElement, "media", ns::MEDIA_ELEMENT, #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; use data_forms::DataForm; use std::error::Error as StdError; diff --git a/src/message_correct.rs b/src/message_correct.rs index 26cdcf99596b8c79d8403dabbe108f46e8800d02..bfd33b86bd877c31948a789ebcf36c760a0882f0 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - use minidom::Element; use error::Error; @@ -19,6 +17,7 @@ generate_element_with_only_attributes!(Replace, "replace", ns::MESSAGE_CORRECT, #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; #[test] fn test_simple() { diff --git a/src/mood.rs b/src/mood.rs index c83915be62d0d68c595c3352c76bf4bcc802f73d..80f3147a18c18c7e7a840295ac85532e7d8f97d0 100644 --- a/src/mood.rs +++ b/src/mood.rs @@ -6,8 +6,6 @@ #![deny(missing_docs)] -use try_from::TryFrom; - use minidom::Element; use error::Error; @@ -274,6 +272,7 @@ generate_element_enum!( #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; #[test] fn test_simple() { diff --git a/src/ping.rs b/src/ping.rs index fa696d08bbc472cff36cc40a83d83eb98f3af2d7..615580dfd548e98fe5b3505ac8428874e995b348 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -5,8 +5,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - use minidom::Element; use error::Error; @@ -18,6 +16,7 @@ generate_empty_element!(Ping, "ping", ns::PING); #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; #[test] fn test_simple() { diff --git a/src/receipts.rs b/src/receipts.rs index 470a46474dde9241e8fc74570fa186429aa0ace9..e9ec83265692f389285b5d8eaf6998e7c0da4d56 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - use minidom::Element; use error::Error; @@ -21,6 +19,7 @@ generate_element_with_only_attributes!(Received, "received", ns::RECEIPTS, [ #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; #[test] fn test_simple() { diff --git a/src/roster.rs b/src/roster.rs index cb78e4ef5a2f1c60f5b00bba931b913ecd9fdc44..358e4a461c47e5336007cc645b3ee19b331e5cb9 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; @@ -64,6 +63,7 @@ generate_element_with_children!( #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; use compare_elements::NamespaceAwareCompare; #[test] diff --git a/src/sasl.rs b/src/sasl.rs index a0ab15bb7d12c3c7edabaee59f9f289d4a8f6df5..cb192804f094380d2734ff88ae4bd0d96f10e00a 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; @@ -42,6 +41,7 @@ generate_element_with_text!(Success, "success", ns::SASL, #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; #[test] fn test_simple() { diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 6e65e012b885d13e0b1458b3687f710743a21746..3fbcfae857e19be0bf17d99f078f7e595762125a 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - use minidom::Element; use jid::Jid; @@ -25,6 +23,7 @@ generate_element_with_only_attributes!(OriginId, "origin-id", ns::SID, [ #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; use std::str::FromStr; #[test] diff --git a/src/stream.rs b/src/stream.rs index 83335ae2377aa3fd51670595d56a274b4937ac46..168eee1fc25f5d34cd112d8caa9544f6487ef7dc 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - use minidom::Element; use jid::Jid; use error::Error; @@ -56,6 +54,7 @@ impl Stream { #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; #[test] fn test_simple() { diff --git a/src/websocket.rs b/src/websocket.rs index 163ce3ef4ae91b2699a90b60c1d069f7df470584..e98d278dcaf978fd8fb825f9e90e88fe17fbe851 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - use minidom::Element; use jid::Jid; use error::Error; @@ -56,6 +54,7 @@ impl Open { #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; #[test] fn test_simple() { From 040792c242fb8685eedfcf97c5b9ca71222f41ea Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 14 May 2018 16:11:22 +0200 Subject: [PATCH 422/698] macros: Remove use requirement on std::str::FromStr. --- src/data_forms.rs | 1 - src/ibb.rs | 2 -- src/jingle_ft.rs | 2 +- src/jingle_ibb.rs | 2 -- src/jingle_s5b.rs | 2 +- src/macros.rs | 8 ++++---- src/mam.rs | 2 +- src/message.rs | 2 +- src/muc/user.rs | 1 - src/pubsub/event.rs | 1 - src/roster.rs | 3 +-- src/sasl.rs | 2 -- src/stanza_error.rs | 1 - 13 files changed, 9 insertions(+), 20 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index 9b173b8aee979c6cf3f2f9b64889e01ed4a3e1ef..1e297409075bd04de6ea47a6c982aab36999a1d6 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -5,7 +5,6 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use try_from::TryFrom; -use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; diff --git a/src/ibb.rs b/src/ibb.rs index 2c0497cc7b32d6ec82729d403e18577575655ede..d51881776157e5e9d83ad0fb2440d05d9896db0d 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::str::FromStr; - use minidom::{Element, IntoAttributeValue}; use error::Error; diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index c78e1d45bf4776c1af567e61256849dfeab92732..788ff22fa64ed1dbec4497379be9c0a15a1a09ab 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -5,9 +5,9 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use try_from::TryFrom; +use std::str::FromStr; use std::collections::BTreeMap; -use std::str::FromStr; use hashes::Hash; use jingle::{Creator, ContentId}; diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index dca426681a0852258fa6f559ca1ddc91e0fb9f2d..2cf0fb446d963c219971b5c1ea3be7b4e5380eec 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::str::FromStr; - use minidom::{Element, IntoAttributeValue}; use error::Error; diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 558eb560e92e22cc0f9ab5ca17c25912faf42219..7ef0bbd20d3e8a5f44723a2a7386023ac305264c 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -5,7 +5,6 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use try_from::TryFrom; -use std::str::FromStr; use std::net::IpAddr; use minidom::{Element, IntoAttributeValue}; @@ -206,6 +205,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use std::str::FromStr; use compare_elements::NamespaceAwareCompare; #[test] diff --git a/src/macros.rs b/src/macros.rs index 5ea49210ed35ff48214f0e0b733e4a97fcec6015..0a056f8d110dc8b34d9689160b0f95f50f2e92a6 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -51,7 +51,7 @@ macro_rules! generate_attribute { $a ),+ } - impl FromStr for $elem { + impl ::std::str::FromStr for $elem { type Err = Error; fn from_str(s: &str) -> Result<$elem, Error> { Ok(match s { @@ -77,7 +77,7 @@ macro_rules! generate_attribute { $a ),+ } - impl FromStr for $elem { + impl ::std::str::FromStr for $elem { type Err = Error; fn from_str(s: &str) -> Result<$elem, Error> { Ok(match s { @@ -298,7 +298,7 @@ macro_rules! generate_id { ($elem:ident) => ( #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $elem(pub String); - impl FromStr for $elem { + impl ::std::str::FromStr for $elem { type Err = Error; fn from_str(s: &str) -> Result<$elem, Error> { // TODO: add a way to parse that differently when needed. @@ -317,7 +317,7 @@ macro_rules! generate_elem_id { ($elem:ident, $name:tt, $ns:expr) => ( #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $elem(pub String); - impl FromStr for $elem { + impl ::std::str::FromStr for $elem { type Err = Error; fn from_str(s: &str) -> Result<$elem, Error> { // TODO: add a way to parse that differently when needed. diff --git a/src/mam.rs b/src/mam.rs index 82158a40e01db7fc082cba3b8dbadc14f636fb5e..115daf65b76d238ff12e0f4ccb0f0b4710fbb270 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -5,7 +5,6 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use try_from::TryFrom; -use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; use jid::Jid; @@ -220,6 +219,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use std::str::FromStr; #[test] fn test_query() { diff --git a/src/message.rs b/src/message.rs index c4c4a17fb23d5a82a5cb718e11db8341dfcc6bf6..36d09a7c4f6e7aba137529fde5dcad7328e988fe 100644 --- a/src/message.rs +++ b/src/message.rs @@ -5,7 +5,6 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use try_from::TryFrom; -use std::str::FromStr; use std::collections::BTreeMap; use minidom::{Element, IntoAttributeValue}; @@ -278,6 +277,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use std::str::FromStr; use compare_elements::NamespaceAwareCompare; #[test] diff --git a/src/muc/user.rs b/src/muc/user.rs index a6614a63af1c4bd6ada54c41583394bc5102b109..1c3d2cc9bb2f9bda4badaca1072f8aa9890bdbaa 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -6,7 +6,6 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use try_from::{TryFrom, TryInto}; -use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index cbeb6ef759b519fd07b082a92f555b3b28838329..1d478cad7df1f76b8b736edc3eb76d37fa0b9a98 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -5,7 +5,6 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use try_from::TryFrom; -use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; use jid::Jid; diff --git a/src/roster.rs b/src/roster.rs index 358e4a461c47e5336007cc645b3ee19b331e5cb9..54847e02679f2b7c54b2d22077c2f03010d7678a 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::str::FromStr; - use minidom::{Element, IntoAttributeValue}; use jid::Jid; @@ -64,6 +62,7 @@ generate_element_with_children!( mod tests { use super::*; use try_from::TryFrom; + use std::str::FromStr; use compare_elements::NamespaceAwareCompare; #[test] diff --git a/src/sasl.rs b/src/sasl.rs index cb192804f094380d2734ff88ae4bd0d96f10e00a..c53eba2f66a958668e2efe1e7f0c1c5fc771d69c 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::str::FromStr; - use minidom::{Element, IntoAttributeValue}; use error::Error; diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 4977739d3238592180914f85939f616002844893..79386252e9d39593b45743c8980dcb2f6abcd5a9 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -5,7 +5,6 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use try_from::TryFrom; -use std::str::FromStr; use std::collections::BTreeMap; use minidom::{Element, IntoAttributeValue}; From 0d4327eb42511fb9655764d845dc5a921a0851f9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 14 May 2018 16:12:56 +0200 Subject: [PATCH 423/698] macros: Remove use requirement on minidom::IntoAttributeValue. --- src/data_forms.rs | 2 +- src/ibb.rs | 2 +- src/jingle.rs | 2 +- src/jingle_ft.rs | 2 +- src/jingle_ibb.rs | 2 +- src/jingle_s5b.rs | 2 +- src/macros.rs | 6 +++--- src/mam.rs | 2 +- src/message.rs | 2 +- src/muc/user.rs | 2 +- src/pubsub/event.rs | 2 +- src/roster.rs | 2 +- src/sasl.rs | 2 +- src/stanza_error.rs | 2 +- 14 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index 1e297409075bd04de6ea47a6c982aab36999a1d6..74985edf0421c74184615d7bb248056828477d04 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -6,7 +6,7 @@ use try_from::TryFrom; -use minidom::{Element, IntoAttributeValue}; +use minidom::Element; use error::Error; use ns; diff --git a/src/ibb.rs b/src/ibb.rs index d51881776157e5e9d83ad0fb2440d05d9896db0d..3471715b9bcebe39bbb2052fdb21e19ba0184176 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::{Element, IntoAttributeValue}; +use minidom::Element; use error::Error; diff --git a/src/jingle.rs b/src/jingle.rs index 08002bcf61ed5e4830a18f55f810126af2386818..1b89dae7ca2d71eb85b3988dd8ae39076668cac5 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -7,7 +7,7 @@ use try_from::TryFrom; use std::str::FromStr; -use minidom::{Element, IntoAttributeValue}; +use minidom::Element; use jid::Jid; use error::Error; diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 788ff22fa64ed1dbec4497379be9c0a15a1a09ab..e2539f9907c1424fd07f4e35c4bf935977201a91 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -13,7 +13,7 @@ use hashes::Hash; use jingle::{Creator, ContentId}; use date::DateTime; -use minidom::{Element, IntoAttributeValue}; +use minidom::Element; use error::Error; use ns; diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 2cf0fb446d963c219971b5c1ea3be7b4e5380eec..2f6d0b1575a17bcb9238f86d79ae3dc557665161 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::{Element, IntoAttributeValue}; +use minidom::Element; use error::Error; diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 7ef0bbd20d3e8a5f44723a2a7386023ac305264c..4e8ca817b65a129d0064a59cad6f7ed2d6c31842 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -7,7 +7,7 @@ use try_from::TryFrom; use std::net::IpAddr; -use minidom::{Element, IntoAttributeValue}; +use minidom::Element; use jid::Jid; use error::Error; diff --git a/src/macros.rs b/src/macros.rs index 0a056f8d110dc8b34d9689160b0f95f50f2e92a6..45b5c056df97a25f16452485d318e01f1333976d 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -60,7 +60,7 @@ macro_rules! generate_attribute { }) } } - impl IntoAttributeValue for $elem { + impl ::minidom::IntoAttributeValue for $elem { fn into_attribute_value(self) -> Option { Some(String::from(match self { $($elem::$a => $b),+ @@ -86,7 +86,7 @@ macro_rules! generate_attribute { }) } } - impl IntoAttributeValue for $elem { + impl ::minidom::IntoAttributeValue for $elem { #[allow(unreachable_patterns)] fn into_attribute_value(self) -> Option { Some(String::from(match self { @@ -305,7 +305,7 @@ macro_rules! generate_id { Ok($elem(String::from(s))) } } - impl IntoAttributeValue for $elem { + impl ::minidom::IntoAttributeValue for $elem { fn into_attribute_value(self) -> Option { Some(self.0) } diff --git a/src/mam.rs b/src/mam.rs index 115daf65b76d238ff12e0f4ccb0f0b4710fbb270..6af739d9dea90ebaadbdc916e79c51f54d752d02 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -6,7 +6,7 @@ use try_from::TryFrom; -use minidom::{Element, IntoAttributeValue}; +use minidom::Element; use jid::Jid; use error::Error; diff --git a/src/message.rs b/src/message.rs index 36d09a7c4f6e7aba137529fde5dcad7328e988fe..72fcbde47663c08b3882abb6d2f76d7b7e7b53a6 100644 --- a/src/message.rs +++ b/src/message.rs @@ -7,7 +7,7 @@ use try_from::TryFrom; use std::collections::BTreeMap; -use minidom::{Element, IntoAttributeValue}; +use minidom::Element; use jid::Jid; diff --git a/src/muc/user.rs b/src/muc/user.rs index 1c3d2cc9bb2f9bda4badaca1072f8aa9890bdbaa..33b20d87cd05fac2e6bb1c433d867956662e8370 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -7,7 +7,7 @@ use try_from::{TryFrom, TryInto}; -use minidom::{Element, IntoAttributeValue}; +use minidom::Element; use jid::Jid; diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 1d478cad7df1f76b8b736edc3eb76d37fa0b9a98..136489562ef3abf4606b0a25db85915d08cfc086 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -6,7 +6,7 @@ use try_from::TryFrom; -use minidom::{Element, IntoAttributeValue}; +use minidom::Element; use jid::Jid; use date::DateTime; diff --git a/src/roster.rs b/src/roster.rs index 54847e02679f2b7c54b2d22077c2f03010d7678a..96d39586af794304e1cc212dd2a8a4060e186cca 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::{Element, IntoAttributeValue}; +use minidom::Element; use jid::Jid; use error::Error; diff --git a/src/sasl.rs b/src/sasl.rs index c53eba2f66a958668e2efe1e7f0c1c5fc771d69c..e7a226327db7958becfa79ec771a529382269b06 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::{Element, IntoAttributeValue}; +use minidom::Element; use error::Error; diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 79386252e9d39593b45743c8980dcb2f6abcd5a9..5d26fa2598eb25eba48fb20a93cd3eb0bb349b6f 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -7,7 +7,7 @@ use try_from::TryFrom; use std::collections::BTreeMap; -use minidom::{Element, IntoAttributeValue}; +use minidom::Element; use error::Error; use jid::Jid; From 6f497027f5a851612dd0e3e65707684c1132b41e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 14 May 2018 16:17:21 +0200 Subject: [PATCH 424/698] macros: Remove use requirement on minidom::Element. --- src/attention.rs | 3 +- src/chatstates.rs | 3 +- src/component.rs | 2 +- src/delay.rs | 2 +- src/eme.rs | 3 +- src/hashes.rs | 3 +- src/ibb.rs | 3 +- src/idle.rs | 2 +- src/jingle_ibb.rs | 3 +- src/macros.rs | 70 +++++++++++++++++++++--------------------- src/media_element.rs | 3 +- src/message_correct.rs | 3 +- src/mood.rs | 3 +- src/ping.rs | 3 +- src/receipts.rs | 3 +- src/roster.rs | 2 +- src/sasl.rs | 3 +- src/stanza_id.rs | 2 +- src/stream.rs | 2 +- src/websocket.rs | 2 +- 20 files changed, 55 insertions(+), 65 deletions(-) diff --git a/src/attention.rs b/src/attention.rs index 9a7ed98c618124fd9e0688d0a6187cae15b5cb24..d7521e9c758b8533b068c3647491f82b09a6c903 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; - use error::Error; use ns; @@ -16,6 +14,7 @@ generate_empty_element!(Attention, "attention", ns::ATTENTION); mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; #[test] fn test_simple() { diff --git a/src/chatstates.rs b/src/chatstates.rs index 83ac6f55b5b6973270429381e65802b24f7e2937..33ce0a9c04f9206fafd058c19bfd5b9894d68378 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -6,8 +6,6 @@ #![deny(missing_docs)] -use minidom::Element; - use error::Error; use ns; @@ -37,6 +35,7 @@ generate_element_enum!( mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; #[test] fn test_simple() { diff --git a/src/component.rs b/src/component.rs index a93fc40ba9bfdbbca5453aa86ef4ea0c39020f33..778b9618ef15b6d7280dfb7ca231a0bf740440ca 100644 --- a/src/component.rs +++ b/src/component.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; use error::Error; use helpers::PlainText; use ns; @@ -37,6 +36,7 @@ impl Handshake { mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; #[test] fn test_simple() { diff --git a/src/delay.rs b/src/delay.rs index 8aba744ee5ce46b11a9c41e7919f63724cb06813..5591c113dc45beed397ac90b7536e2fcada40e2d 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; use date::DateTime; use error::Error; @@ -25,6 +24,7 @@ generate_element_with_text!(Delay, "delay", ns::DELAY, mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; use std::str::FromStr; #[test] diff --git a/src/eme.rs b/src/eme.rs index 9016123f8a5aee0e1e8a4d8fdfa57eb765dd2120..b023a5abfbd165ac8536baf87aa0dfee5158dbbd 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; - use error::Error; use ns; @@ -25,6 +23,7 @@ ExplicitMessageEncryption, "encryption", ns::EME, [ mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; #[test] fn test_simple() { diff --git a/src/hashes.rs b/src/hashes.rs index 47843cea1ef89fba99c482780704a536aa02f007..472d4c471063e90fc8c2635613ea2b41e3490084 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -6,7 +6,7 @@ use std::str::FromStr; -use minidom::{Element, IntoAttributeValue}; +use minidom::IntoAttributeValue; use error::Error; @@ -93,6 +93,7 @@ impl Hash { mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; #[test] fn test_simple() { diff --git a/src/ibb.rs b/src/ibb.rs index 3471715b9bcebe39bbb2052fdb21e19ba0184176..555dfb2ee714527328d105f4288847e4e164ae94 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; - use error::Error; use ns; @@ -38,6 +36,7 @@ generate_element_with_only_attributes!(Close, "close", ns::IBB, [ mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; use std::error::Error as StdError; #[test] diff --git a/src/idle.rs b/src/idle.rs index f2831ea1813c18909016c244a5c6504b91fef201..ea82ee31061a81df7e14c42373841501b5e46974 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; use date::DateTime; use error::Error; @@ -19,6 +18,7 @@ generate_element_with_only_attributes!(Idle, "idle", ns::IDLE, [ mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; use std::str::FromStr; use std::error::Error as StdError; diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 2f6d0b1575a17bcb9238f86d79ae3dc557665161..5cc0f25623d52853e1b079c859ffb530d3ec57dc 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; - use error::Error; use ns; @@ -24,6 +22,7 @@ generate_element_with_only_attributes!(Transport, "transport", ns::JINGLE_IBB, [ mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; use std::error::Error as StdError; #[test] diff --git a/src/macros.rs b/src/macros.rs index 45b5c056df97a25f16452485d318e01f1333976d..752e5bb85db623ea47c4b53901a8e2fefe2f6ab0 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -116,9 +116,9 @@ macro_rules! generate_element_enum { $enum ),+ } - impl ::try_from::TryFrom for $elem { + impl ::try_from::TryFrom<::minidom::Element> for $elem { type Err = Error; - fn try_from(elem: Element) -> Result<$elem, Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, Error> { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); @@ -128,9 +128,9 @@ macro_rules! generate_element_enum { }) } } - impl From<$elem> for Element { - fn from(elem: $elem) -> Element { - Element::builder(match elem { + impl From<$elem> for ::minidom::Element { + fn from(elem: $elem) -> ::minidom::Element { + ::minidom::Element::builder(match elem { $($elem::$enum => $enum_name,)+ }).ns($ns) .build() @@ -152,9 +152,9 @@ macro_rules! generate_attribute_enum { $enum ),+ } - impl ::try_from::TryFrom for $elem { + impl ::try_from::TryFrom<::minidom::Element> for $elem { type Err = Error; - fn try_from(elem: Element) -> Result<$elem, Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, Error> { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); check_no_unknown_attributes!(elem, $name, [$attr]); @@ -164,9 +164,9 @@ macro_rules! generate_attribute_enum { }) } } - impl From<$elem> for Element { - fn from(elem: $elem) -> Element { - Element::builder($name) + impl From<$elem> for ::minidom::Element { + fn from(elem: $elem) -> ::minidom::Element { + ::minidom::Element::builder($name) .ns($ns) .attr($attr, match elem { $($elem::$enum => $enum_name,)+ @@ -231,10 +231,10 @@ macro_rules! generate_empty_element { #[derive(Debug, Clone)] pub struct $elem; - impl ::try_from::TryFrom for $elem { + impl ::try_from::TryFrom<::minidom::Element> for $elem { type Err = Error; - fn try_from(elem: Element) -> Result<$elem, Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); @@ -242,9 +242,9 @@ macro_rules! generate_empty_element { } } - impl From<$elem> for Element { - fn from(_: $elem) -> Element { - Element::builder($name) + impl From<$elem> for ::minidom::Element { + fn from(_: $elem) -> ::minidom::Element { + ::minidom::Element::builder($name) .ns($ns) .build() } @@ -266,10 +266,10 @@ macro_rules! generate_element_with_only_attributes { )* } - impl ::try_from::TryFrom for $elem { + impl ::try_from::TryFrom<::minidom::Element> for $elem { type Err = Error; - fn try_from(elem: Element) -> Result<$elem, Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); @@ -281,9 +281,9 @@ macro_rules! generate_element_with_only_attributes { } } - impl From<$elem> for Element { - fn from(elem: $elem) -> Element { - Element::builder($name) + impl From<$elem> for ::minidom::Element { + fn from(elem: $elem) -> ::minidom::Element { + ::minidom::Element::builder($name) .ns($ns) $( .attr($attr_name, elem.$attr) @@ -324,9 +324,9 @@ macro_rules! generate_elem_id { Ok($elem(String::from(s))) } } - impl ::try_from::TryFrom for $elem { + impl ::try_from::TryFrom<::minidom::Element> for $elem { type Err = Error; - fn try_from(elem: Element) -> Result<$elem, Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); @@ -334,9 +334,9 @@ macro_rules! generate_elem_id { Ok($elem(elem.text())) } } - impl From<$elem> for Element { - fn from(elem: $elem) -> Element { - Element::builder($name) + impl From<$elem> for ::minidom::Element { + fn from(elem: $elem) -> ::minidom::Element { + ::minidom::Element::builder($name) .ns($ns) .append(elem.0) .build() @@ -360,10 +360,10 @@ macro_rules! generate_element_with_text { pub $text_ident: $text_type, } - impl ::try_from::TryFrom for $elem { + impl ::try_from::TryFrom<::minidom::Element> for $elem { type Err = Error; - fn try_from(elem: Element) -> Result<$elem, Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); @@ -376,9 +376,9 @@ macro_rules! generate_element_with_text { } } - impl From<$elem> for Element { - fn from(elem: $elem) -> Element { - Element::builder($name) + impl From<$elem> for ::minidom::Element { + fn from(elem: $elem) -> ::minidom::Element { + ::minidom::Element::builder($name) .ns($ns) $( .attr($attr_name, elem.$attr) @@ -405,10 +405,10 @@ macro_rules! generate_element_with_children { )* } - impl ::try_from::TryFrom for $elem { + impl ::try_from::TryFrom<::minidom::Element> for $elem { type Err = Error; - fn try_from(elem: Element) -> Result<$elem, Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, Error> { check_self!(elem, $name, $ns); check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); let mut parsed_children = vec!(); @@ -433,9 +433,9 @@ macro_rules! generate_element_with_children { } } - impl From<$elem> for Element { - fn from(elem: $elem) -> Element { - Element::builder($name) + impl From<$elem> for ::minidom::Element { + fn from(elem: $elem) -> ::minidom::Element { + ::minidom::Element::builder($name) .ns($ns) $( .attr($attr_name, elem.$attr) diff --git a/src/media_element.rs b/src/media_element.rs index b3e1aa5716c06dabc1c22cd7e8b00ad522de2014..a3d159a382c997daa954b70c4a05076f8cbbabc1 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; - use error::Error; use ns; @@ -32,6 +30,7 @@ generate_element_with_children!(MediaElement, "media", ns::MEDIA_ELEMENT, mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; use data_forms::DataForm; use std::error::Error as StdError; diff --git a/src/message_correct.rs b/src/message_correct.rs index bfd33b86bd877c31948a789ebcf36c760a0882f0..259eb88a3643ed3c50a0cfbb6198da78662c9434 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; - use error::Error; use ns; @@ -18,6 +16,7 @@ generate_element_with_only_attributes!(Replace, "replace", ns::MESSAGE_CORRECT, mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; #[test] fn test_simple() { diff --git a/src/mood.rs b/src/mood.rs index 80f3147a18c18c7e7a840295ac85532e7d8f97d0..5ec89f600a5d306c665d9196e54a4290eb45b416 100644 --- a/src/mood.rs +++ b/src/mood.rs @@ -6,8 +6,6 @@ #![deny(missing_docs)] -use minidom::Element; - use error::Error; use ns; @@ -273,6 +271,7 @@ generate_element_enum!( mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; #[test] fn test_simple() { diff --git a/src/ping.rs b/src/ping.rs index 615580dfd548e98fe5b3505ac8428874e995b348..cacf3c355694ab90dfc14a80d2e90b687130ac2d 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -5,8 +5,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; - use error::Error; use ns; @@ -17,6 +15,7 @@ generate_empty_element!(Ping, "ping", ns::PING); mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; #[test] fn test_simple() { diff --git a/src/receipts.rs b/src/receipts.rs index e9ec83265692f389285b5d8eaf6998e7c0da4d56..0c785c1cb63ec9a3838ad639b73749f031d08949 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; - use error::Error; use ns; @@ -20,6 +18,7 @@ generate_element_with_only_attributes!(Received, "received", ns::RECEIPTS, [ mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; #[test] fn test_simple() { diff --git a/src/roster.rs b/src/roster.rs index 96d39586af794304e1cc212dd2a8a4060e186cca..556cf11384aee058ba3b9f542175d83d3d6c208f 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; use jid::Jid; use error::Error; @@ -62,6 +61,7 @@ generate_element_with_children!( mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; use std::str::FromStr; use compare_elements::NamespaceAwareCompare; diff --git a/src/sasl.rs b/src/sasl.rs index e7a226327db7958becfa79ec771a529382269b06..23a56c43bdb64d96cc9f81fab0f4092593294d8b 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; - use error::Error; use ns; @@ -40,6 +38,7 @@ generate_element_with_text!(Success, "success", ns::SASL, mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; #[test] fn test_simple() { diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 3fbcfae857e19be0bf17d99f078f7e595762125a..a02d8bf45c442de6f2d1a8f4a3992d25e82b9ca3 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; use jid::Jid; use error::Error; @@ -24,6 +23,7 @@ generate_element_with_only_attributes!(OriginId, "origin-id", ns::SID, [ mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; use std::str::FromStr; #[test] diff --git a/src/stream.rs b/src/stream.rs index 168eee1fc25f5d34cd112d8caa9544f6487ef7dc..ee934116a58bdbaff7920677b95cccdb127c2bf5 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; use jid::Jid; use error::Error; use ns; @@ -55,6 +54,7 @@ impl Stream { mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; #[test] fn test_simple() { diff --git a/src/websocket.rs b/src/websocket.rs index e98d278dcaf978fd8fb825f9e90e88fe17fbe851..4257278eb8f0eccece30258e64d555e4e38182e6 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; use jid::Jid; use error::Error; use ns; @@ -55,6 +54,7 @@ impl Open { mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; #[test] fn test_simple() { From d9aaa3e9ce756dae638718ebd30afda9b00e6555 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 14 May 2018 16:21:39 +0200 Subject: [PATCH 425/698] macros: Remove use requirement on error::Error. --- src/attention.rs | 3 +- src/chatstates.rs | 3 +- src/component.rs | 1 - src/delay.rs | 2 +- src/eme.rs | 3 +- src/ibb.rs | 3 +- src/idle.rs | 3 +- src/jingle_ibb.rs | 3 +- src/macros.rs | 66 +++++++++++++++++++++--------------------- src/media_element.rs | 3 +- src/message_correct.rs | 3 +- src/mood.rs | 2 -- src/ping.rs | 3 +- src/receipts.rs | 2 -- src/roster.rs | 2 +- src/sasl.rs | 2 -- src/stanza_id.rs | 3 +- src/stream.rs | 1 - src/websocket.rs | 1 - 19 files changed, 45 insertions(+), 64 deletions(-) diff --git a/src/attention.rs b/src/attention.rs index d7521e9c758b8533b068c3647491f82b09a6c903..02cc02f3ac3c6d5bb688c5b3c4b567096f47fda4 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use error::Error; - use ns; generate_empty_element!(Attention, "attention", ns::ATTENTION); @@ -15,6 +13,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; + use error::Error; #[test] fn test_simple() { diff --git a/src/chatstates.rs b/src/chatstates.rs index 33ce0a9c04f9206fafd058c19bfd5b9894d68378..a328e23454b7fe3ef17ecc105752ca463c4085d6 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -6,8 +6,6 @@ #![deny(missing_docs)] -use error::Error; - use ns; generate_element_enum!( @@ -36,6 +34,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; + use error::Error; #[test] fn test_simple() { diff --git a/src/component.rs b/src/component.rs index 778b9618ef15b6d7280dfb7ca231a0bf740440ca..7c74ff1a09219109a8107bdfe5c7c22c28fb3e22 100644 --- a/src/component.rs +++ b/src/component.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use error::Error; use helpers::PlainText; use ns; diff --git a/src/delay.rs b/src/delay.rs index 5591c113dc45beed397ac90b7536e2fcada40e2d..84d12ad4be6db0c91fdef951ac4c61144a3ca91d 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -6,7 +6,6 @@ use date::DateTime; -use error::Error; use jid::Jid; use ns; @@ -25,6 +24,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; + use error::Error; use std::str::FromStr; #[test] diff --git a/src/eme.rs b/src/eme.rs index b023a5abfbd165ac8536baf87aa0dfee5158dbbd..de48c3b0aff6d67c42b5bab8ffbe8fe49ae92859 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use error::Error; - use ns; generate_element_with_only_attributes!( @@ -24,6 +22,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; + use error::Error; #[test] fn test_simple() { diff --git a/src/ibb.rs b/src/ibb.rs index 555dfb2ee714527328d105f4288847e4e164ae94..60653da069e30ea9a09b358ab72454424dba43ed 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use error::Error; - use ns; use helpers::Base64; @@ -37,6 +35,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; + use error::Error; use std::error::Error as StdError; #[test] diff --git a/src/idle.rs b/src/idle.rs index ea82ee31061a81df7e14c42373841501b5e46974..a94a5ee63c207170f1339b7fa909c557b7d00a96 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -6,8 +6,6 @@ use date::DateTime; -use error::Error; - use ns; generate_element_with_only_attributes!(Idle, "idle", ns::IDLE, [ @@ -19,6 +17,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; + use error::Error; use std::str::FromStr; use std::error::Error as StdError; diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 5cc0f25623d52853e1b079c859ffb530d3ec57dc..d96e9b910b27ef06644a4cd570022b74f674dc9f 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use error::Error; - use ns; use ibb::Stanza; @@ -23,6 +21,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; + use error::Error; use std::error::Error as StdError; #[test] diff --git a/src/macros.rs b/src/macros.rs index 752e5bb85db623ea47c4b53901a8e2fefe2f6ab0..54297b38d3559149313e2476001942d316ecb68a 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -24,7 +24,7 @@ macro_rules! get_attr { ($elem:ident, $attr:tt, required, $value:ident, $func:expr) => ( match $elem.attr($attr) { Some($value) => $func, - None => return Err(Error::ParseError(concat!("Required attribute '", $attr, "' missing."))), + None => return Err(::error::Error::ParseError(concat!("Required attribute '", $attr, "' missing."))), } ); ($elem:ident, $attr:tt, default, $value:ident, $func:expr) => ( @@ -52,11 +52,11 @@ macro_rules! generate_attribute { ),+ } impl ::std::str::FromStr for $elem { - type Err = Error; - fn from_str(s: &str) -> Result<$elem, Error> { + type Err = ::error::Error; + fn from_str(s: &str) -> Result<$elem, ::error::Error> { Ok(match s { $($b => $elem::$a),+, - _ => return Err(Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + _ => return Err(::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), }) } } @@ -78,11 +78,11 @@ macro_rules! generate_attribute { ),+ } impl ::std::str::FromStr for $elem { - type Err = Error; - fn from_str(s: &str) -> Result<$elem, Error> { + type Err = ::error::Error; + fn from_str(s: &str) -> Result<$elem, ::error::Error> { Ok(match s { $($b => $elem::$a),+, - _ => return Err(Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + _ => return Err(::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), }) } } @@ -117,14 +117,14 @@ macro_rules! generate_element_enum { ),+ } impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, Error> { + type Err = ::error::Error; + fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); Ok(match elem.name() { $($enum_name => $elem::$enum,)+ - _ => return Err(Error::ParseError(concat!("This is not a ", $name, " element."))), + _ => return Err(::error::Error::ParseError(concat!("This is not a ", $name, " element."))), }) } } @@ -153,14 +153,14 @@ macro_rules! generate_attribute_enum { ),+ } impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, Error> { + type Err = ::error::Error; + fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); check_no_unknown_attributes!(elem, $name, [$attr]); Ok(match get_attr!(elem, $attr, required) { $($enum_name => $elem::$enum,)+ - _ => return Err(Error::ParseError(concat!("Invalid ", $name, " ", $attr, " value."))), + _ => return Err(::error::Error::ParseError(concat!("Invalid ", $name, " ", $attr, " value."))), }) } } @@ -183,7 +183,7 @@ macro_rules! check_self { ); ($elem:ident, $name:tt, $ns:expr, $pretty_name:tt) => ( if !$elem.is($name, $ns) { - return Err(Error::ParseError(concat!("This is not a ", $pretty_name, " element."))); + return Err(::error::Error::ParseError(concat!("This is not a ", $pretty_name, " element."))); } ); } @@ -191,7 +191,7 @@ macro_rules! check_self { macro_rules! check_ns_only { ($elem:ident, $name:tt, $ns:expr) => ( if !$elem.has_ns($ns) { - return Err(Error::ParseError(concat!("This is not a ", $name, " element."))); + return Err(::error::Error::ParseError(concat!("This is not a ", $name, " element."))); } ); } @@ -199,7 +199,7 @@ macro_rules! check_ns_only { macro_rules! check_no_children { ($elem:ident, $name:tt) => ( for _ in $elem.children() { - return Err(Error::ParseError(concat!("Unknown child in ", $name, " element."))); + return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); } ); } @@ -207,7 +207,7 @@ macro_rules! check_no_children { macro_rules! check_no_attributes { ($elem:ident, $name:tt) => ( for _ in $elem.attrs() { - return Err(Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); + return Err(::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); } ); } @@ -220,7 +220,7 @@ macro_rules! check_no_unknown_attributes { continue; } )* - return Err(Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); + return Err(::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); } ); } @@ -232,9 +232,9 @@ macro_rules! generate_empty_element { pub struct $elem; impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = Error; + type Err = ::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); @@ -267,9 +267,9 @@ macro_rules! generate_element_with_only_attributes { } impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = Error; + type Err = ::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); @@ -299,8 +299,8 @@ macro_rules! generate_id { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $elem(pub String); impl ::std::str::FromStr for $elem { - type Err = Error; - fn from_str(s: &str) -> Result<$elem, Error> { + type Err = ::error::Error; + fn from_str(s: &str) -> Result<$elem, ::error::Error> { // TODO: add a way to parse that differently when needed. Ok($elem(String::from(s))) } @@ -318,15 +318,15 @@ macro_rules! generate_elem_id { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $elem(pub String); impl ::std::str::FromStr for $elem { - type Err = Error; - fn from_str(s: &str) -> Result<$elem, Error> { + type Err = ::error::Error; + fn from_str(s: &str) -> Result<$elem, ::error::Error> { // TODO: add a way to parse that differently when needed. Ok($elem(String::from(s))) } } impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, Error> { + type Err = ::error::Error; + fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); @@ -361,9 +361,9 @@ macro_rules! generate_element_with_text { } impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = Error; + type Err = ::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); @@ -406,9 +406,9 @@ macro_rules! generate_element_with_children { } impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = Error; + type Err = ::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { check_self!(elem, $name, $ns); check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); let mut parsed_children = vec!(); @@ -420,7 +420,7 @@ macro_rules! generate_element_with_children { continue; } )* - return Err(Error::ParseError(concat!("Unknown child in ", $name, " element."))); + return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); } Ok($elem { $( diff --git a/src/media_element.rs b/src/media_element.rs index a3d159a382c997daa954b70c4a05076f8cbbabc1..c9d6a129c1c3494f2c322914eb31eac151d14528 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use error::Error; - use ns; use helpers::TrimmedPlainText; @@ -31,6 +29,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; + use error::Error; use data_forms::DataForm; use std::error::Error as StdError; diff --git a/src/message_correct.rs b/src/message_correct.rs index 259eb88a3643ed3c50a0cfbb6198da78662c9434..edd9605f79cbc33b77eab37e971ae5940e4aa8bc 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use error::Error; - use ns; generate_element_with_only_attributes!(Replace, "replace", ns::MESSAGE_CORRECT, [ @@ -17,6 +15,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; + use error::Error; #[test] fn test_simple() { diff --git a/src/mood.rs b/src/mood.rs index 5ec89f600a5d306c665d9196e54a4290eb45b416..ed8b19b64b6a62155853f5504dbf7d1861abce30 100644 --- a/src/mood.rs +++ b/src/mood.rs @@ -6,8 +6,6 @@ #![deny(missing_docs)] -use error::Error; - use ns; generate_element_enum!( diff --git a/src/ping.rs b/src/ping.rs index cacf3c355694ab90dfc14a80d2e90b687130ac2d..12368c10b59a996714590b5234cb5985e00b68fe 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -5,8 +5,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use error::Error; - use ns; generate_empty_element!(Ping, "ping", ns::PING); @@ -16,6 +14,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; + use error::Error; #[test] fn test_simple() { diff --git a/src/receipts.rs b/src/receipts.rs index 0c785c1cb63ec9a3838ad639b73749f031d08949..564e8fc9e969c1b49f98dd269f597ce17e3bfdca 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use error::Error; - use ns; generate_empty_element!(Request, "request", ns::RECEIPTS); diff --git a/src/roster.rs b/src/roster.rs index 556cf11384aee058ba3b9f542175d83d3d6c208f..01011fded1ec93fc60b27a68fe5d2af58451b633 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -6,7 +6,6 @@ use jid::Jid; -use error::Error; use ns; generate_elem_id!(Group, "group", ns::ROSTER); @@ -62,6 +61,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; + use error::Error; use std::str::FromStr; use compare_elements::NamespaceAwareCompare; diff --git a/src/sasl.rs b/src/sasl.rs index 23a56c43bdb64d96cc9f81fab0f4092593294d8b..3cb84c78c1ff05734583c1fb966dfb05ce4ad26b 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use error::Error; - use ns; use helpers::Base64; diff --git a/src/stanza_id.rs b/src/stanza_id.rs index a02d8bf45c442de6f2d1a8f4a3992d25e82b9ca3..bad880029dfc6dcf65e11bcfa37d0d6f95708626 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -6,8 +6,6 @@ use jid::Jid; -use error::Error; - use ns; generate_element_with_only_attributes!(StanzaId, "stanza-id", ns::SID, [ @@ -24,6 +22,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; + use error::Error; use std::str::FromStr; #[test] diff --git a/src/stream.rs b/src/stream.rs index ee934116a58bdbaff7920677b95cccdb127c2bf5..9d9eff4625340cc86bcaf7614bc0f67cf7c07297 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -5,7 +5,6 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use jid::Jid; -use error::Error; use ns; generate_element_with_only_attributes!(Stream, "stream", ns::STREAM, [ diff --git a/src/websocket.rs b/src/websocket.rs index 4257278eb8f0eccece30258e64d555e4e38182e6..2402164807977d6100355576c8d680b23036e1e7 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -5,7 +5,6 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use jid::Jid; -use error::Error; use ns; generate_element_with_only_attributes!(Open, "open", ns::WEBSOCKET, [ From 93b018e5ac2fb10c59157c246ca2ae5a1e2bae26 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 14 May 2018 16:30:28 +0200 Subject: [PATCH 426/698] macros: Remove use requirement on ns. --- src/attention.rs | 4 +--- src/bind.rs | 2 +- src/blocking.rs | 8 +++---- src/caps.rs | 2 +- src/chatstates.rs | 5 ++--- src/component.rs | 4 +--- src/data_forms.rs | 6 +++--- src/delay.rs | 3 +-- src/disco.rs | 16 +++++++------- src/ecaps2.rs | 2 +- src/eme.rs | 4 +--- src/forwarding.rs | 2 +- src/hashes.rs | 3 +-- src/ibb.rs | 7 +++--- src/ibr.rs | 2 +- src/idle.rs | 4 +--- src/iq.rs | 2 +- src/jingle.rs | 6 +++--- src/jingle_ft.rs | 12 +++++------ src/jingle_ibb.rs | 4 +--- src/jingle_s5b.rs | 4 ++-- src/macros.rs | 48 +++++++++++++++++++++--------------------- src/mam.rs | 8 +++---- src/media_element.rs | 7 +++--- src/message.rs | 8 +++---- src/message_correct.rs | 4 +--- src/mood.rs | 4 +--- src/muc/muc.rs | 2 +- src/muc/user.rs | 12 +++++------ src/ping.rs | 4 +--- src/presence.rs | 2 +- src/pubsub/event.rs | 4 ++-- src/receipts.rs | 7 +++--- src/roster.rs | 12 +++++------ src/rsm.rs | 2 +- src/sasl.rs | 9 ++++---- src/stanza_error.rs | 4 ++-- src/stanza_id.rs | 6 ++---- src/stream.rs | 3 +-- src/version.rs | 2 +- src/websocket.rs | 3 +-- 41 files changed, 112 insertions(+), 141 deletions(-) diff --git a/src/attention.rs b/src/attention.rs index 02cc02f3ac3c6d5bb688c5b3c4b567096f47fda4..20f79bae31bbe5b4f8b23daeccf656b61b9651f4 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -4,9 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use ns; - -generate_empty_element!(Attention, "attention", ns::ATTENTION); +generate_empty_element!(Attention, "attention", ATTENTION); #[cfg(test)] mod tests { diff --git a/src/bind.rs b/src/bind.rs index 3e76dba01dc39d25c4db8e2d2b910deb2ff0f58f..4597636146584e53bc80c551e512eb20ced877f2 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -33,7 +33,7 @@ impl TryFrom for Bind { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "bind", ns::BIND); + check_self!(elem, "bind", BIND); check_no_attributes!(elem, "bind"); let mut bind = Bind::None; diff --git a/src/blocking.rs b/src/blocking.rs index 997e24111ff536f32396a4befd46ffb2e6dbe850..b24c9e49d82dea86edc29cdc7cd330f584bf2a5b 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -13,7 +13,7 @@ use error::Error; use ns; -generate_empty_element!(BlocklistRequest, "blocklist", ns::BLOCKING); +generate_empty_element!(BlocklistRequest, "blocklist", BLOCKING); macro_rules! generate_blocking_element { ($elem:ident, $name:tt) => ( @@ -26,11 +26,11 @@ macro_rules! generate_blocking_element { type Err = Error; fn try_from(elem: Element) -> Result<$elem, Error> { - check_self!(elem, $name, ns::BLOCKING); + check_self!(elem, $name, BLOCKING); check_no_attributes!(elem, $name); let mut items = vec!(); for child in elem.children() { - check_self!(child, "item", ns::BLOCKING); + check_self!(child, "item", BLOCKING); check_no_unknown_attributes!(child, "item", ["jid"]); check_no_children!(child, "item"); items.push(get_attr!(child, "jid", required)); @@ -59,7 +59,7 @@ generate_blocking_element!(BlocklistResult, "blocklist"); generate_blocking_element!(Block, "block"); generate_blocking_element!(Unblock, "unblock"); -generate_empty_element!(Blocked, "blocked", ns::BLOCKING_ERRORS); +generate_empty_element!(Blocked, "blocked", BLOCKING_ERRORS); #[cfg(test)] mod tests { diff --git a/src/caps.rs b/src/caps.rs index 05f84e1e3a393cab83893b217252a59d8cb4f179..cfafef7e3cbdfc469bcf46f64095062bf3e8f5a4 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -32,7 +32,7 @@ impl TryFrom for Caps { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "c", ns::CAPS, "caps"); + check_self!(elem, "c", CAPS, "caps"); check_no_children!(elem, "caps"); check_no_unknown_attributes!(elem, "caps", ["hash", "ver", "ext", "node"]); let ver: String = get_attr!(elem, "ver", required); diff --git a/src/chatstates.rs b/src/chatstates.rs index a328e23454b7fe3ef17ecc105752ca463c4085d6..d341975fae318176ae740f3b06b2f301b6d3a527 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -6,12 +6,10 @@ #![deny(missing_docs)] -use ns; - generate_element_enum!( /// Enum representing chatstate elements part of the /// `http://jabber.org/protocol/chatstates` namespace. - ChatState, "chatstate", ns::CHATSTATES, { + ChatState, "chatstate", CHATSTATES, { /// `` Active => "active", @@ -35,6 +33,7 @@ mod tests { use try_from::TryFrom; use minidom::Element; use error::Error; + use ns; #[test] fn test_simple() { diff --git a/src/component.rs b/src/component.rs index 7c74ff1a09219109a8107bdfe5c7c22c28fb3e22..6f86dc3cf78aff243f6095aac81f25293705c6c2 100644 --- a/src/component.rs +++ b/src/component.rs @@ -5,12 +5,10 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use helpers::PlainText; -use ns; - use sha1::Sha1; use digest::Digest; -generate_element_with_text!(Handshake, "handshake", ns::COMPONENT, +generate_element_with_text!(Handshake, "handshake", COMPONENT, data: PlainText> ); diff --git a/src/data_forms.rs b/src/data_forms.rs index 74985edf0421c74184615d7bb248056828477d04..3d8615de8f2fa7e67e119b3d5ecd24ff2b27d1ea 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -23,7 +23,7 @@ impl TryFrom for Option_ { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "option", ns::DATA_FORMS); + check_self!(elem, "option", DATA_FORMS); check_no_unknown_attributes!(elem, "option", ["label"]); let mut value = None; for child in elem.children() { @@ -90,7 +90,7 @@ impl TryFrom for Field { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "field", ns::DATA_FORMS); + check_self!(elem, "field", DATA_FORMS); check_no_unknown_attributes!(elem, "field", ["label", "type", "var"]); let mut field = Field { var: get_attr!(elem, "var", required), @@ -167,7 +167,7 @@ impl TryFrom for DataForm { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "x", ns::DATA_FORMS); + check_self!(elem, "x", DATA_FORMS); check_no_unknown_attributes!(elem, "x", ["type"]); let type_ = get_attr!(elem, "type", required); let mut form = DataForm { diff --git a/src/delay.rs b/src/delay.rs index 84d12ad4be6db0c91fdef951ac4c61144a3ca91d..4786365f800df4c4eabc96deb35e812b01d979fc 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -8,10 +8,9 @@ use date::DateTime; use jid::Jid; -use ns; use helpers::PlainText; -generate_element_with_text!(Delay, "delay", ns::DELAY, +generate_element_with_text!(Delay, "delay", DELAY, [ from: Option = "from" => optional, stamp: DateTime = "stamp" => required diff --git a/src/disco.rs b/src/disco.rs index c48c863c8ce963c6f6bf8d6cb3ebebdd60587b6e..136a50ad27a995f2773e84cddb7ead3c28e7b9d3 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -21,7 +21,7 @@ generate_element_with_only_attributes!( /// /// It should only be used in an ``, as it can only represent /// the request, and not a result. -DiscoInfoQuery, "query", ns::DISCO_INFO, [ +DiscoInfoQuery, "query", DISCO_INFO, [ /// Node on which we are doing the discovery. node: Option = "node" => optional, ]); @@ -29,7 +29,7 @@ DiscoInfoQuery, "query", ns::DISCO_INFO, [ generate_element_with_only_attributes!( /// Structure representing a `` element. #[derive(PartialEq)] -Feature, "feature", ns::DISCO_INFO, [ +Feature, "feature", DISCO_INFO, [ /// Namespace of the feature we want to represent. var: String = "var" => required, ]); @@ -54,7 +54,7 @@ impl TryFrom for Identity { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "identity", ns::DISCO_INFO, "disco#info identity"); + check_self!(elem, "identity", DISCO_INFO, "disco#info identity"); check_no_children!(elem, "disco#info identity"); check_no_unknown_attributes!(elem, "disco#info identity", ["category", "type", "xml:lang", "name"]); @@ -112,7 +112,7 @@ impl TryFrom for DiscoInfoResult { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "query", ns::DISCO_INFO, "disco#info result"); + check_self!(elem, "query", DISCO_INFO, "disco#info result"); check_no_unknown_attributes!(elem, "disco#info result", ["node"]); let mut result = DiscoInfoResult { @@ -185,14 +185,14 @@ generate_element_with_only_attributes!( /// /// It should only be used in an ``, as it can only represent /// the request, and not a result. -DiscoItemsQuery, "query", ns::DISCO_ITEMS, [ +DiscoItemsQuery, "query", DISCO_ITEMS, [ /// Node on which we are doing the discovery. node: Option = "node" => optional, ]); generate_element_with_only_attributes!( /// Structure representing an `` element. -Item, "item", ns::DISCO_ITEMS, [ +Item, "item", DISCO_ITEMS, [ /// JID of the entity pointed by this item. jid: Jid = "jid" => required, /// Node of the entity pointed by this item. @@ -207,14 +207,14 @@ generate_element_with_children!( /// /// It should only be used in an ``, as it can only /// represent the result, and not a request. - DiscoItemsResult, "query", ns::DISCO_ITEMS, + DiscoItemsResult, "query", DISCO_ITEMS, attributes: [ /// Node on which we have done this discovery. node: Option = "node" => optional ], children: [ /// List of items pointed by this entity. - items: Vec = ("item", ns::DISCO_ITEMS) => Item + items: Vec = ("item", DISCO_ITEMS) => Item ] ); diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 587418b88c0dc7831670247c1ecebebe4dad9abd..8fcd0c9e539eb5bb2a7bcb74400dac9a4eb0f515 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -29,7 +29,7 @@ impl TryFrom for ECaps2 { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "c", ns::ECAPS2, "ecaps2"); + check_self!(elem, "c", ECAPS2, "ecaps2"); check_no_attributes!(elem, "ecaps2"); let mut hashes = vec!(); for child in elem.children() { diff --git a/src/eme.rs b/src/eme.rs index de48c3b0aff6d67c42b5bab8ffbe8fe49ae92859..ff3737b06d9b57c80c64fda8bf7c297987ddc8fb 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -4,11 +4,9 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use ns; - generate_element_with_only_attributes!( /// Structure representing an `` element. -ExplicitMessageEncryption, "encryption", ns::EME, [ +ExplicitMessageEncryption, "encryption", EME, [ /// Namespace of the encryption scheme used. namespace: String = "namespace" => required, diff --git a/src/forwarding.rs b/src/forwarding.rs index 8ab4ead2b534699cd1023f96bf4e3d3aa6453cfa..70eead56dc176f238b01af977b70c80b309cb73c 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -26,7 +26,7 @@ impl TryFrom for Forwarded { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "forwarded", ns::FORWARD); + check_self!(elem, "forwarded", FORWARD); let mut delay = None; let mut stanza = None; for child in elem.children() { diff --git a/src/hashes.rs b/src/hashes.rs index 472d4c471063e90fc8c2635613ea2b41e3490084..06249ab288b6d592d2d99fd4ef92a6d2db6ee50b 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -10,7 +10,6 @@ use minidom::IntoAttributeValue; use error::Error; -use ns; use helpers::Base64; use base64; @@ -69,7 +68,7 @@ impl IntoAttributeValue for Algo { generate_element_with_text!( #[derive(PartialEq)] - Hash, "hash", ns::HASHES, + Hash, "hash", HASHES, [ algo: Algo = "algo" => required ], diff --git a/src/ibb.rs b/src/ibb.rs index 60653da069e30ea9a09b358ab72454424dba43ed..9c2cdccc5f3ea96394acd860baa825bd45748a84 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use ns; use helpers::Base64; generate_attribute!(Stanza, "stanza", { @@ -12,13 +11,13 @@ generate_attribute!(Stanza, "stanza", { Message => "message", }, Default = Iq); -generate_element_with_only_attributes!(Open, "open", ns::IBB, [ +generate_element_with_only_attributes!(Open, "open", IBB, [ block_size: u16 = "block-size" => required, sid: String = "sid" => required, stanza: Stanza = "stanza" => default, ]); -generate_element_with_text!(Data, "data", ns::IBB, +generate_element_with_text!(Data, "data", IBB, [ seq: u16 = "seq" => required, sid: String = "sid" => required @@ -26,7 +25,7 @@ generate_element_with_text!(Data, "data", ns::IBB, data: Base64> ); -generate_element_with_only_attributes!(Close, "close", ns::IBB, [ +generate_element_with_only_attributes!(Close, "close", IBB, [ sid: String = "sid" => required, ]); diff --git a/src/ibr.rs b/src/ibr.rs index 892c67d9d3fad57c659d4ca577d7b01f1a4cceff..99cbcd57ba831381180551ab682691ee3f272d06 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -29,7 +29,7 @@ impl TryFrom for Query { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "query", ns::REGISTER, "IBR query"); + check_self!(elem, "query", REGISTER, "IBR query"); let mut query = Query { registered: false, fields: HashMap::new(), diff --git a/src/idle.rs b/src/idle.rs index a94a5ee63c207170f1339b7fa909c557b7d00a96..1312efb96fd60200f5c16f4e587f468c8179bf1c 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -6,9 +6,7 @@ use date::DateTime; -use ns; - -generate_element_with_only_attributes!(Idle, "idle", ns::IDLE, [ +generate_element_with_only_attributes!(Idle, "idle", IDLE, [ since: DateTime = "since" => required, ]); diff --git a/src/iq.rs b/src/iq.rs index 9a7f3b5933f3ecad76e2e0a97f6b2ea04bc432fa..8726a9d5756b1af4f21a57b88c7729dbf825a660 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -207,7 +207,7 @@ impl TryFrom for Iq { type Err = Error; fn try_from(root: Element) -> Result { - check_self!(root, "iq", ns::DEFAULT_NS); + check_self!(root, "iq", DEFAULT_NS); let from = get_attr!(root, "from", optional); let to = get_attr!(root, "to", optional); let id = get_attr!(root, "id", optional); diff --git a/src/jingle.rs b/src/jingle.rs index 1b89dae7ca2d71eb85b3988dd8ae39076668cac5..8bca605474106efd464f3c61d14b016561cb8d77 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -119,7 +119,7 @@ impl TryFrom for Content { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "content", ns::JINGLE); + check_self!(elem, "content", JINGLE); let mut content = Content { creator: get_attr!(elem, "creator", required), @@ -250,7 +250,7 @@ impl TryFrom for ReasonElement { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "reason", ns::JINGLE); + check_self!(elem, "reason", JINGLE); let mut reason = None; let mut text = None; for child in elem.children() { @@ -340,7 +340,7 @@ impl TryFrom for Jingle { type Err = Error; fn try_from(root: Element) -> Result { - check_self!(root, "jingle", ns::JINGLE, "Jingle"); + check_self!(root, "jingle", JINGLE, "Jingle"); let mut jingle = Jingle { action: get_attr!(root, "action", required), diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index e2539f9907c1424fd07f4e35c4bf935977201a91..eb7e9169e0aeb71bfec2eb301b8da270d0f43fe9 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -20,13 +20,13 @@ use ns; generate_element_with_children!( #[derive(PartialEq, Default)] - Range, "range", ns::JINGLE_FT, + Range, "range", JINGLE_FT, attributes: [ offset: u64 = "offset" => default, length: Option = "length" => optional ], children: [ - hashes: Vec = ("hash", ns::HASHES) => Hash + hashes: Vec = ("hash", HASHES) => Hash ] ); @@ -109,7 +109,7 @@ impl TryFrom for File { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "file", ns::JINGLE_FT); + check_self!(elem, "file", JINGLE_FT); check_no_attributes!(elem, "file"); let mut file = File { @@ -218,7 +218,7 @@ impl TryFrom for Description { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "description", ns::JINGLE_FT, "JingleFT description"); + check_self!(elem, "description", JINGLE_FT, "JingleFT description"); check_no_attributes!(elem, "JingleFT description"); let mut file = None; for child in elem.children() { @@ -256,7 +256,7 @@ impl TryFrom for Checksum { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "checksum", ns::JINGLE_FT); + check_self!(elem, "checksum", JINGLE_FT); check_no_unknown_attributes!(elem, "checksum", ["name", "creator"]); let mut file = None; for child in elem.children() { @@ -287,7 +287,7 @@ impl From for Element { } } -generate_element_with_only_attributes!(Received, "received", ns::JINGLE_FT, [ +generate_element_with_only_attributes!(Received, "received", JINGLE_FT, [ name: ContentId = "name" => required, creator: Creator = "creator" => required, ]); diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index d96e9b910b27ef06644a4cd570022b74f674dc9f..2e68e685b0b84e6ce4102621fa05e393198f95c0 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -4,13 +4,11 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use ns; - use ibb::Stanza; generate_id!(StreamId); -generate_element_with_only_attributes!(Transport, "transport", ns::JINGLE_IBB, [ +generate_element_with_only_attributes!(Transport, "transport", JINGLE_IBB, [ block_size: u16 = "block-size" => required, sid: StreamId = "sid" => required, stanza: Stanza = "stanza" => default, diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 4e8ca817b65a129d0064a59cad6f7ed2d6c31842..79949d73005f67d6dc38cbf9fe15d932755f7da9 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -30,7 +30,7 @@ generate_id!(CandidateId); generate_id!(StreamId); -generate_element_with_only_attributes!(Candidate, "candidate", ns::JINGLE_S5B, [ +generate_element_with_only_attributes!(Candidate, "candidate", JINGLE_S5B, [ cid: CandidateId = "cid" => required, host: IpAddr = "host" => required, jid: Jid = "jid" => required, @@ -110,7 +110,7 @@ impl TryFrom for Transport { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "transport", ns::JINGLE_S5B); + check_self!(elem, "transport", JINGLE_S5B); let sid = get_attr!(elem, "sid", required); let dstaddr = get_attr!(elem, "dstaddr", optional); let mode = get_attr!(elem, "mode", default); diff --git a/src/macros.rs b/src/macros.rs index 54297b38d3559149313e2476001942d316ecb68a..26a4b6334d8ededad88284b4d15b1d10e9669584 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -104,10 +104,10 @@ macro_rules! generate_attribute { } macro_rules! generate_element_enum { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+,}) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+,}) => ( generate_element_enum!($(#[$meta])* $elem, $name, $ns, {$($(#[$enum_meta])* $enum => $enum_name),+}); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+}) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+}) => ( $(#[$meta])* #[derive(Debug, Clone, PartialEq)] pub enum $elem { @@ -132,7 +132,7 @@ macro_rules! generate_element_enum { fn from(elem: $elem) -> ::minidom::Element { ::minidom::Element::builder(match elem { $($elem::$enum => $enum_name,)+ - }).ns($ns) + }).ns(::ns::$ns) .build() } } @@ -140,10 +140,10 @@ macro_rules! generate_element_enum { } macro_rules! generate_attribute_enum { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, $attr:tt, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+,}) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, $attr:tt, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+,}) => ( generate_attribute_enum!($(#[$meta])* $elem, $name, $ns, $attr, {$($(#[$enum_meta])* $enum => $enum_name),+}); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, $attr:tt, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+}) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, $attr:tt, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+}) => ( $(#[$meta])* #[derive(Debug, Clone, PartialEq)] pub enum $elem { @@ -167,7 +167,7 @@ macro_rules! generate_attribute_enum { impl From<$elem> for ::minidom::Element { fn from(elem: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) - .ns($ns) + .ns(::ns::$ns) .attr($attr, match elem { $($elem::$enum => $enum_name,)+ }) @@ -178,19 +178,19 @@ macro_rules! generate_attribute_enum { } macro_rules! check_self { - ($elem:ident, $name:tt, $ns:expr) => ( + ($elem:ident, $name:tt, $ns:ident) => ( check_self!($elem, $name, $ns, $name); ); - ($elem:ident, $name:tt, $ns:expr, $pretty_name:tt) => ( - if !$elem.is($name, $ns) { + ($elem:ident, $name:tt, $ns:ident, $pretty_name:tt) => ( + if !$elem.is($name, ::ns::$ns) { return Err(::error::Error::ParseError(concat!("This is not a ", $pretty_name, " element."))); } ); } macro_rules! check_ns_only { - ($elem:ident, $name:tt, $ns:expr) => ( - if !$elem.has_ns($ns) { + ($elem:ident, $name:tt, $ns:ident) => ( + if !$elem.has_ns(::ns::$ns) { return Err(::error::Error::ParseError(concat!("This is not a ", $name, " element."))); } ); @@ -226,7 +226,7 @@ macro_rules! check_no_unknown_attributes { } macro_rules! generate_empty_element { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident) => ( $(#[$meta])* #[derive(Debug, Clone)] pub struct $elem; @@ -245,7 +245,7 @@ macro_rules! generate_empty_element { impl From<$elem> for ::minidom::Element { fn from(_: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) - .ns($ns) + .ns(::ns::$ns) .build() } } @@ -253,10 +253,10 @@ macro_rules! generate_empty_element { } macro_rules! generate_element_with_only_attributes { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => ( generate_element_with_only_attributes!($(#[$meta])* $elem, $name, $ns, [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*]); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => ( $(#[$meta])* #[derive(Debug, Clone)] pub struct $elem { @@ -284,7 +284,7 @@ macro_rules! generate_element_with_only_attributes { impl From<$elem> for ::minidom::Element { fn from(elem: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) - .ns($ns) + .ns(::ns::$ns) $( .attr($attr_name, elem.$attr) )* @@ -314,7 +314,7 @@ macro_rules! generate_id { } macro_rules! generate_elem_id { - ($elem:ident, $name:tt, $ns:expr) => ( + ($elem:ident, $name:tt, $ns:ident) => ( #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $elem(pub String); impl ::std::str::FromStr for $elem { @@ -337,7 +337,7 @@ macro_rules! generate_elem_id { impl From<$elem> for ::minidom::Element { fn from(elem: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) - .ns($ns) + .ns(::ns::$ns) .append(elem.0) .build() } @@ -346,10 +346,10 @@ macro_rules! generate_elem_id { } macro_rules! generate_element_with_text { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, $text_ident:ident: $codec:ident < $text_type:ty >) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, $text_ident:ident: $codec:ident < $text_type:ty >) => ( generate_element_with_text!($(#[$meta])* $elem, $name, $ns, [], $text_ident: $codec<$text_type>); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], $text_ident:ident: $codec:ident < $text_type:ty >) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], $text_ident:ident: $codec:ident < $text_type:ty >) => ( $(#[$meta])* #[derive(Debug, Clone)] pub struct $elem { @@ -379,7 +379,7 @@ macro_rules! generate_element_with_text { impl From<$elem> for ::minidom::Element { fn from(elem: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) - .ns($ns) + .ns(::ns::$ns) $( .attr($attr_name, elem.$attr) )* @@ -391,7 +391,7 @@ macro_rules! generate_element_with_text { } macro_rules! generate_element_with_children { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:expr) => $child_constructor:ident),+]) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( $(#[$meta])* #[derive(Debug, Clone)] pub struct $elem { @@ -414,7 +414,7 @@ macro_rules! generate_element_with_children { let mut parsed_children = vec!(); for child in elem.children() { $( - if child.is($child_name, $child_ns) { + if child.is($child_name, ::ns::$child_ns) { let parsed_child = $child_constructor::try_from(child.clone())?; parsed_children.push(parsed_child); continue; @@ -436,7 +436,7 @@ macro_rules! generate_element_with_children { impl From<$elem> for ::minidom::Element { fn from(elem: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) - .ns($ns) + .ns(::ns::$ns) $( .attr($attr_name, elem.$attr) )* diff --git a/src/mam.rs b/src/mam.rs index 6af739d9dea90ebaadbdc916e79c51f54d752d02..d9f8892902a386b140bbcd4d2b0cebe5dae1ddd9 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -55,7 +55,7 @@ impl TryFrom for Query { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "query", ns::MAM); + check_self!(elem, "query", MAM); check_no_unknown_attributes!(elem, "query", ["queryid", "node"]); let mut form = None; let mut set = None; @@ -78,7 +78,7 @@ impl TryFrom for Result_ { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "result", ns::MAM); + check_self!(elem, "result", MAM); check_no_unknown_attributes!(elem, "result", ["queryid", "id"]); let mut forwarded = None; for child in elem.children() { @@ -103,7 +103,7 @@ impl TryFrom for Fin { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "fin", ns::MAM); + check_self!(elem, "fin", MAM); check_no_unknown_attributes!(elem, "fin", ["complete"]); let mut set = None; for child in elem.children() { @@ -128,7 +128,7 @@ impl TryFrom for Prefs { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "prefs", ns::MAM); + check_self!(elem, "prefs", MAM); check_no_unknown_attributes!(elem, "prefs", ["default"]); let mut always = vec!(); let mut never = vec!(); diff --git a/src/media_element.rs b/src/media_element.rs index c9d6a129c1c3494f2c322914eb31eac151d14528..43f0d7acd101e18f254e8c020a43396fe73911df 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -4,23 +4,22 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use ns; use helpers::TrimmedPlainText; -generate_element_with_text!(URI, "uri", ns::MEDIA_ELEMENT, +generate_element_with_text!(URI, "uri", MEDIA_ELEMENT, [ type_: String = "type" => required ], uri: TrimmedPlainText ); -generate_element_with_children!(MediaElement, "media", ns::MEDIA_ELEMENT, +generate_element_with_children!(MediaElement, "media", MEDIA_ELEMENT, attributes: [ width: Option = "width" => optional, height: Option = "height" => optional ], children: [ - uris: Vec = ("uri", ns::MEDIA_ELEMENT) => URI + uris: Vec = ("uri", MEDIA_ELEMENT) => URI ] ); diff --git a/src/message.rs b/src/message.rs index 72fcbde47663c08b3882abb6d2f76d7b7e7b53a6..2fffdb902bd919eb8432a69015b0de6b09151654 100644 --- a/src/message.rs +++ b/src/message.rs @@ -115,9 +115,9 @@ generate_attribute!(MessageType, "type", { type Lang = String; -generate_elem_id!(Body, "body", ns::DEFAULT_NS); -generate_elem_id!(Subject, "subject", ns::DEFAULT_NS); -generate_elem_id!(Thread, "thread", ns::DEFAULT_NS); +generate_elem_id!(Body, "body", DEFAULT_NS); +generate_elem_id!(Subject, "subject", DEFAULT_NS); +generate_elem_id!(Thread, "thread", DEFAULT_NS); /// The main structure representing the `` stanza. #[derive(Debug, Clone)] @@ -188,7 +188,7 @@ impl TryFrom for Message { type Err = Error; fn try_from(root: Element) -> Result { - check_self!(root, "message", ns::DEFAULT_NS); + check_self!(root, "message", DEFAULT_NS); let from = get_attr!(root, "from", optional); let to = get_attr!(root, "to", optional); let id = get_attr!(root, "id", optional); diff --git a/src/message_correct.rs b/src/message_correct.rs index edd9605f79cbc33b77eab37e971ae5940e4aa8bc..3dbb86e49b5838bbfbf732bf376a8f2b0d772122 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -4,9 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use ns; - -generate_element_with_only_attributes!(Replace, "replace", ns::MESSAGE_CORRECT, [ +generate_element_with_only_attributes!(Replace, "replace", MESSAGE_CORRECT, [ id: String = "id" => required, ]); diff --git a/src/mood.rs b/src/mood.rs index ed8b19b64b6a62155853f5504dbf7d1861abce30..400a91cdf64ce34bb9c7364936d1c263e8defaa6 100644 --- a/src/mood.rs +++ b/src/mood.rs @@ -6,11 +6,9 @@ #![deny(missing_docs)] -use ns; - generate_element_enum!( /// Enum representing all of the possible values of the XEP-0107 moods. - MoodEnum, "mood", ns::MOOD, { + MoodEnum, "mood", MOOD, { /// Impressed with fear or apprehension; in fear; apprehensive. Afraid => "afraid", diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 14a65302322f41ad0a10fce208893ccc63c269b0..4a6fda401064f946dcba19802e8bd63007f25d24 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -22,7 +22,7 @@ impl TryFrom for Muc { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "x", ns::MUC); + check_self!(elem, "x", MUC); check_no_attributes!(elem, "x"); let mut password = None; diff --git a/src/muc/user.rs b/src/muc/user.rs index 33b20d87cd05fac2e6bb1c433d867956662e8370..b1390dd69b572855fae9b2cd705e0e1e9ef998a3 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -17,7 +17,7 @@ use ns; generate_attribute_enum!( /// Lists all of the possible status codes used in MUC presences. -Status, "status", ns::MUC_USER, "code", { +Status, "status", MUC_USER, "code", { /// Inform user that any occupant is allowed to see the user's full JID NonAnonymousRoom => 100, @@ -92,7 +92,7 @@ impl TryFrom for Actor { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "actor", ns::MUC_USER); + check_self!(elem, "actor", MUC_USER); check_no_unknown_attributes!(elem, "actor", ["jid", "nick"]); for _ in elem.children() { return Err(Error::ParseError("Unknown child in actor element.")); @@ -121,11 +121,11 @@ impl From for Element { } } -generate_element_with_only_attributes!(Continue, "continue", ns::MUC_USER, [ +generate_element_with_only_attributes!(Continue, "continue", MUC_USER, [ thread: Option = "thread" => optional, ]); -generate_elem_id!(Reason, "reason", ns::MUC_USER); +generate_elem_id!(Reason, "reason", MUC_USER); generate_attribute!(Affiliation, "affiliation", { Owner => "owner", @@ -157,7 +157,7 @@ impl TryFrom for Item { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "item", ns::MUC_USER); + check_self!(elem, "item", MUC_USER); check_no_unknown_attributes!(elem, "item", ["affiliation", "jid", "nick", "role"]); let mut actor: Option = None; let mut continue_: Option = None; @@ -216,7 +216,7 @@ impl TryFrom for MucUser { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "x", ns::MUC_USER); + check_self!(elem, "x", MUC_USER); check_no_attributes!(elem, "x"); let mut status = vec!(); let mut items = vec!(); diff --git a/src/ping.rs b/src/ping.rs index 12368c10b59a996714590b5234cb5985e00b68fe..8159ffe9384d2cfe5fb2d1af51b324c6dd935b48 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -5,9 +5,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use ns; - -generate_empty_element!(Ping, "ping", ns::PING); +generate_empty_element!(Ping, "ping", PING); #[cfg(test)] mod tests { diff --git a/src/presence.rs b/src/presence.rs index f4a4f11fb810a53147ee262031e156b6b5243c52..ecb587546d2432846c6fdd312867f11eaaa5a0f9 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -248,7 +248,7 @@ impl TryFrom for Presence { type Err = Error; fn try_from(root: Element) -> Result { - check_self!(root, "presence", ns::DEFAULT_NS); + check_self!(root, "presence", DEFAULT_NS); let mut show = None; let mut priority = None; let mut presence = Presence { diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 136489562ef3abf4606b0a25db85915d08cfc086..bd7a9351dc518a464c4b4dc161b4477c27a3d0e6 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -32,7 +32,7 @@ impl TryFrom for Item { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "item", ns::PUBSUB_EVENT); + check_self!(elem, "item", PUBSUB_EVENT); check_no_unknown_attributes!(elem, "item", ["id", "node", "publisher"]); let mut payloads = elem.children().cloned().collect::>(); let payload = payloads.pop(); @@ -141,7 +141,7 @@ impl TryFrom for PubSubEvent { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "event", ns::PUBSUB_EVENT); + check_self!(elem, "event", PUBSUB_EVENT); check_no_attributes!(elem, "event"); let mut payload = None; diff --git a/src/receipts.rs b/src/receipts.rs index 564e8fc9e969c1b49f98dd269f597ce17e3bfdca..dfaf54734a12f012208e5e2b95795ddb5b73410e 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -4,11 +4,9 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use ns; +generate_empty_element!(Request, "request", RECEIPTS); -generate_empty_element!(Request, "request", ns::RECEIPTS); - -generate_element_with_only_attributes!(Received, "received", ns::RECEIPTS, [ +generate_element_with_only_attributes!(Received, "received", RECEIPTS, [ id: Option = "id" => optional, ]); @@ -17,6 +15,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; + use ns; #[test] fn test_simple() { diff --git a/src/roster.rs b/src/roster.rs index 01011fded1ec93fc60b27a68fe5d2af58451b633..2b62f8088c37c989465214484affb378392ac3b6 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -6,9 +6,7 @@ use jid::Jid; -use ns; - -generate_elem_id!(Group, "group", ns::ROSTER); +generate_elem_id!(Group, "group", ROSTER); generate_attribute!(Subscription, "subscription", { None => "none", @@ -21,7 +19,7 @@ generate_attribute!(Subscription, "subscription", { generate_element_with_children!( /// Contact from the user’s contact list. #[derive(PartialEq)] - Item, "item", ns::ROSTER, + Item, "item", ROSTER, attributes: [ /// JID of this contact. jid: Jid = "jid" => required, @@ -35,13 +33,13 @@ generate_element_with_children!( children: [ /// Groups this contact is part of. - groups: Vec = ("group", ns::ROSTER) => Group + groups: Vec = ("group", ROSTER) => Group ] ); generate_element_with_children!( /// The contact list of the user. - Roster, "query", ns::ROSTER, + Roster, "query", ROSTER, attributes: [ /// Version of the contact list. /// @@ -52,7 +50,7 @@ generate_element_with_children!( ], children: [ /// List of the contacts of the user. - items: Vec = ("item", ns::ROSTER) => Item + items: Vec = ("item", ROSTER) => Item ] ); diff --git a/src/rsm.rs b/src/rsm.rs index 6b9f54390f4f83fdbb368676b4dcd3534e8124c8..74484e853854295b7a0ee3c600fe19aeae25b5b7 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -28,7 +28,7 @@ impl TryFrom for Set { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "set", ns::RSM, "RSM set"); + check_self!(elem, "set", RSM, "RSM set"); let mut set = Set { after: None, before: None, diff --git a/src/sasl.rs b/src/sasl.rs index 3cb84c78c1ff05734583c1fb966dfb05ce4ad26b..98b4f93bf2a1a818c6503b2f55fbd6fa26046893 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use ns; use helpers::Base64; generate_attribute!(Mechanism, "mechanism", { @@ -13,22 +12,22 @@ generate_attribute!(Mechanism, "mechanism", { Anonymous => "ANONYMOUS", }); -generate_element_with_text!(Auth, "auth", ns::SASL, +generate_element_with_text!(Auth, "auth", SASL, [ mechanism: Mechanism = "mechanism" => required ], data: Base64> ); -generate_element_with_text!(Challenge, "challenge", ns::SASL, +generate_element_with_text!(Challenge, "challenge", SASL, data: Base64> ); -generate_element_with_text!(Response, "response", ns::SASL, +generate_element_with_text!(Response, "response", SASL, data: Base64> ); -generate_element_with_text!(Success, "success", ns::SASL, +generate_element_with_text!(Success, "success", SASL, data: Base64> ); diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 5d26fa2598eb25eba48fb20a93cd3eb0bb349b6f..f2501498f8588d208b0643165d557528ac1742eb 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -21,7 +21,7 @@ generate_attribute!(ErrorType, "type", { Wait => "wait", }); -generate_element_enum!(DefinedCondition, "condition", ns::XMPP_STANZAS, { +generate_element_enum!(DefinedCondition, "condition", XMPP_STANZAS, { BadRequest => "bad-request", Conflict => "conflict", FeatureNotImplemented => "feature-not-implemented", @@ -61,7 +61,7 @@ impl TryFrom for StanzaError { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "error", ns::DEFAULT_NS); + check_self!(elem, "error", DEFAULT_NS); let type_ = get_attr!(elem, "type", required); let by = get_attr!(elem, "by", optional); diff --git a/src/stanza_id.rs b/src/stanza_id.rs index bad880029dfc6dcf65e11bcfa37d0d6f95708626..b321b46c8c19c4523131d65b37f4222a2113952f 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -6,14 +6,12 @@ use jid::Jid; -use ns; - -generate_element_with_only_attributes!(StanzaId, "stanza-id", ns::SID, [ +generate_element_with_only_attributes!(StanzaId, "stanza-id", SID, [ id: String = "id" => required, by: Jid = "by" => required, ]); -generate_element_with_only_attributes!(OriginId, "origin-id", ns::SID, [ +generate_element_with_only_attributes!(OriginId, "origin-id", SID, [ id: String = "id" => required, ]); diff --git a/src/stream.rs b/src/stream.rs index 9d9eff4625340cc86bcaf7614bc0f67cf7c07297..93a71c41d9a96249e93a19967bd16168f37f330b 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -5,9 +5,8 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use jid::Jid; -use ns; -generate_element_with_only_attributes!(Stream, "stream", ns::STREAM, [ +generate_element_with_only_attributes!(Stream, "stream", STREAM, [ from: Option = "from" => optional, to: Option = "to" => optional, id: Option = "id" => optional, diff --git a/src/version.rs b/src/version.rs index 9521dc990b19267f17eac60b715c4121dd5ce403..e01a40d308b8711690935153838e36763062d395 100644 --- a/src/version.rs +++ b/src/version.rs @@ -20,7 +20,7 @@ impl TryFrom for Version { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "query", ns::VERSION, "version"); + check_self!(elem, "query", VERSION, "version"); check_no_attributes!(elem, "version"); let mut name = None; let mut version = None; diff --git a/src/websocket.rs b/src/websocket.rs index 2402164807977d6100355576c8d680b23036e1e7..b5aa7903457af5d89ff46813471bd8ff1aa0a20a 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -5,9 +5,8 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use jid::Jid; -use ns; -generate_element_with_only_attributes!(Open, "open", ns::WEBSOCKET, [ +generate_element_with_only_attributes!(Open, "open", WEBSOCKET, [ from: Option = "from" => optional, to: Option = "to" => optional, id: Option = "id" => optional, From 3d495ccf416c8b61a3ee26a1af3aaa5f7c5ed75d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 14 May 2018 16:33:47 +0200 Subject: [PATCH 427/698] Use check_no_children!() where it makes sense. --- src/jingle_message.rs | 4 +--- src/message.rs | 12 +++--------- src/muc/user.rs | 4 +--- src/presence.rs | 14 ++++---------- src/pubsub/event.rs | 8 ++------ src/stanza_error.rs | 8 ++------ 6 files changed, 13 insertions(+), 37 deletions(-) diff --git a/src/jingle_message.rs b/src/jingle_message.rs index 159ce597adcbaeb3100d1febc17119c6c454a50a..268d93a4e6072636576a30bf74b293dd1149bbf5 100644 --- a/src/jingle_message.rs +++ b/src/jingle_message.rs @@ -33,9 +33,7 @@ fn get_sid(elem: Element) -> Result { } fn check_empty_and_get_sid(elem: Element) -> Result { - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in Jingle message element.")); - } + check_no_children!(elem, "Jingle message"); get_sid(elem) } diff --git a/src/message.rs b/src/message.rs index 2fffdb902bd919eb8432a69015b0de6b09151654..f103ba8e5dd1bc5147e965064d01d31b689b7fd4 100644 --- a/src/message.rs +++ b/src/message.rs @@ -199,18 +199,14 @@ impl TryFrom for Message { let mut payloads = vec!(); for elem in root.children() { if elem.is("body", ns::DEFAULT_NS) { - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in body element.")); - } + check_no_children!(elem, "body"); let lang = get_attr!(elem, "xml:lang", default); let body = Body(elem.text()); if bodies.insert(lang, body).is_some() { return Err(Error::ParseError("Body element present twice for the same xml:lang.")); } } else if elem.is("subject", ns::DEFAULT_NS) { - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in subject element.")); - } + check_no_children!(elem, "subject"); let lang = get_attr!(elem, "xml:lang", default); let subject = Subject(elem.text()); if subjects.insert(lang, subject).is_some() { @@ -220,9 +216,7 @@ impl TryFrom for Message { if thread.is_some() { return Err(Error::ParseError("Thread element present twice.")); } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in thread element.")); - } + check_no_children!(elem, "thread"); thread = Some(Thread(elem.text())); } else { payloads.push(elem.clone()) diff --git a/src/muc/user.rs b/src/muc/user.rs index b1390dd69b572855fae9b2cd705e0e1e9ef998a3..f53799cdfc7370e9c5147a157a25b9c1d825ba73 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -94,9 +94,7 @@ impl TryFrom for Actor { fn try_from(elem: Element) -> Result { check_self!(elem, "actor", MUC_USER); check_no_unknown_attributes!(elem, "actor", ["jid", "nick"]); - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in actor element.")); - } + check_no_children!(elem, "actor"); let jid: Option = get_attr!(elem, "jid", optional); let nick = get_attr!(elem, "nick", optional); diff --git a/src/presence.rs b/src/presence.rs index ecb587546d2432846c6fdd312867f11eaaa5a0f9..9eba1987595e63590610c0b16dd2a47e26ea8322 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -267,15 +267,11 @@ impl TryFrom for Presence { return Err(Error::ParseError("More than one show element in a presence.")); } check_no_attributes!(elem, "show"); - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in show element.")); - } + check_no_children!(elem, "show"); show = Some(Show::from_str(elem.text().as_ref())?); } else if elem.is("status", ns::DEFAULT_NS) { check_no_unknown_attributes!(elem, "status", ["xml:lang"]); - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in status element.")); - } + check_no_children!(elem, "status"); let lang = get_attr!(elem, "xml:lang", default); if presence.statuses.insert(lang, elem.text()).is_some() { return Err(Error::ParseError("Status element present twice for the same xml:lang.")); @@ -284,10 +280,8 @@ impl TryFrom for Presence { if priority.is_some() { return Err(Error::ParseError("More than one priority element in a presence.")); } - check_no_attributes!(elem, "status"); - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in priority element.")); - } + check_no_attributes!(elem, "priority"); + check_no_children!(elem, "priority"); priority = Some(Priority::from_str(elem.text().as_ref())?); } else { presence.payloads.push(elem.clone()); diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index bd7a9351dc518a464c4b4dc161b4477c27a3d0e6..8ac16cafd3eab5c7ce5b54d311ee8e4a258cb0a9 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -175,14 +175,10 @@ impl TryFrom for PubSubEvent { } else if child.is("items", ns::PUBSUB_EVENT) { payload = Some(parse_items(child.clone(), node)?); } else if child.is("purge", ns::PUBSUB_EVENT) { - for _ in child.children() { - return Err(Error::ParseError("Unknown child in purge element.")); - } + check_no_children!(child, "purge"); payload = Some(PubSubEvent::Purge { node }); } else if child.is("subscription", ns::PUBSUB_EVENT) { - for _ in child.children() { - return Err(Error::ParseError("Unknown child in purge element.")); - } + check_no_children!(child, "subscription"); payload = Some(PubSubEvent::Subscription { node: node, expiry: get_attr!(child, "expiry", optional), diff --git a/src/stanza_error.rs b/src/stanza_error.rs index f2501498f8588d208b0643165d557528ac1742eb..46496c910236a4f6e1b51545e4834f9be039dc6d 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -71,9 +71,7 @@ impl TryFrom for StanzaError { for child in elem.children() { if child.is("text", ns::XMPP_STANZAS) { - for _ in child.children() { - return Err(Error::ParseError("Unknown element in error text.")); - } + check_no_children!(child, "text"); let lang = get_attr!(elem, "xml:lang", default); if texts.insert(lang, child.text()).is_some() { return Err(Error::ParseError("Text element present twice for the same xml:lang.")); @@ -82,9 +80,7 @@ impl TryFrom for StanzaError { if defined_condition.is_some() { return Err(Error::ParseError("Error must not have more than one defined-condition.")); } - for _ in child.children() { - return Err(Error::ParseError("Unknown element in defined-condition.")); - } + check_no_children!(child, "defined-condition"); let condition = DefinedCondition::try_from(child.clone())?; defined_condition = Some(condition); } else { From 90063e5433a3861395ac588026e8222abe79709a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 14 May 2018 17:32:15 +0200 Subject: [PATCH 428/698] mood: Add support for the element. --- src/macros.rs | 5 +++-- src/mood.rs | 12 ++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index 26a4b6334d8ededad88284b4d15b1d10e9669584..af7b7fcf6fdee0d0d715dd3e701fd2ab791f08f2 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -314,7 +314,8 @@ macro_rules! generate_id { } macro_rules! generate_elem_id { - ($elem:ident, $name:tt, $ns:ident) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident) => ( + $(#[$meta])* #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $elem(pub String); impl ::std::str::FromStr for $elem { @@ -391,7 +392,7 @@ macro_rules! generate_element_with_text { } macro_rules! generate_element_with_children { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( $(#[$meta])* #[derive(Debug, Clone)] pub struct $elem { diff --git a/src/mood.rs b/src/mood.rs index 400a91cdf64ce34bb9c7364936d1c263e8defaa6..b8a3ef9078ddaf40c09caf6d8c840ecdaaf16dc2 100644 --- a/src/mood.rs +++ b/src/mood.rs @@ -263,6 +263,11 @@ generate_element_enum!( } ); +generate_elem_id!( + /// Free-form text description of the mood. + Text, "text", MOOD +); + #[cfg(test)] mod tests { use super::*; @@ -275,4 +280,11 @@ mod tests { let mood = MoodEnum::try_from(elem).unwrap(); assert_eq!(mood, MoodEnum::Happy); } + + #[test] + fn test_text() { + let elem: Element = "Yay!".parse().unwrap(); + let text = Text::try_from(elem).unwrap(); + assert_eq!(text.0, String::from("Yay!")); + } } From b37a8da906ff16f56c6a23a93c7c04beab9e13e4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 14 May 2018 17:43:03 +0200 Subject: [PATCH 429/698] Add a User Nickname (XEP-0172) parser and serialiser. --- src/lib.rs | 3 +++ src/nick.rs | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/ns.rs | 3 +++ 3 files changed, 60 insertions(+) create mode 100644 src/nick.rs diff --git a/src/lib.rs b/src/lib.rs index e420e233e6389dcc7a06d6183c5f4703bfb924b6..a0b36c34e4d46b974328e375649777593981f02c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -110,6 +110,9 @@ pub mod caps; /// XEP-0166: Jingle pub mod jingle; +/// XEP-0172: User Nickname +pub mod nick; + /// XEP-0184: Message Delivery Receipts pub mod receipts; diff --git a/src/nick.rs b/src/nick.rs new file mode 100644 index 0000000000000000000000000000000000000000..9844e9318c8670319e3f032e9717c10d9ffbfade --- /dev/null +++ b/src/nick.rs @@ -0,0 +1,54 @@ +// Copyright (c) 2018 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +generate_elem_id!( + /// Represents a global, memorable, friendly or informal name chosen by a user. + Nick, "nick", NICK +); + +#[cfg(test)] +mod tests { + use super::*; + use try_from::TryFrom; + use minidom::Element; + use error::Error; + + #[test] + fn test_simple() { + let elem: Element = "Link Mauve".parse().unwrap(); + let nick = Nick::try_from(elem).unwrap(); + assert_eq!(&nick.0, "Link Mauve"); + } + + #[test] + fn test_serialise() { + let elem1 = Element::from(Nick(String::from("Link Mauve"))); + let elem2: Element = "Link Mauve".parse().unwrap(); + assert_eq!(elem1, elem2); + } + + #[test] + fn test_invalid() { + let elem: Element = "".parse().unwrap(); + let error = Nick::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in nick element."); + } + + #[test] + fn test_invalid_attribute() { + let elem: Element = "".parse().unwrap(); + let error = Nick::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in nick element."); + } +} diff --git a/src/ns.rs b/src/ns.rs index 2227886271a88847628fb82edda6b36faa428163..1eb6e3015f45eb47c31cdc08cb6a26487090b28a 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -74,6 +74,9 @@ pub const CAPS: &str = "http://jabber.org/protocol/caps"; /// XEP-0166: Jingle pub const JINGLE: &str = "urn:xmpp:jingle:1"; +/// XEP-0172: User Nickname +pub const NICK: &str = "http://jabber.org/protocol/nick"; + /// XEP-0184: Message Delivery Receipts pub const RECEIPTS: &str = "urn:xmpp:receipts"; From 688ae145be042c722655c4e9c46bdb8442956afb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 14 May 2018 17:49:25 +0200 Subject: [PATCH 430/698] pubsub: Move common attributes to the module. --- src/pubsub/event.rs | 4 +--- src/pubsub/mod.rs | 4 ++++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 8ac16cafd3eab5c7ce5b54d311ee8e4a258cb0a9..dea11ba446daf99dffad2ede856084326250fc07 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -16,9 +16,7 @@ use ns; use data_forms::DataForm; -generate_id!(NodeName); -generate_id!(ItemId); -generate_id!(SubscriptionId); +use pubsub::{NodeName, ItemId, SubscriptionId}; #[derive(Debug, Clone)] pub struct Item { diff --git a/src/pubsub/mod.rs b/src/pubsub/mod.rs index 9eb939442e863cabae522697569d5da6ac324f00..89b6f8ce951ca89cc8a697e794f1d19841efdf6c 100644 --- a/src/pubsub/mod.rs +++ b/src/pubsub/mod.rs @@ -7,3 +7,7 @@ pub mod event; pub use self::event::PubSubEvent; + +generate_id!(NodeName); +generate_id!(ItemId); +generate_id!(SubscriptionId); From d2dc77e4a3da7670b219beecba8056a380549aa6 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 14 May 2018 21:02:22 +0200 Subject: [PATCH 431/698] pubsub: Move Subscription to the module. --- src/pubsub/event.rs | 9 +-------- src/pubsub/mod.rs | 7 +++++++ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index dea11ba446daf99dffad2ede856084326250fc07..4ac75cf2d4beb01211f8506857c36d6dace05a0b 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -16,7 +16,7 @@ use ns; use data_forms::DataForm; -use pubsub::{NodeName, ItemId, SubscriptionId}; +use pubsub::{NodeName, ItemId, Subscription, SubscriptionId}; #[derive(Debug, Clone)] pub struct Item { @@ -58,13 +58,6 @@ impl From for Element { } } -generate_attribute!(Subscription, "subscription", { - None => "none", - Pending => "pending", - Subscribed => "subscribed", - Unconfigured => "unconfigured", -}, Default = None); - #[derive(Debug, Clone)] pub enum PubSubEvent { /* diff --git a/src/pubsub/mod.rs b/src/pubsub/mod.rs index 89b6f8ce951ca89cc8a697e794f1d19841efdf6c..735fb9c699d05f7851d5f587907993e732d470b6 100644 --- a/src/pubsub/mod.rs +++ b/src/pubsub/mod.rs @@ -11,3 +11,10 @@ pub use self::event::PubSubEvent; generate_id!(NodeName); generate_id!(ItemId); generate_id!(SubscriptionId); + +generate_attribute!(Subscription, "subscription", { + None => "none", + Pending => "pending", + Subscribed => "subscribed", + Unconfigured => "unconfigured", +}, Default = None); From a3e35510cc8244c08e5e73e18e7ed4bc33187f30 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 14 May 2018 21:03:24 +0200 Subject: [PATCH 432/698] macros: Always use the correct std::default::Default trait. --- src/macros.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index af7b7fcf6fdee0d0d715dd3e701fd2ab791f08f2..0a2648d567a36874f10fea060a2b98f0dca50413 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -30,7 +30,7 @@ macro_rules! get_attr { ($elem:ident, $attr:tt, default, $value:ident, $func:expr) => ( match $elem.attr($attr) { Some($value) => $func, - None => Default::default(), + None => ::std::default::Default::default(), } ); } @@ -95,7 +95,7 @@ macro_rules! generate_attribute { })) } } - impl Default for $elem { + impl ::std::default::Default for $elem { fn default() -> $elem { $elem::$default } From fcdfc6d85adf984b88670e06137caf38ccd27b34 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 14 May 2018 21:04:16 +0200 Subject: [PATCH 433/698] Add a PubSub module. --- src/macros.rs | 138 ++++++++++++ src/pubsub/mod.rs | 2 + src/pubsub/pubsub.rs | 490 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 630 insertions(+) create mode 100644 src/pubsub/pubsub.rs diff --git a/src/macros.rs b/src/macros.rs index 0a2648d567a36874f10fea060a2b98f0dca50413..81a7f82d32621cb01037ee1ed57d7c0d391393b2 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -101,6 +101,38 @@ macro_rules! generate_attribute { } } ); + ($elem:ident, $name:tt, bool) => ( + #[derive(Debug, Clone, PartialEq)] + pub enum $elem { + /// True value, represented by either 'true' or '1'. + True, + /// False value, represented by either 'false' or '0'. + False, + } + impl ::std::str::FromStr for $elem { + type Err = ::error::Error; + fn from_str(s: &str) -> Result { + Ok(match s { + "true" | "1" => $elem::True, + "false" | "0" => $elem::False, + _ => return Err(::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + }) + } + } + impl ::minidom::IntoAttributeValue for $elem { + fn into_attribute_value(self) -> Option { + match self { + $elem::True => Some(String::from("true")), + $elem::False => None + } + } + } + impl ::std::default::Default for $elem { + fn default() -> $elem { + $elem::False + } + } + ); } macro_rules! generate_element_enum { @@ -392,6 +424,9 @@ macro_rules! generate_element_with_text { } macro_rules! generate_element_with_children { + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [$($(#[$child_meta])* $child_ident: Vec<$child_type> = ($child_name, $child_ns) => $child_constructor),+]); + ); ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( $(#[$meta])* #[derive(Debug, Clone)] @@ -448,4 +483,107 @@ macro_rules! generate_element_with_children { } } ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, child: ($(#[$child_meta:meta])* $child_ident:ident: Option<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], child: ($(#[$child_meta])* $child_ident: Option<$child_type> = ($child_name, $child_ns) => $child_constructor)); + ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], child: ($(#[$child_meta:meta])* $child_ident:ident: Option<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], child: ($(#[$child_meta])* $child_ident: Option<$child_type> = ($child_name, $child_ns) => $child_constructor)); + ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], child: ($(#[$child_meta:meta])* $child_ident:ident: Option<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( + $(#[$meta])* + #[derive(Debug, Clone)] + pub struct $elem { + $( + $(#[$attr_meta])* + pub $attr: $attr_type, + )* + $(#[$child_meta])* + pub $child_ident: Option<$child_type>, + } + + impl ::try_from::TryFrom<::minidom::Element> for $elem { + type Err = ::error::Error; + + fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { + check_self!(elem, $name, $ns); + check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); + let mut parsed_child = None; + for child in elem.children() { + if child.is($child_name, ::ns::$child_ns) { + parsed_child = Some($child_constructor::try_from(child.clone())?); + continue; + } + return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); + } + Ok($elem { + $( + $attr: get_attr!(elem, $attr_name, $attr_action), + )* + $child_ident: parsed_child, + }) + } + } + + impl From<$elem> for ::minidom::Element { + fn from(elem: $elem) -> ::minidom::Element { + ::minidom::Element::builder($name) + .ns(::ns::$ns) + $( + .attr($attr_name, elem.$attr) + )* + .append(elem.$child_ident) + .build() + } + } + ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, child: ($(#[$child_meta:meta])* $child_ident:ident: $child_type:ty = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], child: ($(#[$child_meta])* $child_ident: $child_type = ($child_name, $child_ns) => $child_constructor)); + ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], child: ($(#[$child_meta:meta])* $child_ident:ident: $child_type:ty = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( + $(#[$meta])* + #[derive(Debug, Clone)] + pub struct $elem { + $( + $(#[$attr_meta])* + pub $attr: $attr_type, + )* + $(#[$child_meta])* + pub $child_ident: $child_type, + } + + impl ::try_from::TryFrom<::minidom::Element> for $elem { + type Err = ::error::Error; + + fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { + check_self!(elem, $name, $ns); + check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); + let mut parsed_child = None; + for child in elem.children() { + if child.is($child_name, ::ns::$child_ns) { + parsed_child = Some($child_constructor::try_from(child.clone())?); + continue; + } + return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); + } + Ok($elem { + $( + $attr: get_attr!(elem, $attr_name, $attr_action), + )* + $child_ident: parsed_child.ok_or(::error::Error::ParseError(concat!("Missing child ", $child_name, " in ", $name, " element.")))?, + }) + } + } + + impl From<$elem> for ::minidom::Element { + fn from(elem: $elem) -> ::minidom::Element { + ::minidom::Element::builder($name) + .ns(::ns::$ns) + $( + .attr($attr_name, elem.$attr) + )* + .append(elem.$child_ident) + .build() + } + } + ); } diff --git a/src/pubsub/mod.rs b/src/pubsub/mod.rs index 735fb9c699d05f7851d5f587907993e732d470b6..e12d0c1aec0cc04e13085c8476f51945a417f6b8 100644 --- a/src/pubsub/mod.rs +++ b/src/pubsub/mod.rs @@ -5,8 +5,10 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. pub mod event; +pub mod pubsub; pub use self::event::PubSubEvent; +pub use self::pubsub::PubSub; generate_id!(NodeName); generate_id!(ItemId); diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs new file mode 100644 index 0000000000000000000000000000000000000000..ce1160d8904654a2d10d1ae703196eebbce2442c --- /dev/null +++ b/src/pubsub/pubsub.rs @@ -0,0 +1,490 @@ +// Copyright (c) 2018 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use try_from::TryFrom; + +use minidom::Element; +use jid::Jid; + +use error::Error; + +use ns; + +use data_forms::DataForm; + +use pubsub::{NodeName, ItemId, Subscription, SubscriptionId}; + +// TODO: a better solution would be to split this into a query and a result elements, like for +// XEP-0030. +generate_element_with_children!( + Affiliations, "affiliations", PUBSUB, + attributes: [ + node: Option = "node" => optional, + ], + children: [ + affiliations: Vec = ("affiliation", PUBSUB) => Affiliation + ] +); + +generate_attribute!( + AffiliationAttribute, "affiliation", { + Member => "member", + None => "none", + Outcast => "outcast", + Owner => "owner", + Publisher => "publisher", + PublishOnly => "publish-only", + } +); + +generate_element_with_only_attributes!( + Affiliation, "affiliation", PUBSUB, [ + node: NodeName = "node" => required, + affiliation: AffiliationAttribute = "affiliation" => required, + ] +); + +generate_element_with_children!( + Configure, "configure", PUBSUB, + child: ( + form: Option = ("x", DATA_FORMS) => DataForm + ) +); + +generate_element_with_only_attributes!( + Create, "create", PUBSUB, [ + node: Option = "node" => optional, + ] +); + +generate_element_with_only_attributes!( + Default, "default", PUBSUB, [ + node: Option = "node" => optional, + // TODO: do we really want to support collection nodes? + // type: String = "type" => optional, + ] +); + +generate_element_with_children!( + Items, "items", PUBSUB, + attributes: [ + // TODO: should be an xs:positiveInteger, that is, an unbounded int ≥ 1. + max_items: Option = "max_items" => optional, + node: NodeName = "node" => required, + subid: Option = "subid" => optional, + ], + children: [ + items: Vec = ("item", PUBSUB) => Item + ] +); + +#[derive(Debug, Clone)] +pub struct Item { + payload: Option, + id: Option, +} + +impl TryFrom for Item { + type Err = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "item", PUBSUB); + check_no_unknown_attributes!(elem, "item", ["id"]); + let mut payloads = elem.children().cloned().collect::>(); + let payload = payloads.pop(); + if !payloads.is_empty() { + return Err(Error::ParseError("More than a single payload in item element.")); + } + Ok(Item { + payload, + id: get_attr!(elem, "id", optional), + }) + } +} + +impl From for Element { + fn from(item: Item) -> Element { + Element::builder("item") + .ns(ns::PUBSUB) + .attr("id", item.id) + .append(item.payload) + .build() + } +} + +generate_element_with_children!( + Options, "options", PUBSUB, + attributes: [ + jid: Jid = "jid" => required, + node: Option = "node" => optional, + subid: Option = "subid" => optional, + ], + child: ( + form: Option = ("x", DATA_FORMS) => DataForm + ) +); + +generate_element_with_children!( + Publish, "publish", PUBSUB, + attributes: [ + node: NodeName = "node" => required, + ], + children: [ + items: Vec = ("item", PUBSUB) => Item + ] +); + +generate_element_with_children!( + PublishOptions, "publish-options", PUBSUB, + child: ( + form: DataForm = ("x", DATA_FORMS) => DataForm + ) +); + +generate_attribute!(Notify, "notify", bool); + +generate_element_with_children!( + Retract, "retract", PUBSUB, + attributes: [ + node: NodeName = "node" => required, + notify: Notify = "notify" => default, + ], + children: [ + items: Vec = ("item", PUBSUB) => Item + ] +); + +#[derive(Debug, Clone)] +pub struct SubscribeOptions { + required: bool, +} + +impl TryFrom for SubscribeOptions { + type Err = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "subscribe-options", PUBSUB); + check_no_attributes!(elem, "subscribe-options"); + let mut required = false; + for child in elem.children() { + if child.is("required", ns::PUBSUB) { + if required { + return Err(Error::ParseError("More than one required element in subscribe-options.")); + } + required = true; + } else { + return Err(Error::ParseError("Unknown child in subscribe-options element.")); + } + } + Ok(SubscribeOptions { required }) + } +} + +impl From for Element { + fn from(subscribe_options: SubscribeOptions) -> Element { + Element::builder("subscribe-options") + .ns(ns::PUBSUB) + .append(if subscribe_options.required { + vec!(Element::builder("required") + .ns(ns::PUBSUB) + .build()) + } else { + vec!() + }) + .build() + } +} + +generate_element_with_only_attributes!( + Subscribe, "subscribe", PUBSUB, [ + jid: Jid = "jid" => required, + node: Option = "node" => optional, + ] +); + +generate_element_with_children!( + Subscriptions, "subscriptions", PUBSUB, + attributes: [ + node: Option = "node" => optional, + ], + children: [ + subscription: Vec = ("subscription", PUBSUB) => SubscriptionElem + ] +); + +generate_element_with_children!( + SubscriptionElem, "subscription", PUBSUB, + attributes: [ + jid: Jid = "jid" => required, + node: Option = "node" => optional, + subid: Option = "subid" => optional, + subscription: Option = "subscription" => optional, + ], + child: ( + subscribe_options: Option = ("subscribe-options", PUBSUB) => SubscribeOptions + ) +); + +generate_element_with_only_attributes!( + Unsubscribe, "unsubscribe", PUBSUB, [ + jid: Jid = "jid" => required, + node: Option = "node" => optional, + subid: Option = "subid" => optional, + ] +); + +#[derive(Debug, Clone)] +pub enum PubSub { + Create { + create: Create, + configure: Option + }, + Publish { + publish: Publish, + publish_options: Option + }, + Affiliations(Affiliations), + Default(Default), + Items(Items), + Retract(Retract), + Subscription(SubscriptionElem), + Subscriptions(Subscriptions), + Unsubscribe(Unsubscribe), +} + +impl TryFrom for PubSub { + type Err = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "pubsub", PUBSUB); + check_no_attributes!(elem, "pubsub"); + + let mut payload = None; + for child in elem.children() { + if child.is("create", ns::PUBSUB) { + if payload.is_some() { + return Err(Error::ParseError("…")); + } + let create = Create::try_from(child.clone())?; + payload = Some(PubSub::Create { create, configure: None }); + } else { + return Err(Error::ParseError("Unknown child in pubsub element.")); + } + } + Ok(payload.ok_or(Error::ParseError("No payload in pubsub element."))?) + } +} + +impl From for Element { + fn from(pubsub: PubSub) -> Element { + Element::builder("pubsub") + .ns(ns::PUBSUB) + .append(match pubsub { + PubSub::Create { create, configure } => { + let mut elems = vec!(Element::from(create)); + if let Some(configure) = configure { + elems.push(Element::from(configure)); + } + elems + }, + PubSub::Publish { publish, publish_options } => { + let mut elems = vec!(Element::from(publish)); + if let Some(publish_options) = publish_options { + elems.push(Element::from(publish_options)); + } + elems + }, + PubSub::Affiliations(affiliations) => vec!(Element::from(affiliations)), + PubSub::Default(default) => vec!(Element::from(default)), + PubSub::Items(items) => vec!(Element::from(items)), + PubSub::Retract(retract) => vec!(Element::from(retract)), + PubSub::Subscription(subscription) => vec!(Element::from(subscription)), + PubSub::Subscriptions(subscriptions) => vec!(Element::from(subscriptions)), + PubSub::Unsubscribe(unsubscribe) => vec!(Element::from(unsubscribe)), + }) + .build() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use compare_elements::NamespaceAwareCompare; + + #[test] + fn invalid_empty_pubsub() { + let elem: Element = "".parse().unwrap(); + let error = PubSub::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "No payload in pubsub element."); + /* + match pubsub { + PubSub::EmptyItems { node } => assert_eq!(node, NodeName(String::from("coucou"))), + _ => panic!(), + } + */ + } + + #[test] + fn publish_option() { + let elem: Element = "http://jabber.org/protocol/pubsub#publish-options".parse().unwrap(); + let publish_options = PublishOptions::try_from(elem).unwrap(); + assert_eq!(&publish_options.form.form_type.unwrap(), "http://jabber.org/protocol/pubsub#publish-options"); + } + + #[test] + fn subscribe_options() { + let elem1: Element = "".parse().unwrap(); + let subscribe_options1 = SubscribeOptions::try_from(elem1).unwrap(); + assert_eq!(subscribe_options1.required, false); + + let elem2: Element = "".parse().unwrap(); + let subscribe_options2 = SubscribeOptions::try_from(elem2).unwrap(); + assert_eq!(subscribe_options2.required, true); + } + + /* + #[test] + fn test_simple_items() { + let elem: Element = "".parse().unwrap(); + let event = PubSub::try_from(elem).unwrap(); + match event { + PubSub::PublishedItems { node, items } => { + assert_eq!(node, NodeName(String::from("coucou"))); + assert_eq!(items[0].id, Some(ItemId(String::from("test")))); + assert_eq!(items[0].node, Some(NodeName(String::from("huh?")))); + assert_eq!(items[0].publisher, Some(Jid::from_str("test@coucou").unwrap())); + assert_eq!(items[0].payload, None); + }, + _ => panic!(), + } + } + + #[test] + fn test_simple_pep() { + let elem: Element = "".parse().unwrap(); + let event = PubSub::try_from(elem).unwrap(); + match event { + PubSub::PublishedItems { node, items } => { + assert_eq!(node, NodeName(String::from("something"))); + assert_eq!(items[0].id, None); + assert_eq!(items[0].node, None); + assert_eq!(items[0].publisher, None); + match items[0].payload { + Some(ref elem) => assert!(elem.is("foreign", "example:namespace")), + _ => panic!(), + } + }, + _ => panic!(), + } + } + + #[test] + fn test_simple_retract() { + let elem: Element = "".parse().unwrap(); + let event = PubSub::try_from(elem).unwrap(); + match event { + PubSub::RetractedItems { node, items } => { + assert_eq!(node, NodeName(String::from("something"))); + assert_eq!(items[0], ItemId(String::from("coucou"))); + assert_eq!(items[1], ItemId(String::from("test"))); + }, + _ => panic!(), + } + } + + #[test] + fn test_simple_delete() { + let elem: Element = "".parse().unwrap(); + let event = PubSub::try_from(elem).unwrap(); + match event { + PubSub::Delete { node, redirect } => { + assert_eq!(node, NodeName(String::from("coucou"))); + assert_eq!(redirect, Some(String::from("hello"))); + }, + _ => panic!(), + } + } + + #[test] + fn test_simple_purge() { + let elem: Element = "".parse().unwrap(); + let event = PubSub::try_from(elem).unwrap(); + match event { + PubSub::Purge { node } => { + assert_eq!(node, NodeName(String::from("coucou"))); + }, + _ => panic!(), + } + } + + #[test] + fn test_simple_configure() { + let elem: Element = "http://jabber.org/protocol/pubsub#node_config".parse().unwrap(); + let event = PubSub::try_from(elem).unwrap(); + match event { + PubSub::Configuration { node, form: _ } => { + assert_eq!(node, NodeName(String::from("coucou"))); + //assert_eq!(form.type_, Result_); + }, + _ => panic!(), + } + } + + #[test] + fn test_invalid() { + let elem: Element = "".parse().unwrap(); + let error = PubSub::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in event element."); + } + + #[test] + fn test_invalid_attribute() { + let elem: Element = "".parse().unwrap(); + let error = PubSub::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in event element."); + } + + #[test] + fn test_ex221_subscription() { + let elem: Element = r#" + + + +"#.parse().unwrap(); + let event = PubSub::try_from(elem.clone()).unwrap(); + match event.clone() { + PubSub::Subscription { node, expiry, jid, subid, subscription } => { + assert_eq!(node, NodeName(String::from("princely_musings"))); + assert_eq!(subid, Some(SubscriptionId(String::from("ba49252aaa4f5d320c24d3766f0bdcade78c78d3")))); + assert_eq!(subscription, Some(Subscription::Subscribed)); + assert_eq!(jid, Some(Jid::from_str("francisco@denmark.lit").unwrap())); + assert_eq!(expiry, Some("2006-02-28T23:59:59Z".parse().unwrap())); + }, + _ => panic!(), + } + + let elem2: Element = event.into(); + assert!(elem.compare_to(&elem2)); + } + */ +} From 58a1d80b77cb2b1ed70575f700a1e788d1b4a964 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 14 May 2018 21:32:58 +0200 Subject: [PATCH 434/698] pubsub: Add forgotten toplevel parsing. --- src/pubsub/pubsub.rs | 275 ++++++++++++++++++++++--------------------- 1 file changed, 140 insertions(+), 135 deletions(-) diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index ce1160d8904654a2d10d1ae703196eebbce2442c..5ca62e9a4fb945e262a5d0ffda814fbaf2050be9 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -140,7 +140,7 @@ generate_element_with_children!( generate_element_with_children!( PublishOptions, "publish-options", PUBSUB, child: ( - form: DataForm = ("x", DATA_FORMS) => DataForm + form: Option = ("x", DATA_FORMS) => DataForm ) ); @@ -266,10 +266,78 @@ impl TryFrom for PubSub { for child in elem.children() { if child.is("create", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError("…")); + return Err(Error::ParseError("Payload is already defined in pubsub element.")); } let create = Create::try_from(child.clone())?; payload = Some(PubSub::Create { create, configure: None }); + } else if child.is("configure", ns::PUBSUB) { + if let Some(PubSub::Create { create, configure }) = payload { + if configure.is_some() { + return Err(Error::ParseError("Configure is already defined in pubsub element.")); + } + let configure = Some(Configure::try_from(child.clone())?); + payload = Some(PubSub::Create { create, configure }); + } else { + return Err(Error::ParseError("Payload is already defined in pubsub element.")); + } + } else if child.is("publish", ns::PUBSUB) { + if payload.is_some() { + return Err(Error::ParseError("Payload is already defined in pubsub element.")); + } + let publish = Publish::try_from(child.clone())?; + payload = Some(PubSub::Publish { publish, publish_options: None }); + } else if child.is("publish-options", ns::PUBSUB) { + if let Some(PubSub::Publish { publish, publish_options }) = payload { + if publish_options.is_some() { + return Err(Error::ParseError("Publish-options are already defined in pubsub element.")); + } + let publish_options = Some(PublishOptions::try_from(child.clone())?); + payload = Some(PubSub::Publish { publish, publish_options }); + } else { + return Err(Error::ParseError("Payload is already defined in pubsub element.")); + } + } else if child.is("affiliations", ns::PUBSUB) { + if payload.is_some() { + return Err(Error::ParseError("Payload is already defined in pubsub element.")); + } + let affiliations = Affiliations::try_from(child.clone())?; + payload = Some(PubSub::Affiliations(affiliations)); + } else if child.is("default", ns::PUBSUB) { + if payload.is_some() { + return Err(Error::ParseError("Payload is already defined in pubsub element.")); + } + let default = Default::try_from(child.clone())?; + payload = Some(PubSub::Default(default)); + } else if child.is("items", ns::PUBSUB) { + if payload.is_some() { + return Err(Error::ParseError("Payload is already defined in pubsub element.")); + } + let items = Items::try_from(child.clone())?; + payload = Some(PubSub::Items(items)); + } else if child.is("retract", ns::PUBSUB) { + if payload.is_some() { + return Err(Error::ParseError("Payload is already defined in pubsub element.")); + } + let retract = Retract::try_from(child.clone())?; + payload = Some(PubSub::Retract(retract)); + } else if child.is("subscription", ns::PUBSUB) { + if payload.is_some() { + return Err(Error::ParseError("Payload is already defined in pubsub element.")); + } + let subscription = SubscriptionElem::try_from(child.clone())?; + payload = Some(PubSub::Subscription(subscription)); + } else if child.is("subscriptions", ns::PUBSUB) { + if payload.is_some() { + return Err(Error::ParseError("Payload is already defined in pubsub element.")); + } + let subscriptions = Subscriptions::try_from(child.clone())?; + payload = Some(PubSub::Subscriptions(subscriptions)); + } else if child.is("unsubscribe", ns::PUBSUB) { + if payload.is_some() { + return Err(Error::ParseError("Payload is already defined in pubsub element.")); + } + let unsubscribe = Unsubscribe::try_from(child.clone())?; + payload = Some(PubSub::Unsubscribe(unsubscribe)); } else { return Err(Error::ParseError("Unknown child in pubsub element.")); } @@ -315,176 +383,113 @@ mod tests { use compare_elements::NamespaceAwareCompare; #[test] - fn invalid_empty_pubsub() { - let elem: Element = "".parse().unwrap(); - let error = PubSub::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "No payload in pubsub element."); - /* - match pubsub { - PubSub::EmptyItems { node } => assert_eq!(node, NodeName(String::from("coucou"))), + fn create() { + let elem: Element = "".parse().unwrap(); + let elem1 = elem.clone(); + let pubsub = PubSub::try_from(elem).unwrap(); + match pubsub.clone() { + PubSub::Create { create, configure } => { + assert!(create.node.is_none()); + assert!(configure.is_none()); + } _ => panic!(), } - */ - } - - #[test] - fn publish_option() { - let elem: Element = "http://jabber.org/protocol/pubsub#publish-options".parse().unwrap(); - let publish_options = PublishOptions::try_from(elem).unwrap(); - assert_eq!(&publish_options.form.form_type.unwrap(), "http://jabber.org/protocol/pubsub#publish-options"); - } - #[test] - fn subscribe_options() { - let elem1: Element = "".parse().unwrap(); - let subscribe_options1 = SubscribeOptions::try_from(elem1).unwrap(); - assert_eq!(subscribe_options1.required, false); + let elem2 = Element::from(pubsub); + assert!(elem1.compare_to(&elem2)); - let elem2: Element = "".parse().unwrap(); - let subscribe_options2 = SubscribeOptions::try_from(elem2).unwrap(); - assert_eq!(subscribe_options2.required, true); - } - - /* - #[test] - fn test_simple_items() { - let elem: Element = "".parse().unwrap(); - let event = PubSub::try_from(elem).unwrap(); - match event { - PubSub::PublishedItems { node, items } => { - assert_eq!(node, NodeName(String::from("coucou"))); - assert_eq!(items[0].id, Some(ItemId(String::from("test")))); - assert_eq!(items[0].node, Some(NodeName(String::from("huh?")))); - assert_eq!(items[0].publisher, Some(Jid::from_str("test@coucou").unwrap())); - assert_eq!(items[0].payload, None); - }, + let elem: Element = "".parse().unwrap(); + let elem1 = elem.clone(); + let pubsub = PubSub::try_from(elem).unwrap(); + match pubsub.clone() { + PubSub::Create { create, configure } => { + assert_eq!(&create.node.unwrap().0, "coucou"); + assert!(configure.is_none()); + } _ => panic!(), } - } - #[test] - fn test_simple_pep() { - let elem: Element = "".parse().unwrap(); - let event = PubSub::try_from(elem).unwrap(); - match event { - PubSub::PublishedItems { node, items } => { - assert_eq!(node, NodeName(String::from("something"))); - assert_eq!(items[0].id, None); - assert_eq!(items[0].node, None); - assert_eq!(items[0].publisher, None); - match items[0].payload { - Some(ref elem) => assert!(elem.is("foreign", "example:namespace")), - _ => panic!(), - } - }, - _ => panic!(), - } + let elem2 = Element::from(pubsub); + assert!(elem1.compare_to(&elem2)); } #[test] - fn test_simple_retract() { - let elem: Element = "".parse().unwrap(); - let event = PubSub::try_from(elem).unwrap(); - match event { - PubSub::RetractedItems { node, items } => { - assert_eq!(node, NodeName(String::from("something"))); - assert_eq!(items[0], ItemId(String::from("coucou"))); - assert_eq!(items[1], ItemId(String::from("test"))); - }, + fn create_and_configure() { + let elem: Element = "".parse().unwrap(); + let elem1 = elem.clone(); + let pubsub = PubSub::try_from(elem).unwrap(); + match pubsub.clone() { + PubSub::Create { create, configure } => { + assert!(create.node.is_none()); + assert!(configure.unwrap().form.is_none()); + } _ => panic!(), } - } - #[test] - fn test_simple_delete() { - let elem: Element = "".parse().unwrap(); - let event = PubSub::try_from(elem).unwrap(); - match event { - PubSub::Delete { node, redirect } => { - assert_eq!(node, NodeName(String::from("coucou"))); - assert_eq!(redirect, Some(String::from("hello"))); - }, - _ => panic!(), - } + let elem2 = Element::from(pubsub); + assert!(elem1.compare_to(&elem2)); } #[test] - fn test_simple_purge() { - let elem: Element = "".parse().unwrap(); - let event = PubSub::try_from(elem).unwrap(); - match event { - PubSub::Purge { node } => { - assert_eq!(node, NodeName(String::from("coucou"))); - }, + fn publish() { + let elem: Element = "".parse().unwrap(); + let elem1 = elem.clone(); + let pubsub = PubSub::try_from(elem).unwrap(); + match pubsub.clone() { + PubSub::Publish { publish, publish_options } => { + assert_eq!(&publish.node.0, "coucou"); + assert!(publish_options.is_none()); + } _ => panic!(), } + + let elem2 = Element::from(pubsub); + assert!(elem1.compare_to(&elem2)); } #[test] - fn test_simple_configure() { - let elem: Element = "http://jabber.org/protocol/pubsub#node_config".parse().unwrap(); - let event = PubSub::try_from(elem).unwrap(); - match event { - PubSub::Configuration { node, form: _ } => { - assert_eq!(node, NodeName(String::from("coucou"))); - //assert_eq!(form.type_, Result_); - }, + fn publish_with_publish_options() { + let elem: Element = "".parse().unwrap(); + let elem1 = elem.clone(); + let pubsub = PubSub::try_from(elem).unwrap(); + match pubsub.clone() { + PubSub::Publish { publish, publish_options } => { + assert_eq!(&publish.node.0, "coucou"); + assert!(publish_options.unwrap().form.is_none()); + } _ => panic!(), } + + let elem2 = Element::from(pubsub); + assert!(elem1.compare_to(&elem2)); } #[test] - fn test_invalid() { - let elem: Element = "".parse().unwrap(); + fn invalid_empty_pubsub() { + let elem: Element = "".parse().unwrap(); let error = PubSub::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown child in event element."); + assert_eq!(message, "No payload in pubsub element."); } #[test] - fn test_invalid_attribute() { - let elem: Element = "".parse().unwrap(); - let error = PubSub::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown attribute in event element."); + fn publish_option() { + let elem: Element = "http://jabber.org/protocol/pubsub#publish-options".parse().unwrap(); + let publish_options = PublishOptions::try_from(elem).unwrap(); + assert_eq!(&publish_options.form.unwrap().form_type.unwrap(), "http://jabber.org/protocol/pubsub#publish-options"); } #[test] - fn test_ex221_subscription() { - let elem: Element = r#" - - - -"#.parse().unwrap(); - let event = PubSub::try_from(elem.clone()).unwrap(); - match event.clone() { - PubSub::Subscription { node, expiry, jid, subid, subscription } => { - assert_eq!(node, NodeName(String::from("princely_musings"))); - assert_eq!(subid, Some(SubscriptionId(String::from("ba49252aaa4f5d320c24d3766f0bdcade78c78d3")))); - assert_eq!(subscription, Some(Subscription::Subscribed)); - assert_eq!(jid, Some(Jid::from_str("francisco@denmark.lit").unwrap())); - assert_eq!(expiry, Some("2006-02-28T23:59:59Z".parse().unwrap())); - }, - _ => panic!(), - } + fn subscribe_options() { + let elem1: Element = "".parse().unwrap(); + let subscribe_options1 = SubscribeOptions::try_from(elem1).unwrap(); + assert_eq!(subscribe_options1.required, false); - let elem2: Element = event.into(); - assert!(elem.compare_to(&elem2)); + let elem2: Element = "".parse().unwrap(); + let subscribe_options2 = SubscribeOptions::try_from(elem2).unwrap(); + assert_eq!(subscribe_options2.required, true); } - */ } From 71dc5ad6c80f059d4d7a002e1a904170b80bbfeb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 15 May 2018 00:18:15 +0200 Subject: [PATCH 435/698] pubsub: Document the new structs and their fields. --- src/macros.rs | 23 ++++----- src/pubsub/mod.rs | 3 ++ src/pubsub/pubsub.rs | 115 +++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 127 insertions(+), 14 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index 81a7f82d32621cb01037ee1ed57d7c0d391393b2..20dff441d3c4cb90dbc2904eac674029c2d19a55 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -36,18 +36,18 @@ macro_rules! get_attr { } macro_rules! generate_attribute { - ($elem:ident, $name:tt, {$($a:ident => $b:tt),+,}) => ( - generate_attribute!($elem, $name, {$($a => $b),+}); + ($(#[$meta:meta])* $elem:ident, $name:tt, {$($(#[$a_meta:meta])* $a:ident => $b:tt),+,}) => ( + generate_attribute!($(#[$meta])* $elem, $name, {$($(#[$a_meta])* $a => $b),+}); ); - ($elem:ident, $name:tt, {$($a:ident => $b:tt),+,}, Default = $default:ident) => ( - generate_attribute!($elem, $name, {$($a => $b),+}, Default = $default); + ($(#[$meta:meta])* $elem:ident, $name:tt, {$($(#[$a_meta:meta])* $a:ident => $b:tt),+,}, Default = $default:ident) => ( + generate_attribute!($(#[$meta])* $elem, $name, {$($(#[$a_meta])* $a => $b),+}, Default = $default); ); - ($elem:ident, $name:tt, {$($a:ident => $b:tt),+}) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, {$($(#[$a_meta:meta])* $a:ident => $b:tt),+}) => ( + $(#[$meta])* #[derive(Debug, Clone, PartialEq)] pub enum $elem { $( - #[doc=$b] - #[doc="value for this attribute."] + $(#[$a_meta])* $a ),+ } @@ -68,12 +68,12 @@ macro_rules! generate_attribute { } } ); - ($elem:ident, $name:tt, {$($a:ident => $b:tt),+}, Default = $default:ident) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, {$($(#[$a_meta:meta])* $a:ident => $b:tt),+}, Default = $default:ident) => ( + $(#[$meta])* #[derive(Debug, Clone, PartialEq)] pub enum $elem { $( - #[doc=$b] - #[doc="value for this attribute."] + $(#[$a_meta])* $a ),+ } @@ -101,7 +101,8 @@ macro_rules! generate_attribute { } } ); - ($elem:ident, $name:tt, bool) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, bool) => ( + $(#[$meta])* #[derive(Debug, Clone, PartialEq)] pub enum $elem { /// True value, represented by either 'true' or '1'. diff --git a/src/pubsub/mod.rs b/src/pubsub/mod.rs index e12d0c1aec0cc04e13085c8476f51945a417f6b8..badf672996da99d58f915b7975f366e29b8f37b8 100644 --- a/src/pubsub/mod.rs +++ b/src/pubsub/mod.rs @@ -4,7 +4,10 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +/// The `http://jabber.org/protocol/pubsub#event` protocol. pub mod event; + +/// The `http://jabber.org/protocol/pubsub` protocol. pub mod pubsub; pub use self::event::PubSubEvent; diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index 5ca62e9a4fb945e262a5d0ffda814fbaf2050be9..d90ca9826ba9831ee20155a05e16780aaee05b35 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#![deny(missing_docs)] + use try_from::TryFrom; use minidom::Element; @@ -20,71 +22,108 @@ use pubsub::{NodeName, ItemId, Subscription, SubscriptionId}; // TODO: a better solution would be to split this into a query and a result elements, like for // XEP-0030. generate_element_with_children!( + /// A list of affiliations you have on a service, or on a node. Affiliations, "affiliations", PUBSUB, attributes: [ + /// The optional node name this request pertains to. node: Option = "node" => optional, ], children: [ + /// The actual list of affiliation elements. affiliations: Vec = ("affiliation", PUBSUB) => Affiliation ] ); generate_attribute!( + /// A list of possible affiliations to a node. AffiliationAttribute, "affiliation", { + /// You are a member of this node, you can subscribe and retrieve items. Member => "member", + + /// You don’t have a specific affiliation with this node, you can only subscribe to it. None => "none", + + /// You are banned from this node. Outcast => "outcast", + + /// You are an owner of this node, and can do anything with it. Owner => "owner", + + /// You are a publisher on this node, you can publish and retract items to it. Publisher => "publisher", + + /// You can publish and retract items on this node, but not subscribe or retrive items. PublishOnly => "publish-only", } ); generate_element_with_only_attributes!( + /// An affiliation element. Affiliation, "affiliation", PUBSUB, [ + /// The node this affiliation pertains to. node: NodeName = "node" => required, + + /// The affiliation you currently have on this node. affiliation: AffiliationAttribute = "affiliation" => required, ] ); generate_element_with_children!( + /// Request to configure a new node. Configure, "configure", PUBSUB, child: ( + /// The form to configure it. form: Option = ("x", DATA_FORMS) => DataForm ) ); generate_element_with_only_attributes!( + /// Request to create a new node. Create, "create", PUBSUB, [ + /// The node name to create, if `None` the service will generate one. node: Option = "node" => optional, ] ); generate_element_with_only_attributes!( + /// Request for a default node configuration. Default, "default", PUBSUB, [ + /// The node targetted by this request, otherwise the entire service. node: Option = "node" => optional, + // TODO: do we really want to support collection nodes? // type: String = "type" => optional, ] ); generate_element_with_children!( + /// A request for a list of items. Items, "items", PUBSUB, attributes: [ // TODO: should be an xs:positiveInteger, that is, an unbounded int ≥ 1. + /// Maximum number of items returned. max_items: Option = "max_items" => optional, + + /// The node queried by this request. node: NodeName = "node" => required, + + /// The subscription identifier related to this request. subid: Option = "subid" => optional, ], children: [ + /// The actual list of items returned. items: Vec = ("item", PUBSUB) => Item ] ); +/// An item from a PubSub node. #[derive(Debug, Clone)] pub struct Item { - payload: Option, - id: Option, + /// The payload of this item, in an arbitrary namespace. + pub payload: Option, + + /// The 'id' attribute of this item. + pub id: Option, } impl TryFrom for Item { @@ -116,49 +155,71 @@ impl From for Element { } generate_element_with_children!( + /// The options associated to a subscription request. Options, "options", PUBSUB, attributes: [ + /// The JID affected by this request. jid: Jid = "jid" => required, + + /// The node affected by this request. node: Option = "node" => optional, + + /// The subscription identifier affected by this request. subid: Option = "subid" => optional, ], child: ( + /// The form describing the subscription. form: Option = ("x", DATA_FORMS) => DataForm ) ); generate_element_with_children!( + /// Request to publish items to a node. Publish, "publish", PUBSUB, attributes: [ + /// The target node for this operation. node: NodeName = "node" => required, ], children: [ + /// The items you want to publish. items: Vec = ("item", PUBSUB) => Item ] ); generate_element_with_children!( + /// The options associated to a publish request. PublishOptions, "publish-options", PUBSUB, child: ( + /// The form describing these options. form: Option = ("x", DATA_FORMS) => DataForm ) ); -generate_attribute!(Notify, "notify", bool); +generate_attribute!( + /// Whether a retract request should notify subscribers or not. + Notify, "notify", bool +); generate_element_with_children!( + /// A request to retract some items from a node. Retract, "retract", PUBSUB, attributes: [ + /// The node affected by this request. node: NodeName = "node" => required, + + /// Whether a retract request should notify subscribers or not. notify: Notify = "notify" => default, ], children: [ + /// The items affected by this request. items: Vec = ("item", PUBSUB) => Item ] ); +/// Indicate that the subscription can be configured. #[derive(Debug, Clone)] pub struct SubscribeOptions { + /// If `true`, the configuration is actually required. required: bool, } @@ -199,59 +260,107 @@ impl From for Element { } generate_element_with_only_attributes!( + /// A request to subscribe a JID to a node. Subscribe, "subscribe", PUBSUB, [ + /// The JID being subscribed. jid: Jid = "jid" => required, + + /// The node to subscribe to. node: Option = "node" => optional, ] ); generate_element_with_children!( + /// A request for current subscriptions. Subscriptions, "subscriptions", PUBSUB, attributes: [ + /// The node to query. node: Option = "node" => optional, ], children: [ + /// The list of subscription elements returned. subscription: Vec = ("subscription", PUBSUB) => SubscriptionElem ] ); generate_element_with_children!( + /// A subscription element, describing the state of a subscription. SubscriptionElem, "subscription", PUBSUB, attributes: [ + /// The JID affected by this subscription. jid: Jid = "jid" => required, + + /// The node affected by this subscription. node: Option = "node" => optional, + + /// The subscription identifier for this subscription. subid: Option = "subid" => optional, + + /// The state of the subscription. subscription: Option = "subscription" => optional, ], child: ( + /// The options related to this subscription. subscribe_options: Option = ("subscribe-options", PUBSUB) => SubscribeOptions ) ); generate_element_with_only_attributes!( + /// An unsubscribe request. Unsubscribe, "unsubscribe", PUBSUB, [ + /// The JID affected by this request. jid: Jid = "jid" => required, + + /// The node affected by this request. node: Option = "node" => optional, + + /// The subscription identifier for this subscription. subid: Option = "subid" => optional, ] ); +/// Main payload used to communicate with a PubSub service. +/// +/// `` #[derive(Debug, Clone)] pub enum PubSub { + /// Request to create a new node, with optional suggested name and suggested configuration. Create { + /// The create request. create: Create, + + /// The configure request for the new node. configure: Option }, + + /// Request to publish items to a node, with optional options. Publish { + /// The publish request. publish: Publish, + + /// The options related to this publish request. publish_options: Option }, + + /// A list of affiliations you have on a service, or on a node. Affiliations(Affiliations), + + /// Request for a default node configuration. Default(Default), + + /// A request for a list of items. Items(Items), + + /// A request to retract some items from a node. Retract(Retract), + + /// A request about a subscription. Subscription(SubscriptionElem), + + /// A request for current subscriptions. Subscriptions(Subscriptions), + + /// An unsubscribe request. Unsubscribe(Unsubscribe), } From f52c28fb35057bc9e8c4eb7570a4dcccda9c1b54 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 15 May 2018 01:47:12 +0200 Subject: [PATCH 436/698] jingle, jingle_s5b: Add missing checks for unknown attributes and children. --- src/jingle.rs | 4 ++++ src/jingle_s5b.rs | 1 + 2 files changed, 5 insertions(+) diff --git a/src/jingle.rs b/src/jingle.rs index 8bca605474106efd464f3c61d14b016561cb8d77..8d7d309ece38655ac1c90fd2a3dfd5a9a2a183c1 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -120,6 +120,7 @@ impl TryFrom for Content { fn try_from(elem: Element) -> Result { check_self!(elem, "content", JINGLE); + check_no_unknown_attributes!(elem, "content", ["creator", "disposition", "name", "senders"]); let mut content = Content { creator: get_attr!(elem, "creator", required), @@ -146,6 +147,8 @@ impl TryFrom for Content { return Err(Error::ParseError("Content must not have more than one security.")); } content.security = Some(child.clone()); + } else { + return Err(Error::ParseError("Unknown child in content element.")); } } Ok(content) @@ -341,6 +344,7 @@ impl TryFrom for Jingle { fn try_from(root: Element) -> Result { check_self!(root, "jingle", JINGLE, "Jingle"); + check_no_unknown_attributes!(root, "Jingle", ["action", "initiator", "responder", "sid"]); let mut jingle = Jingle { action: get_attr!(root, "action", required), diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 79949d73005f67d6dc38cbf9fe15d932755f7da9..fe9eaa104aef19039584dc1c6dbf0b6cc535d0ad 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -111,6 +111,7 @@ impl TryFrom for Transport { fn try_from(elem: Element) -> Result { check_self!(elem, "transport", JINGLE_S5B); + check_no_unknown_attributes!(elem, "transport", ["sid", "dstaddr", "mode"]); let sid = get_attr!(elem, "sid", required); let dstaddr = get_attr!(elem, "dstaddr", optional); let mode = get_attr!(elem, "mode", default); From 965f6a1f8396a76a3645432b3d3e0138ad672415 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 15 May 2018 01:55:16 +0200 Subject: [PATCH 437/698] ecaps2: Use a macro to generate ECaps2. --- src/ecaps2.rs | 61 +++++++++++++++------------------------------------ src/macros.rs | 3 +++ 2 files changed, 21 insertions(+), 43 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 8fcd0c9e539eb5bb2a7bcb74400dac9a4eb0f515..b96585bcd2d7bee710cab5b634d3473cc9142e4d 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -4,14 +4,10 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - use disco::{Feature, Identity, DiscoInfoResult, DiscoInfoQuery}; use data_forms::DataForm; use hashes::{Hash, Algo}; -use minidom::Element; -use error::Error; use ns; use base64; @@ -20,35 +16,12 @@ use sha3::{Sha3_256, Sha3_512}; use blake2::Blake2b; use digest::{Digest, VariableOutput}; -#[derive(Debug, Clone)] -pub struct ECaps2 { - hashes: Vec, -} - -impl TryFrom for ECaps2 { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "c", ECAPS2, "ecaps2"); - check_no_attributes!(elem, "ecaps2"); - let mut hashes = vec!(); - for child in elem.children() { - hashes.push(Hash::try_from(child.clone())?); - } - Ok(ECaps2 { - hashes: hashes, - }) - } -} - -impl From for Element { - fn from(ecaps2: ECaps2) -> Element { - Element::builder("c") - .ns(ns::ECAPS2) - .append(ecaps2.hashes) - .build() - } -} +generate_element_with_children!( + ECaps2, "c", ECAPS2, + children: [ + hashes: Vec = ("hash", HASHES) => Hash + ] +); fn compute_item(field: &str) -> Vec { let mut bytes = field.as_bytes().to_vec(); @@ -165,7 +138,9 @@ pub fn query_ecaps2(hash: Hash) -> DiscoInfoQuery { #[cfg(test)] mod tests { use super::*; - use ecaps2; + use try_from::TryFrom; + use minidom::Element; + use error::Error; #[test] fn test_parse() { @@ -186,14 +161,14 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "This is not a hash element."); + assert_eq!(message, "Unknown child in c element."); } #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); let disco = DiscoInfoResult::try_from(elem).unwrap(); - let ecaps2 = ecaps2::compute_disco(&disco); + let ecaps2 = compute_disco(&disco); assert_eq!(ecaps2.len(), 54); } @@ -256,13 +231,13 @@ mod tests { 98, 105, 108, 101, 31, 31, 66, 111, 109, 98, 117, 115, 77, 111, 100, 31, 30, 28, 28]; let disco = DiscoInfoResult::try_from(elem).unwrap(); - let ecaps2 = ecaps2::compute_disco(&disco); + let ecaps2 = compute_disco(&disco); assert_eq!(ecaps2.len(), 0x1d9); assert_eq!(ecaps2, expected); - let sha_256 = ecaps2::hash_ecaps2(&ecaps2, Algo::Sha_256).unwrap(); + let sha_256 = hash_ecaps2(&ecaps2, Algo::Sha_256).unwrap(); assert_eq!(sha_256.hash, base64::decode("kzBZbkqJ3ADrj7v08reD1qcWUwNGHaidNUgD7nHpiw8=").unwrap()); - let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, Algo::Sha3_256).unwrap(); + let sha3_256 = hash_ecaps2(&ecaps2, Algo::Sha3_256).unwrap(); assert_eq!(sha3_256.hash, base64::decode("79mdYAfU9rEdTOcWDO7UEAt6E56SUzk/g6TnqUeuD9Q=").unwrap()); } @@ -428,19 +403,19 @@ mod tests { 48, 49, 49, 49, 50, 49, 54, 45, 109, 111, 100, 32, 40, 84, 99, 108, 47, 84, 107, 32, 56, 46,54, 98, 50, 41, 31, 30, 29, 28]; let disco = DiscoInfoResult::try_from(elem).unwrap(); - let ecaps2 = ecaps2::compute_disco(&disco); + let ecaps2 = compute_disco(&disco); assert_eq!(ecaps2.len(), 0x543); assert_eq!(ecaps2, expected); - let sha_256 = ecaps2::hash_ecaps2(&ecaps2, Algo::Sha_256).unwrap(); + let sha_256 = hash_ecaps2(&ecaps2, Algo::Sha_256).unwrap(); assert_eq!(sha_256.hash, base64::decode("u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY=").unwrap()); - let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, Algo::Sha3_256).unwrap(); + let sha3_256 = hash_ecaps2(&ecaps2, Algo::Sha3_256).unwrap(); assert_eq!(sha3_256.hash, base64::decode("XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg=").unwrap()); } #[test] fn test_blake2b_512() { - let hash = ecaps2::hash_ecaps2("abc".as_bytes(), Algo::Blake2b_512).unwrap(); + let hash = hash_ecaps2("abc".as_bytes(), Algo::Blake2b_512).unwrap(); let known_hash: Vec = vec!( 0xBA, 0x80, 0xA5, 0x3F, 0x98, 0x1C, 0x4D, 0x0D, 0x6A, 0x27, 0x97, 0xB6, 0x9F, 0x12, 0xF6, 0xE9, 0x4C, 0x21, 0x2F, 0x14, 0x68, 0x5A, 0xC4, 0xB7, 0x4B, 0x12, 0xBB, 0x6F, 0xDB, 0xFF, 0xA2, 0xD1, diff --git a/src/macros.rs b/src/macros.rs index 20dff441d3c4cb90dbc2904eac674029c2d19a55..d4b6427c82a54549ab02fb670d31a438e1e1c042 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -425,6 +425,9 @@ macro_rules! generate_element_with_text { } macro_rules! generate_element_with_children { + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: Vec<$child_type> = ($child_name, $child_ns) => $child_constructor),+]); + ); ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [$($(#[$child_meta])* $child_ident: Vec<$child_type> = ($child_name, $child_ns) => $child_constructor),+]); ); From d9f2af6c97cf790bd3ac689388c55989bf52dc57 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 15 May 2018 02:06:38 +0200 Subject: [PATCH 438/698] mam: Use a macro to generate Result_. --- src/macros.rs | 3 +++ src/mam.rs | 52 ++++++++++----------------------------------------- 2 files changed, 13 insertions(+), 42 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index d4b6427c82a54549ab02fb670d31a438e1e1c042..df4b03e0a8817b3dc53b7df88177ca855b210d48 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -543,6 +543,9 @@ macro_rules! generate_element_with_children { ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, child: ($(#[$child_meta:meta])* $child_ident:ident: $child_type:ty = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], child: ($(#[$child_meta])* $child_ident: $child_type = ($child_name, $child_ns) => $child_constructor)); ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], child: ($(#[$child_meta:meta])* $child_ident:ident: $child_type:ty = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], child: ($(#[$child_meta])* $child_ident: $child_type = ($child_name, $child_ns) => $child_constructor)); + ); ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], child: ($(#[$child_meta:meta])* $child_ident:ident: $child_type:ty = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( $(#[$meta])* #[derive(Debug, Clone)] diff --git a/src/mam.rs b/src/mam.rs index d9f8892902a386b140bbcd4d2b0cebe5dae1ddd9..22052cf3d3f2ee0e807fd37ba9dd6221929c8811 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -25,12 +25,16 @@ pub struct Query { pub set: Option, } -#[derive(Debug, Clone)] -pub struct Result_ { - pub queryid: String, - pub id: String, - pub forwarded: Forwarded, -} +generate_element_with_children!( + Result_, "result", MAM, + attributes: [ + id: String = "id" => required, + queryid: String = "queryid" => required, + ], + child: ( + forwarded: Forwarded = ("forwarded", FORWARD) => Forwarded + ) +); #[derive(Debug, Clone)] pub struct Fin { @@ -74,31 +78,6 @@ impl TryFrom for Query { } } -impl TryFrom for Result_ { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "result", MAM); - check_no_unknown_attributes!(elem, "result", ["queryid", "id"]); - let mut forwarded = None; - for child in elem.children() { - if child.is("forwarded", ns::FORWARD) { - forwarded = Some(Forwarded::try_from(child.clone())?); - } else { - return Err(Error::ParseError("Unknown child in result element.")); - } - } - let forwarded = forwarded.ok_or(Error::ParseError("Mandatory forwarded element missing in result."))?; - let queryid = get_attr!(elem, "queryid", required); - let id = get_attr!(elem, "id", required); - Ok(Result_ { - queryid, - id, - forwarded, - }) - } -} - impl TryFrom for Fin { type Err = Error; @@ -168,17 +147,6 @@ impl From for Element { } } -impl From for Element { - fn from(result: Result_) -> Element { - Element::builder("result") - .ns(ns::MAM) - .attr("queryid", result.queryid) - .attr("id", result.id) - .append(result.forwarded) - .build() - } -} - impl From for Element { fn from(fin: Fin) -> Element { Element::builder("fin") From d29021b85c9c79185ef12619bb5b886adae407ee Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 May 2018 14:48:29 +0200 Subject: [PATCH 439/698] iq: Replace clunky payload enums with proper traits. --- src/iq.rs | 170 ++++-------------------------------------------------- 1 file changed, 10 insertions(+), 160 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index 8726a9d5756b1af4f21a57b88c7729dbf825a660..73395c6f28d9bd444cc93b95379286dab131ff30 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -17,163 +17,15 @@ use error::Error; use ns; use stanza_error::StanzaError; -use roster::Roster; -use disco::{DiscoInfoResult, DiscoInfoQuery}; -use ibb::{Open as IbbOpen, Data as IbbData, Close as IbbClose}; -use jingle::Jingle; -use ping::Ping; -use mam::{Query as MamQuery, Fin as MamFin, Prefs as MamPrefs}; -/// Lists every known payload of an ``. -#[derive(Debug, Clone)] -pub enum IqGetPayload { - Roster(Roster), - DiscoInfo(DiscoInfoQuery), - Ping(Ping), - MamQuery(MamQuery), - MamPrefs(MamPrefs), +/// Should be implemented on every known payload of an ``. +pub trait IqGetPayload: TryFrom + Into {} - Unknown(Element), -} +/// Should be implemented on every known payload of an ``. +pub trait IqSetPayload: TryFrom + Into {} -/// Lists every known payload of an ``. -#[derive(Debug, Clone)] -pub enum IqSetPayload { - Roster(Roster), - IbbOpen(IbbOpen), - IbbData(IbbData), - IbbClose(IbbClose), - Jingle(Jingle), - MamQuery(MamQuery), - MamPrefs(MamPrefs), - - Unknown(Element), -} - -/// Lists every known payload of an ``. -#[derive(Debug, Clone)] -pub enum IqResultPayload { - Roster(Roster), - DiscoInfo(DiscoInfoResult), - MamQuery(MamQuery), - MamFin(MamFin), - MamPrefs(MamPrefs), - - Unknown(Element), -} - -impl TryFrom for IqGetPayload { - type Err = Error; - - fn try_from(elem: Element) -> Result { - Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { - // RFC-6121 - ("query", ns::ROSTER) => IqGetPayload::Roster(Roster::try_from(elem)?), - - // XEP-0030 - ("query", ns::DISCO_INFO) => IqGetPayload::DiscoInfo(DiscoInfoQuery::try_from(elem)?), - - // XEP-0199 - ("ping", ns::PING) => IqGetPayload::Ping(Ping::try_from(elem)?), - - // XEP-0313 - ("query", ns::MAM) => IqGetPayload::MamQuery(MamQuery::try_from(elem)?), - ("prefs", ns::MAM) => IqGetPayload::MamPrefs(MamPrefs::try_from(elem)?), - - _ => IqGetPayload::Unknown(elem), - }) - } -} - -impl From for Element { - fn from(payload: IqGetPayload) -> Element { - match payload { - IqGetPayload::Roster(roster) => roster.into(), - IqGetPayload::DiscoInfo(disco) => disco.into(), - IqGetPayload::Ping(ping) => ping.into(), - IqGetPayload::MamQuery(query) => query.into(), - IqGetPayload::MamPrefs(prefs) => prefs.into(), - - IqGetPayload::Unknown(elem) => elem, - } - } -} - -impl TryFrom for IqSetPayload { - type Err = Error; - - fn try_from(elem: Element) -> Result { - Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { - // RFC-6121 - ("query", ns::ROSTER) => IqSetPayload::Roster(Roster::try_from(elem)?), - - // XEP-0047 - ("open", ns::IBB) => IqSetPayload::IbbOpen(IbbOpen::try_from(elem)?), - ("data", ns::IBB) => IqSetPayload::IbbData(IbbData::try_from(elem)?), - ("close", ns::IBB) => IqSetPayload::IbbClose(IbbClose::try_from(elem)?), - - // XEP-0166 - ("jingle", ns::JINGLE) => IqSetPayload::Jingle(Jingle::try_from(elem)?), - - // XEP-0313 - ("query", ns::MAM) => IqSetPayload::MamQuery(MamQuery::try_from(elem)?), - ("prefs", ns::MAM) => IqSetPayload::MamPrefs(MamPrefs::try_from(elem)?), - - _ => IqSetPayload::Unknown(elem), - }) - } -} - -impl From for Element { - fn from(payload: IqSetPayload) -> Element { - match payload { - IqSetPayload::Roster(roster) => roster.into(), - IqSetPayload::IbbOpen(open) => open.into(), - IqSetPayload::IbbData(data) => data.into(), - IqSetPayload::IbbClose(close) => close.into(), - IqSetPayload::Jingle(jingle) => jingle.into(), - IqSetPayload::MamQuery(query) => query.into(), - IqSetPayload::MamPrefs(prefs) => prefs.into(), - - IqSetPayload::Unknown(elem) => elem, - } - } -} - -impl TryFrom for IqResultPayload { - type Err = Error; - - fn try_from(elem: Element) -> Result { - Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { - // RFC-6121 - ("query", ns::ROSTER) => IqResultPayload::Roster(Roster::try_from(elem)?), - - // XEP-0030 - ("query", ns::DISCO_INFO) => IqResultPayload::DiscoInfo(DiscoInfoResult::try_from(elem)?), - - // XEP-0313 - ("query", ns::MAM) => IqResultPayload::MamQuery(MamQuery::try_from(elem)?), - ("fin", ns::MAM) => IqResultPayload::MamFin(MamFin::try_from(elem)?), - ("prefs", ns::MAM) => IqResultPayload::MamPrefs(MamPrefs::try_from(elem)?), - - _ => IqResultPayload::Unknown(elem), - }) - } -} - -impl From for Element { - fn from(payload: IqResultPayload) -> Element { - match payload { - IqResultPayload::Roster(roster) => roster.into(), - IqResultPayload::DiscoInfo(disco) => disco.into(), - IqResultPayload::MamQuery(query) => query.into(), - IqResultPayload::MamFin(fin) => fin.into(), - IqResultPayload::MamPrefs(prefs) => prefs.into(), - - IqResultPayload::Unknown(elem) => elem, - } - } -} +/// Should be implemented on every known payload of an ``. +pub trait IqResultPayload: TryFrom + Into {} #[derive(Debug, Clone)] pub enum IqType { @@ -296,6 +148,7 @@ mod tests { use super::*; use stanza_error::{ErrorType, DefinedCondition}; use compare_elements::NamespaceAwareCompare; + use disco::DiscoInfoQuery; #[test] fn test_require_type() { @@ -459,13 +312,10 @@ mod tests { #[cfg(feature = "component")] let elem: Element = "".parse().unwrap(); let iq = Iq::try_from(elem).unwrap(); - let payload = match iq.payload { - IqType::Get(payload) => IqGetPayload::try_from(payload).unwrap(), + let disco_info = match iq.payload { + IqType::Get(payload) => DiscoInfoQuery::try_from(payload).unwrap(), _ => panic!(), }; - assert!(match payload { - IqGetPayload::DiscoInfo(DiscoInfoQuery { .. }) => true, - _ => false, - }); + assert!(disco_info.node.is_none()); } } From d5f88d26369ea633286f228c922d7f219c9ffedd Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 May 2018 14:49:00 +0200 Subject: [PATCH 440/698] iq: Add helper constructors. --- src/iq.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/iq.rs b/src/iq.rs index 73395c6f28d9bd444cc93b95379286dab131ff30..e695dc3e9a50e0a48a363180923d1e4232d6c260 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -55,6 +55,59 @@ pub struct Iq { pub payload: IqType, } +impl Iq { + pub fn from_get(payload: impl IqGetPayload) -> Iq { + Iq { + from: None, + to: None, + id: None, + payload: IqType::Get(payload.into()), + } + } + + pub fn from_set(payload: impl IqSetPayload) -> Iq { + Iq { + from: None, + to: None, + id: None, + payload: IqType::Set(payload.into()), + } + } + + pub fn from_result(payload: Option) -> Iq { + Iq { + from: None, + to: None, + id: None, + payload: IqType::Result(payload.map(|payload| payload.into())), + } + } + + pub fn from_error(payload: StanzaError) -> Iq { + Iq { + from: None, + to: None, + id: None, + payload: IqType::Error(payload), + } + } + + pub fn with_to(mut self, to: Jid) -> Iq { + self.to = Some(to); + self + } + + pub fn with_from(mut self, from: Jid) -> Iq { + self.from = Some(from); + self + } + + pub fn with_id(mut self, id: String) -> Iq { + self.id = Some(id); + self + } +} + impl TryFrom for Iq { type Err = Error; From 412eafb363d056a198d85b3bfeaf16ba8645f7cd Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 May 2018 15:08:17 +0200 Subject: [PATCH 441/698] iq: Implement the Iq*Payload traits on every possible payload. --- src/bind.rs | 4 ++++ src/blocking.rs | 7 +++++++ src/disco.rs | 9 +++++++++ src/ibb.rs | 7 +++++++ src/ibr.rs | 5 +++++ src/jingle.rs | 3 +++ src/mam.rs | 11 +++++++++++ src/ping.rs | 4 ++++ src/pubsub/pubsub.rs | 5 +++++ src/roster.rs | 5 +++++ src/version.rs | 4 ++++ 11 files changed, 64 insertions(+) diff --git a/src/bind.rs b/src/bind.rs index 4597636146584e53bc80c551e512eb20ced877f2..97a75aa3e750514a60fc8a43b415fbacad36b3f6 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -12,6 +12,7 @@ use minidom::Element; use error::Error; use jid::Jid; use ns; +use iq::{IqSetPayload, IqResultPayload}; #[derive(Debug, Clone, PartialEq)] pub enum Bind { @@ -29,6 +30,9 @@ impl Bind { } } +impl IqSetPayload for Bind {} +impl IqResultPayload for Bind {} + impl TryFrom for Bind { type Err = Error; diff --git a/src/blocking.rs b/src/blocking.rs index b24c9e49d82dea86edc29cdc7cd330f584bf2a5b..11287ba7b41b524e5abfdcd1930c85d2aa365028 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -12,9 +12,12 @@ use minidom::Element; use error::Error; use ns; +use iq::{IqGetPayload, IqSetPayload, IqResultPayload}; generate_empty_element!(BlocklistRequest, "blocklist", BLOCKING); +impl IqGetPayload for BlocklistRequest {} + macro_rules! generate_blocking_element { ($elem:ident, $name:tt) => ( #[derive(Debug, Clone)] @@ -59,6 +62,10 @@ generate_blocking_element!(BlocklistResult, "blocklist"); generate_blocking_element!(Block, "block"); generate_blocking_element!(Unblock, "unblock"); +impl IqResultPayload for BlocklistResult {} +impl IqSetPayload for Block {} +impl IqSetPayload for Unblock {} + generate_empty_element!(Blocked, "blocked", BLOCKING_ERRORS); #[cfg(test)] diff --git a/src/disco.rs b/src/disco.rs index 136a50ad27a995f2773e84cddb7ead3c28e7b9d3..ea49476cf6b0dd210714d53919ea16f35ea28a9d 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -14,6 +14,7 @@ use jid::Jid; use error::Error; use ns; +use iq::{IqGetPayload, IqResultPayload}; use data_forms::{DataForm, DataFormType}; generate_element_with_only_attributes!( @@ -26,6 +27,8 @@ DiscoInfoQuery, "query", DISCO_INFO, [ node: Option = "node" => optional, ]); +impl IqGetPayload for DiscoItemsQuery {} + generate_element_with_only_attributes!( /// Structure representing a `` element. #[derive(PartialEq)] @@ -108,6 +111,8 @@ pub struct DiscoInfoResult { pub extensions: Vec, } +impl IqResultPayload for DiscoInfoResult {} + impl TryFrom for DiscoInfoResult { type Err = Error; @@ -190,6 +195,8 @@ DiscoItemsQuery, "query", DISCO_ITEMS, [ node: Option = "node" => optional, ]); +impl IqGetPayload for DiscoInfoResult {} + generate_element_with_only_attributes!( /// Structure representing an `` element. Item, "item", DISCO_ITEMS, [ @@ -218,6 +225,8 @@ generate_element_with_children!( ] ); +impl IqResultPayload for DiscoItemsResult {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/ibb.rs b/src/ibb.rs index 9c2cdccc5f3ea96394acd860baa825bd45748a84..e33ca3bf7c33321c10d35a06165c86fa4251d4c5 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -5,6 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use helpers::Base64; +use iq::IqSetPayload; generate_attribute!(Stanza, "stanza", { Iq => "iq", @@ -17,6 +18,8 @@ generate_element_with_only_attributes!(Open, "open", IBB, [ stanza: Stanza = "stanza" => default, ]); +impl IqSetPayload for Open {} + generate_element_with_text!(Data, "data", IBB, [ seq: u16 = "seq" => required, @@ -25,10 +28,14 @@ generate_element_with_text!(Data, "data", IBB, data: Base64> ); +impl IqSetPayload for Data {} + generate_element_with_only_attributes!(Close, "close", IBB, [ sid: String = "sid" => required, ]); +impl IqSetPayload for Close {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/ibr.rs b/src/ibr.rs index 99cbcd57ba831381180551ab682691ee3f272d06..72c02b2b9c2d388418b6f0b122044a229869105f 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -11,6 +11,7 @@ use minidom::Element; use error::Error; +use iq::{IqGetPayload, IqSetPayload, IqResultPayload}; use data_forms::DataForm; use ns; @@ -25,6 +26,10 @@ pub struct Query { //pub oob: Option, } +impl IqGetPayload for Query; +impl IqSetPayload for Query; +impl IqResultPayload for Query; + impl TryFrom for Query { type Err = Error; diff --git a/src/jingle.rs b/src/jingle.rs index 8d7d309ece38655ac1c90fd2a3dfd5a9a2a183c1..b64b0e7cccc0d73597b7f44d98ddb4f94e729cef 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -12,6 +12,7 @@ use jid::Jid; use error::Error; use ns; +use iq::IqSetPayload; generate_attribute!(Action, "action", { ContentAccept => "content-accept", @@ -305,6 +306,8 @@ pub struct Jingle { pub other: Vec, } +impl IqSetPayload for Jingle {} + impl Jingle { pub fn new(action: Action, sid: SessionId) -> Jingle { Jingle { diff --git a/src/mam.rs b/src/mam.rs index 22052cf3d3f2ee0e807fd37ba9dd6221929c8811..664947ee49a2a1f72ab447d12bfedddd194d7c8b 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -11,6 +11,7 @@ use jid::Jid; use error::Error; +use iq::{IqGetPayload, IqSetPayload, IqResultPayload}; use data_forms::DataForm; use rsm::Set; use forwarding::Forwarded; @@ -25,6 +26,10 @@ pub struct Query { pub set: Option, } +impl IqGetPayload for Query {} +impl IqSetPayload for Query {} +impl IqResultPayload for Query {} + generate_element_with_children!( Result_, "result", MAM, attributes: [ @@ -42,6 +47,8 @@ pub struct Fin { pub set: Set, } +impl IqResultPayload for Fin {} + generate_attribute!(DefaultPrefs, "default", { Always => "always", Never => "never", @@ -55,6 +62,10 @@ pub struct Prefs { pub never: Vec, } +impl IqGetPayload for Prefs {} +impl IqSetPayload for Prefs {} +impl IqResultPayload for Prefs {} + impl TryFrom for Query { type Err = Error; diff --git a/src/ping.rs b/src/ping.rs index 8159ffe9384d2cfe5fb2d1af51b324c6dd935b48..c2f0a4409ca90e0b619bdf699a8acb6045373240 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -5,8 +5,12 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use iq::IqGetPayload; + generate_empty_element!(Ping, "ping", PING); +impl IqGetPayload for Ping {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index d90ca9826ba9831ee20155a05e16780aaee05b35..569a954a7fe7754d2de345407f29e9921d98a90e 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -15,6 +15,7 @@ use error::Error; use ns; +use iq::{IqGetPayload, IqSetPayload, IqResultPayload}; use data_forms::DataForm; use pubsub::{NodeName, ItemId, Subscription, SubscriptionId}; @@ -364,6 +365,10 @@ pub enum PubSub { Unsubscribe(Unsubscribe), } +impl IqGetPayload for PubSub {} +impl IqSetPayload for PubSub {} +impl IqResultPayload for PubSub {} + impl TryFrom for PubSub { type Err = Error; diff --git a/src/roster.rs b/src/roster.rs index 2b62f8088c37c989465214484affb378392ac3b6..e851c485874890d7b242dacaa338a70ca61807b7 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -5,6 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use jid::Jid; +use iq::{IqGetPayload, IqSetPayload, IqResultPayload}; generate_elem_id!(Group, "group", ROSTER); @@ -54,6 +55,10 @@ generate_element_with_children!( ] ); +impl IqGetPayload for Roster {} +impl IqSetPayload for Roster {} +impl IqResultPayload for Roster {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/version.rs b/src/version.rs index e01a40d308b8711690935153838e36763062d395..49b23ad93c3b2192d1fa04e8a00baed1d3028b82 100644 --- a/src/version.rs +++ b/src/version.rs @@ -8,6 +8,7 @@ use try_from::TryFrom; use minidom::Element; use error::Error; use ns; +use iq::{IqGetPayload, IqResultPayload}; #[derive(Debug, Clone)] pub struct Version { @@ -16,6 +17,9 @@ pub struct Version { pub os: Option, } +impl IqGetPayload for Version {} +impl IqResultPayload for Version {} + impl TryFrom for Version { type Err = Error; From 0bec19c224edf5a9452d6b4e51fb9c403804cfe9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 May 2018 15:16:15 +0200 Subject: [PATCH 442/698] ibr: Fix syntax error. --- src/ibr.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ibr.rs b/src/ibr.rs index 72c02b2b9c2d388418b6f0b122044a229869105f..6964755c822c8ac876c634276db6eff59130e8b6 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -26,9 +26,9 @@ pub struct Query { //pub oob: Option, } -impl IqGetPayload for Query; -impl IqSetPayload for Query; -impl IqResultPayload for Query; +impl IqGetPayload for Query {} +impl IqSetPayload for Query {} +impl IqResultPayload for Query {} impl TryFrom for Query { type Err = Error; From 9bd6fe002d30f6bce89ac10da810619db7ccf3ce Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 17 May 2018 19:24:51 +0200 Subject: [PATCH 443/698] disco: Implement IqGetPayload on the correct structs. --- src/disco.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index ea49476cf6b0dd210714d53919ea16f35ea28a9d..4c70edd2f87422cb348b1ee6771eaf8bf77bae45 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -27,7 +27,7 @@ DiscoInfoQuery, "query", DISCO_INFO, [ node: Option = "node" => optional, ]); -impl IqGetPayload for DiscoItemsQuery {} +impl IqGetPayload for DiscoInfoQuery {} generate_element_with_only_attributes!( /// Structure representing a `` element. @@ -195,7 +195,7 @@ DiscoItemsQuery, "query", DISCO_ITEMS, [ node: Option = "node" => optional, ]); -impl IqGetPayload for DiscoInfoResult {} +impl IqGetPayload for DiscoItemsQuery {} generate_element_with_only_attributes!( /// Structure representing an `` element. From c828f938398ac42822e86201d91c7bf563cb18c8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 18 May 2018 19:04:02 +0200 Subject: [PATCH 444/698] Add a Stream Management implementation. --- src/lib.rs | 3 ++ src/ns.rs | 3 ++ src/sm.rs | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 src/sm.rs diff --git a/src/lib.rs b/src/lib.rs index a0b36c34e4d46b974328e375649777593981f02c..220a087f02ea725b1ee9a9f276a8cc4a34be5320 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -119,6 +119,9 @@ pub mod receipts; /// XEP-0191: Blocking Command pub mod blocking; +/// XEP-0198: Stream Management +pub mod sm; + /// XEP-0199: XMPP Ping pub mod ping; diff --git a/src/ns.rs b/src/ns.rs index 1eb6e3015f45eb47c31cdc08cb6a26487090b28a..e5f79fb5e374a180cb29e184edbd0f79faebab44 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -85,6 +85,9 @@ pub const BLOCKING: &str = "urn:xmpp:blocking"; /// XEP-0191: Blocking Command pub const BLOCKING_ERRORS: &str = "urn:xmpp:blocking:errors"; +/// XEP-0198: Stream Management +pub const SM: &str = "urn:xmpp:sm:3"; + /// XEP-0199: XMPP Ping pub const PING: &str = "urn:xmpp:ping"; diff --git a/src/sm.rs b/src/sm.rs new file mode 100644 index 0000000000000000000000000000000000000000..994c8da7fde17c76276b10ac48f6cd696df1f8c9 --- /dev/null +++ b/src/sm.rs @@ -0,0 +1,113 @@ +// Copyright (c) 2018 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use stanza_error::DefinedCondition; + +generate_element_with_only_attributes!( + A, "a", SM, [ + h: u32 = "h" => required, + ] +); + +impl A { + pub fn new(h: u32) -> A { + A { h } + } +} + +generate_attribute!(ResumeAttr, "resume", bool); + +generate_element_with_only_attributes!( + Enable, "enable", SM, [ + // TODO: should be the infinite integer set ≥ 1. + max: Option = "max" => optional, + resume: ResumeAttr = "resume" => default, + ] +); + +impl Enable { + pub fn new() -> Self { + Enable { + max: None, + resume: ResumeAttr::False, + } + } + + pub fn with_max(mut self, max: u32) -> Self { + self.max = Some(max); + self + } + + pub fn with_resume(mut self) -> Self { + self.resume = ResumeAttr::True; + self + } +} + +generate_element_with_only_attributes!( + Enabled, "enabled", SM, [ + id: Option = "id" => optional, + location: Option = "location" => optional, + // TODO: should be the infinite integer set ≥ 1. + max: Option = "max" => optional, + resume: ResumeAttr = "resume" => default, + ] +); + +generate_element_with_children!( + Failed, "failed", SM, + attributes: [ + h: Option = "h" => optional, + ], + child: ( + // XXX: implement the * handling. + error: Option = ("*", XMPP_STANZAS) => DefinedCondition + ) +); + +generate_empty_element!( + R, "r", SM +); + +generate_element_with_only_attributes!( + Resume, "resume", SM, [ + h: u32 = "h" => required, + previd: String = "previd" => required, + ] +); + +generate_element_with_only_attributes!( + Resumed, "resumed", SM, [ + h: u32 = "h" => required, + previd: String = "previd" => required, + ] +); + +// TODO: add support for optional and required. +generate_empty_element!( + /// Represents availability of Stream Management in ``. + StreamManagement, "sm", SM +); + +#[cfg(test)] +mod tests { + use super::*; + use try_from::TryFrom; + use minidom::Element; + + #[test] + fn a() { + let elem: Element = " Date: Mon, 28 May 2018 16:23:23 +0200 Subject: [PATCH 445/698] version: Add a serialisation test. --- src/version.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/version.rs b/src/version.rs index 49b23ad93c3b2192d1fa04e8a00baed1d3028b82..cb8773369a6f4ef41d69cca186b36859e5ddff90 100644 --- a/src/version.rs +++ b/src/version.rs @@ -82,6 +82,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use compare_elements::NamespaceAwareCompare; #[test] fn test_simple() { @@ -91,4 +92,17 @@ mod tests { assert_eq!(version.version, String::from("0.3.0")); assert_eq!(version.os, None); } + + #[test] + fn serialisation() { + let version = Version { + name: String::from("xmpp-rs"), + version: String::from("0.3.0"), + os: None, + }; + let elem1 = Element::from(version); + let elem2: Element = "xmpp-rs0.3.0".parse().unwrap(); + println!("{:?}", elem1); + assert!(elem1.compare_to(&elem2)); + } } From cbef3f6e8d281218e7da4d6e52ca3a2e103e9fa7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 28 May 2018 16:24:17 +0200 Subject: [PATCH 446/698] muc: Add a serialisation test. --- src/muc/muc.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 4a6fda401064f946dcba19802e8bd63007f25d24..5132adbf6c1901b758680f9ae9ab343b921dd6c5 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -52,6 +52,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use compare_elements::NamespaceAwareCompare; #[test] fn test_muc_simple() { @@ -98,7 +99,11 @@ mod tests { coucou " .parse().unwrap(); + let elem1 = elem.clone(); let muc = Muc::try_from(elem).unwrap(); assert_eq!(muc.password, Some("coucou".to_owned())); + + let elem2 = Element::from(muc); + assert!(elem1.compare_to(&elem2)); } } From a625b88fce4e1eaeb9609a6f69393554ea1692f7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 28 May 2018 16:29:51 +0200 Subject: [PATCH 447/698] macros: Merge all element children into a common syntax. --- src/jingle.rs | 79 +++-------------- src/macros.rs | 207 +++++++++++++++++++------------------------ src/mam.rs | 112 ++++++----------------- src/muc/muc.rs | 51 ++--------- src/muc/user.rs | 142 ++++++++--------------------- src/pubsub/pubsub.rs | 16 ++-- src/sm.rs | 4 +- src/version.rs | 79 +++-------------- 8 files changed, 200 insertions(+), 490 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index b64b0e7cccc0d73597b7f44d98ddb4f94e729cef..951a2fa1d72a570f9baa8f08c4029b1adb1ebe3f 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -66,16 +66,20 @@ generate_attribute!(Disposition, "disposition", { generate_id!(ContentId); -#[derive(Debug, Clone)] -pub struct Content { - pub creator: Creator, - pub disposition: Disposition, - pub name: ContentId, - pub senders: Senders, - pub description: Option, - pub transport: Option, - pub security: Option, -} +generate_element_with_children!( + Content, "content", JINGLE, + attributes: [ + creator: Creator = "creator" => required, + disposition: Disposition = "disposition" => default, + name: ContentId = "name" => required, + senders: Senders = "senders" => default + ], + children: [ + description: Option = ("description", JINGLE) => Element, + transport: Option = ("transport", JINGLE) => Element, + security: Option = ("security", JINGLE) => Element + ] +); impl Content { pub fn new(creator: Creator, name: ContentId) -> Content { @@ -116,61 +120,6 @@ impl Content { } } -impl TryFrom for Content { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "content", JINGLE); - check_no_unknown_attributes!(elem, "content", ["creator", "disposition", "name", "senders"]); - - let mut content = Content { - creator: get_attr!(elem, "creator", required), - disposition: get_attr!(elem, "disposition", default), - name: get_attr!(elem, "name", required), - senders: get_attr!(elem, "senders", default), - description: None, - transport: None, - security: None, - }; - for child in elem.children() { - if child.name() == "description" { - if content.description.is_some() { - return Err(Error::ParseError("Content must not have more than one description.")); - } - content.description = Some(child.clone()); - } else if child.name() == "transport" { - if content.transport.is_some() { - return Err(Error::ParseError("Content must not have more than one transport.")); - } - content.transport = Some(child.clone()); - } else if child.name() == "security" { - if content.security.is_some() { - return Err(Error::ParseError("Content must not have more than one security.")); - } - content.security = Some(child.clone()); - } else { - return Err(Error::ParseError("Unknown child in content element.")); - } - } - Ok(content) - } -} - -impl From for Element { - fn from(content: Content) -> Element { - Element::builder("content") - .ns(ns::JINGLE) - .attr("creator", content.creator) - .attr("disposition", content.disposition) - .attr("name", content.name) - .attr("senders", content.senders) - .append(content.description) - .append(content.transport) - .append(content.security) - .build() - } -} - #[derive(Debug, Clone, PartialEq)] pub enum Reason { AlternativeSession, //(String), diff --git a/src/macros.rs b/src/macros.rs index df4b03e0a8817b3dc53b7df88177ca855b210d48..9adae5197760ab4c196add8d387f35e26ae60085 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -424,129 +424,96 @@ macro_rules! generate_element_with_text { ); } -macro_rules! generate_element_with_children { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: Vec<$child_type> = ($child_name, $child_ns) => $child_constructor),+]); +macro_rules! start_decl { + (Vec, $type:ty) => ( + Vec<$type> ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [$($(#[$child_meta])* $child_ident: Vec<$child_type> = ($child_name, $child_ns) => $child_constructor),+]); + (Option, $type:ty) => ( + Option<$type> ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( - $(#[$meta])* - #[derive(Debug, Clone)] - pub struct $elem { - $( - $(#[$attr_meta])* - pub $attr: $attr_type, - )* - $( - $(#[$child_meta])* - pub $child_ident: Vec<$child_type>, - )* - } - - impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = ::error::Error; - - fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { - check_self!(elem, $name, $ns); - check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); - let mut parsed_children = vec!(); - for child in elem.children() { - $( - if child.is($child_name, ::ns::$child_ns) { - let parsed_child = $child_constructor::try_from(child.clone())?; - parsed_children.push(parsed_child); - continue; - } - )* - return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); - } - Ok($elem { - $( - $attr: get_attr!(elem, $attr_name, $attr_action), - )* - $( - $child_ident: parsed_children, - )* - }) - } - } + (Required, $type:ty) => ( + $type + ); +} - impl From<$elem> for ::minidom::Element { - fn from(elem: $elem) -> ::minidom::Element { - ::minidom::Element::builder($name) - .ns(::ns::$ns) - $( - .attr($attr_name, elem.$attr) - )* - $( - .append(elem.$child_ident) - )* - .build() - } - } +macro_rules! start_parse_elem { + ($temp:ident: Vec) => ( + let mut $temp = Vec::new(); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, child: ($(#[$child_meta:meta])* $child_ident:ident: Option<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], child: ($(#[$child_meta])* $child_ident: Option<$child_type> = ($child_name, $child_ns) => $child_constructor)); + ($temp:ident: Option) => ( + let mut $temp = None; ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], child: ($(#[$child_meta:meta])* $child_ident:ident: Option<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], child: ($(#[$child_meta])* $child_ident: Option<$child_type> = ($child_name, $child_ns) => $child_constructor)); + ($temp:ident: Required) => ( + let mut $temp = None; ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], child: ($(#[$child_meta:meta])* $child_ident:ident: Option<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( - $(#[$meta])* - #[derive(Debug, Clone)] - pub struct $elem { - $( - $(#[$attr_meta])* - pub $attr: $attr_type, - )* - $(#[$child_meta])* - pub $child_ident: Option<$child_type>, - } +} - impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = ::error::Error; +macro_rules! do_parse { + ($elem:ident, Element) => ( + $elem.clone() + ); + ($elem:ident, String) => ( + $elem.text() + ); + ($elem:ident, $constructor:ident) => ( + $constructor::try_from($elem.clone())? + ); +} - fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { - check_self!(elem, $name, $ns); - check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); - let mut parsed_child = None; - for child in elem.children() { - if child.is($child_name, ::ns::$child_ns) { - parsed_child = Some($child_constructor::try_from(child.clone())?); - continue; - } - return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); - } - Ok($elem { - $( - $attr: get_attr!(elem, $attr_name, $attr_action), - )* - $child_ident: parsed_child, - }) - } +macro_rules! do_parse_elem { + ($temp:ident: Vec = $constructor:ident => $elem:ident) => ( + $temp.push(do_parse!($elem, $constructor)); + ); + ($temp:ident: Option = $constructor:ident => $elem:ident) => ( + if $temp.is_some() { + return Err(::error::Error::ParseError(concat!("coucou", " must not have more than one ", "coucou", "."))); } + $temp = Some(do_parse!($elem, $constructor)); + ); + ($temp:ident: Required = $constructor:ident => $elem:ident) => ( + $temp = Some(do_parse!($elem, $constructor)); + ); +} - impl From<$elem> for ::minidom::Element { - fn from(elem: $elem) -> ::minidom::Element { - ::minidom::Element::builder($name) - .ns(::ns::$ns) - $( - .attr($attr_name, elem.$attr) - )* - .append(elem.$child_ident) - .build() - } - } +macro_rules! finish_parse_elem { + ($temp:ident: Vec = $name:tt) => ( + $temp + ); + ($temp:ident: Option = $name:tt) => ( + $temp + ); + ($temp:ident: Required = $name:tt) => ( + $temp.ok_or(::error::Error::ParseError(concat!("Missing child coucou in ", $name, " element.")))? + ); +} + +macro_rules! generate_serialiser { + ($parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => ( + ::minidom::Element::builder($name) + .ns(::ns::$ns) + .append($parent.$elem) + .build() ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, child: ($(#[$child_meta:meta])* $child_ident:ident: $child_type:ty = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], child: ($(#[$child_meta])* $child_ident: $child_type = ($child_name, $child_ns) => $child_constructor)); + ($parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => ( + $parent.$elem.map(|elem| + ::minidom::Element::builder($name) + .ns(::ns::$ns) + .append(elem) + .build()) ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], child: ($(#[$child_meta:meta])* $child_ident:ident: $child_type:ty = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], child: ($(#[$child_meta])* $child_ident: $child_type = ($child_name, $child_ns) => $child_constructor)); + ($parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => ( + $parent.$elem ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], child: ($(#[$child_meta:meta])* $child_ident:ident: $child_type:ty = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( +} + +macro_rules! generate_element_with_children { + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),+]); + ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),+]); + ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( $(#[$meta])* #[derive(Debug, Clone)] pub struct $elem { @@ -554,8 +521,10 @@ macro_rules! generate_element_with_children { $(#[$attr_meta])* pub $attr: $attr_type, )* + $( $(#[$child_meta])* - pub $child_ident: $child_type, + pub $child_ident: start_decl!($coucou, $child_type), + )* } impl ::try_from::TryFrom<::minidom::Element> for $elem { @@ -564,19 +533,25 @@ macro_rules! generate_element_with_children { fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { check_self!(elem, $name, $ns); check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); - let mut parsed_child = None; + $( + start_parse_elem!($child_ident: $coucou); + )* for child in elem.children() { + $( if child.is($child_name, ::ns::$child_ns) { - parsed_child = Some($child_constructor::try_from(child.clone())?); + do_parse_elem!($child_ident: $coucou = $child_constructor => child); continue; } + )* return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); } Ok($elem { $( $attr: get_attr!(elem, $attr_name, $attr_action), )* - $child_ident: parsed_child.ok_or(::error::Error::ParseError(concat!("Missing child ", $child_name, " in ", $name, " element.")))?, + $( + $child_ident: finish_parse_elem!($child_ident: $coucou = $child_name), + )* }) } } @@ -588,7 +563,9 @@ macro_rules! generate_element_with_children { $( .attr($attr_name, elem.$attr) )* - .append(elem.$child_ident) + $( + .append(generate_serialiser!(elem, $child_ident, $coucou, $child_constructor, ($child_name, $child_ns))) + )* .build() } } diff --git a/src/mam.rs b/src/mam.rs index 664947ee49a2a1f72ab447d12bfedddd194d7c8b..b835fc505e688c56924979f2ff7f8656d63420b3 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -18,13 +18,17 @@ use forwarding::Forwarded; use ns; -#[derive(Debug, Clone)] -pub struct Query { - pub queryid: Option, - pub node: Option, - pub form: Option, - pub set: Option, -} +generate_element_with_children!( + Query, "query", MAM, + attributes: [ + queryid: Option = "queryid" => optional, + node: Option = "node" => optional + ], + children: [ + form: Option = ("x", DATA_FORMS) => DataForm, + set: Option = ("set", RSM) => Set + ] +); impl IqGetPayload for Query {} impl IqSetPayload for Query {} @@ -36,16 +40,24 @@ generate_element_with_children!( id: String = "id" => required, queryid: String = "queryid" => required, ], - child: ( - forwarded: Forwarded = ("forwarded", FORWARD) => Forwarded - ) + children: [ + forwarded: Required = ("forwarded", FORWARD) => Forwarded + ] ); -#[derive(Debug, Clone)] -pub struct Fin { - pub complete: bool, - pub set: Set, -} +generate_attribute!( + Complete, "complete", bool +); + +generate_element_with_children!( + Fin, "fin", MAM, + attributes: [ + complete: Complete = "complete" => default + ], + children: [ + set: Required = ("set", RSM) => Set + ] +); impl IqResultPayload for Fin {} @@ -66,54 +78,6 @@ impl IqGetPayload for Prefs {} impl IqSetPayload for Prefs {} impl IqResultPayload for Prefs {} -impl TryFrom for Query { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "query", MAM); - check_no_unknown_attributes!(elem, "query", ["queryid", "node"]); - let mut form = None; - let mut set = None; - for child in elem.children() { - if child.is("x", ns::DATA_FORMS) { - form = Some(DataForm::try_from(child.clone())?); - } else if child.is("set", ns::RSM) { - set = Some(Set::try_from(child.clone())?); - } else { - return Err(Error::ParseError("Unknown child in query element.")); - } - } - let queryid = get_attr!(elem, "queryid", optional); - let node = get_attr!(elem, "node", optional); - Ok(Query { queryid, node, form, set }) - } -} - -impl TryFrom for Fin { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "fin", MAM); - check_no_unknown_attributes!(elem, "fin", ["complete"]); - let mut set = None; - for child in elem.children() { - if child.is("set", ns::RSM) { - set = Some(Set::try_from(child.clone())?); - } else { - return Err(Error::ParseError("Unknown child in fin element.")); - } - } - let set = set.ok_or(Error::ParseError("Mandatory set element missing in fin."))?; - let complete = match elem.attr("complete") { - Some(complete) if complete == "true" => true, - Some(complete) if complete == "false" => false, - None => false, - Some(_) => return Err(Error::ParseError("Invalid value for 'complete' attribute.")), - }; - Ok(Fin { complete, set }) - } -} - impl TryFrom for Prefs { type Err = Error; @@ -146,28 +110,6 @@ impl TryFrom for Prefs { } } -impl From for Element { - fn from(query: Query) -> Element { - Element::builder("query") - .ns(ns::MAM) - .attr("queryid", query.queryid) - .attr("node", query.node) - //.append(query.form) - .append(query.set) - .build() - } -} - -impl From for Element { - fn from(fin: Fin) -> Element { - Element::builder("fin") - .ns(ns::MAM) - .attr("complete", if fin.complete { Some("true") } else { None }) - .append(fin.set) - .build() - } -} - fn serialise_jid_list(name: &str, jids: Vec) -> Option { if jids.is_empty() { None diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 5132adbf6c1901b758680f9ae9ab343b921dd6c5..80905a20679928d1cf29f6a171b8fd88ee919a2c 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -5,53 +5,18 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - -use minidom::Element; - -use error::Error; - -use ns; - -#[derive(Debug, Clone)] -pub struct Muc { - pub password: Option, -} - -impl TryFrom for Muc { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "x", MUC); - check_no_attributes!(elem, "x"); - - let mut password = None; - for child in elem.children() { - if child.is("password", ns::MUC) { - password = Some(child.text()); - } else { - return Err(Error::ParseError("Unknown child in x element.")); - } - } - - Ok(Muc { - password: password, - }) - } -} - -impl From for Element { - fn from(muc: Muc) -> Element { - Element::builder("x") - .ns(ns::MUC) - .append(muc.password) - .build() - } -} +generate_element_with_children!( + Muc, "x", MUC, children: [ + password: Option = ("password", MUC) => String + ] +); #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; + use minidom::Element; + use error::Error; use compare_elements::NamespaceAwareCompare; #[test] diff --git a/src/muc/user.rs b/src/muc/user.rs index f53799cdfc7370e9c5147a157a25b9c1d825ba73..69060cef781823e0a1457d09cca79ec291a32b57 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -5,7 +5,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::{TryFrom, TryInto}; +use try_from::TryFrom; use minidom::Element; @@ -80,6 +80,7 @@ Status, "status", MUC_USER, "code", { /// Optional element used in elements inside presence stanzas of type /// "unavailable" that are sent to users who are kick or banned, as well as within IQs for tracking /// purposes. -- CHANGELOG 0.17 (2002-10-23) +/// /// Possesses a 'jid' and a 'nick' attribute, so that an action can be attributed either to a real /// JID or to a roomnick. -- CHANGELOG 1.25 (2012-02-08) #[derive(Debug, Clone, PartialEq)] @@ -140,108 +141,25 @@ generate_attribute!(Role, "role", { None => "none", }, Default = None); -#[derive(Debug, Clone)] -pub struct Item { - pub affiliation: Affiliation, - pub jid: Option, - pub nick: Option, - pub role: Role, - pub actor: Option, - pub continue_: Option, - pub reason: Option, -} - -impl TryFrom for Item { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "item", MUC_USER); - check_no_unknown_attributes!(elem, "item", ["affiliation", "jid", "nick", "role"]); - let mut actor: Option = None; - let mut continue_: Option = None; - let mut reason: Option = None; - for child in elem.children() { - if child.is("actor", ns::MUC_USER) { - actor = Some(child.clone().try_into()?); - } else if child.is("continue", ns::MUC_USER) { - continue_ = Some(child.clone().try_into()?); - } else if child.is("reason", ns::MUC_USER) { - reason = Some(child.clone().try_into()?); - } else { - return Err(Error::ParseError("Unknown child in item element.")); - } - } - - let affiliation: Affiliation = get_attr!(elem, "affiliation", required); - let jid: Option = get_attr!(elem, "jid", optional); - let nick: Option = get_attr!(elem, "nick", optional); - let role: Role = get_attr!(elem, "role", required); - - Ok(Item{ - affiliation: affiliation, - jid: jid, - nick: nick, - role: role, - actor: actor, - continue_: continue_, - reason: reason, - }) - } -} - -impl From for Element { - fn from(item: Item) -> Element { - Element::builder("item") - .ns(ns::MUC_USER) - .attr("affiliation", item.affiliation) - .attr("jid", item.jid) - .attr("nick", item.nick) - .attr("role", item.role) - .append(item.actor) - .append(item.continue_) - .append(item.reason) - .build() - } -} - -#[derive(Debug, Clone)] -pub struct MucUser { - pub status: Vec, - pub items: Vec, -} - -impl TryFrom for MucUser { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "x", MUC_USER); - check_no_attributes!(elem, "x"); - let mut status = vec!(); - let mut items = vec!(); - for child in elem.children() { - if child.is("status", ns::MUC_USER) { - status.push(Status::try_from(child.clone())?); - } else if child.is("item", ns::MUC_USER) { - items.push(Item::try_from(child.clone())?); - } else { - return Err(Error::ParseError("Unknown child in x element.")); - } - } - Ok(MucUser { - status, - items, - }) - } -} - -impl From for Element { - fn from(muc_user: MucUser) -> Element { - Element::builder("x") - .ns(ns::MUC_USER) - .append(muc_user.status) - .build() - } -} +generate_element_with_children!( + Item, "item", MUC_USER, attributes: [ + affiliation: Affiliation = "affiliation" => required, + jid: Option = "jid" => optional, + nick: Option = "nick" => optional, + role: Role = "role" => required + ], children: [ + actor: Option = ("actor", MUC_USER) => Actor, + continue_: Option = ("continue", MUC_USER) => Continue, + reason: Option = ("reason", MUC_USER) => Reason + ] +); + +generate_element_with_children!( + MucUser, "x", MUC_USER, children: [ + status: Vec = ("status", MUC_USER) => Status, + items: Vec = ("item", MUC_USER) => Item + ] +); #[cfg(test)] mod tests { @@ -257,6 +175,24 @@ mod tests { MucUser::try_from(elem).unwrap(); } + #[test] + fn statuses_and_items() { + let elem: Element = " + + + + + + ".parse().unwrap(); + let muc_user = MucUser::try_from(elem).unwrap(); + assert_eq!(muc_user.status.len(), 2); + assert_eq!(muc_user.status[0], Status::AffiliationChange); + assert_eq!(muc_user.status[1], Status::ConfigShowsUnavailableMembers); + assert_eq!(muc_user.items.len(), 1); + assert_eq!(muc_user.items[0].affiliation, Affiliation::Member); + assert_eq!(muc_user.items[0].role, Role::Moderator); + } + #[test] fn test_invalid_child() { let elem: Element = " diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index 569a954a7fe7754d2de345407f29e9921d98a90e..840232b0f2c15c804cd343e77ac0b0a4af3b754e 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -72,10 +72,10 @@ generate_element_with_only_attributes!( generate_element_with_children!( /// Request to configure a new node. Configure, "configure", PUBSUB, - child: ( + children: [ /// The form to configure it. form: Option = ("x", DATA_FORMS) => DataForm - ) + ] ); generate_element_with_only_attributes!( @@ -168,10 +168,10 @@ generate_element_with_children!( /// The subscription identifier affected by this request. subid: Option = "subid" => optional, ], - child: ( + children: [ /// The form describing the subscription. form: Option = ("x", DATA_FORMS) => DataForm - ) + ] ); generate_element_with_children!( @@ -190,10 +190,10 @@ generate_element_with_children!( generate_element_with_children!( /// The options associated to a publish request. PublishOptions, "publish-options", PUBSUB, - child: ( + children: [ /// The form describing these options. form: Option = ("x", DATA_FORMS) => DataForm - ) + ] ); generate_attribute!( @@ -300,10 +300,10 @@ generate_element_with_children!( /// The state of the subscription. subscription: Option = "subscription" => optional, ], - child: ( + children: [ /// The options related to this subscription. subscribe_options: Option = ("subscribe-options", PUBSUB) => SubscribeOptions - ) + ] ); generate_element_with_only_attributes!( diff --git a/src/sm.rs b/src/sm.rs index 994c8da7fde17c76276b10ac48f6cd696df1f8c9..4030bf71c3eabb919e4b17aca796b6e2a97632e7 100644 --- a/src/sm.rs +++ b/src/sm.rs @@ -62,10 +62,10 @@ generate_element_with_children!( attributes: [ h: Option = "h" => optional, ], - child: ( + children: [ // XXX: implement the * handling. error: Option = ("*", XMPP_STANZAS) => DefinedCondition - ) + ] ); generate_empty_element!( diff --git a/src/version.rs b/src/version.rs index cb8773369a6f4ef41d69cca186b36859e5ddff90..94cfcbb28c95f08c42420291983e220545e4e800 100644 --- a/src/version.rs +++ b/src/version.rs @@ -4,84 +4,25 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; -use minidom::Element; -use error::Error; -use ns; use iq::{IqGetPayload, IqResultPayload}; -#[derive(Debug, Clone)] -pub struct Version { - pub name: String, - pub version: String, - pub os: Option, -} +generate_element_with_children!( + Version, "query", VERSION, + children: [ + name: Required = ("name", VERSION) => String, + version: Required = ("version", VERSION) => String, + os: Option = ("os", VERSION) => String + ] +); impl IqGetPayload for Version {} impl IqResultPayload for Version {} -impl TryFrom for Version { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "query", VERSION, "version"); - check_no_attributes!(elem, "version"); - let mut name = None; - let mut version = None; - let mut os = None; - for child in elem.children() { - if child.is("name", ns::VERSION) { - if name.is_some() { - return Err(Error::ParseError("More than one name in version element.")); - } - name = Some(child.text()); - } else if child.is("version", ns::VERSION) { - if version.is_some() { - return Err(Error::ParseError("More than one version in version element.")); - } - version = Some(child.text()); - } else if child.is("os", ns::VERSION) { - if os.is_some() { - return Err(Error::ParseError("More than one os in version element.")); - } - os = Some(child.text()); - } else { - return Err(Error::ParseError("Unknown child in version element.")); - } - } - let name = name.unwrap(); - let version = version.unwrap(); - Ok(Version { - name, - version, - os, - }) - } -} - -impl From for Element { - fn from(version: Version) -> Element { - Element::builder("query") - .ns(ns::VERSION) - .append(Element::builder("name") - .ns(ns::VERSION) - .append(version.name) - .build()) - .append(Element::builder("version") - .ns(ns::VERSION) - .append(version.version) - .build()) - .append(Element::builder("os") - .ns(ns::VERSION) - .append(version.os) - .build()) - .build() - } -} - #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; + use minidom::Element; use compare_elements::NamespaceAwareCompare; #[test] From ef227c461769d4023182d2ea0b2e8064c76bcbbe Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 28 May 2018 16:37:22 +0200 Subject: [PATCH 448/698] macros: Simplify generate_element_with_only_attributes!(). --- src/macros.rs | 53 ++++++++++----------------------------------------- 1 file changed, 10 insertions(+), 43 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index 9adae5197760ab4c196add8d387f35e26ae60085..3c07f180f2979aeb69fdadaa1f3085fe2d5371aa 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -287,43 +287,10 @@ macro_rules! generate_empty_element { macro_rules! generate_element_with_only_attributes { ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => ( - generate_element_with_only_attributes!($(#[$meta])* $elem, $name, $ns, [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*]); + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []); ); ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => ( - $(#[$meta])* - #[derive(Debug, Clone)] - pub struct $elem { - $( - $(#[$attr_meta])* - pub $attr: $attr_type, - )* - } - - impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = ::error::Error; - - fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { - check_self!(elem, $name, $ns); - check_no_children!(elem, $name); - check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); - Ok($elem { - $( - $attr: get_attr!(elem, $attr_name, $attr_action), - )* - }) - } - } - - impl From<$elem> for ::minidom::Element { - fn from(elem: $elem) -> ::minidom::Element { - ::minidom::Element::builder($name) - .ns(::ns::$ns) - $( - .attr($attr_name, elem.$attr) - )* - .build() - } - } + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []); ); } @@ -507,13 +474,13 @@ macro_rules! generate_serialiser { } macro_rules! generate_element_with_children { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),+]); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => ( + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),+]); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => ( + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => ( $(#[$meta])* #[derive(Debug, Clone)] pub struct $elem { @@ -536,10 +503,10 @@ macro_rules! generate_element_with_children { $( start_parse_elem!($child_ident: $coucou); )* - for child in elem.children() { + for _child in elem.children() { $( - if child.is($child_name, ::ns::$child_ns) { - do_parse_elem!($child_ident: $coucou = $child_constructor => child); + if _child.is($child_name, ::ns::$child_ns) { + do_parse_elem!($child_ident: $coucou = $child_constructor => _child); continue; } )* From 9c598fbdf984e9d46cd393721d1d8530655fc1f8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 28 May 2018 16:42:35 +0200 Subject: [PATCH 449/698] macros: Rename generate_element_with_only_attributes!() into generate_element!(). --- src/disco.rs | 20 ++++++++++++-------- src/eme.rs | 5 +++-- src/ibb.rs | 6 ++++-- src/idle.rs | 3 ++- src/jingle_ft.rs | 3 ++- src/jingle_ibb.rs | 3 ++- src/jingle_s5b.rs | 3 ++- src/macros.rs | 18 +++++++++--------- src/message_correct.rs | 3 ++- src/muc/user.rs | 3 ++- src/pubsub/pubsub.rs | 25 +++++++++++++++---------- src/receipts.rs | 3 ++- src/sm.rs | 25 +++++++++++++++---------- src/stanza_id.rs | 6 ++++-- src/stream.rs | 3 ++- src/websocket.rs | 3 ++- 16 files changed, 80 insertions(+), 52 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index 4c70edd2f87422cb348b1ee6771eaf8bf77bae45..679b06e5e197b6556a1728f36c67a8b5b3d9a302 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -17,22 +17,24 @@ use ns; use iq::{IqGetPayload, IqResultPayload}; use data_forms::{DataForm, DataFormType}; -generate_element_with_only_attributes!( +generate_element!( /// Structure representing a `` element. /// /// It should only be used in an ``, as it can only represent /// the request, and not a result. -DiscoInfoQuery, "query", DISCO_INFO, [ +DiscoInfoQuery, "query", DISCO_INFO, +attributes: [ /// Node on which we are doing the discovery. node: Option = "node" => optional, ]); impl IqGetPayload for DiscoInfoQuery {} -generate_element_with_only_attributes!( +generate_element!( /// Structure representing a `` element. #[derive(PartialEq)] -Feature, "feature", DISCO_INFO, [ +Feature, "feature", DISCO_INFO, +attributes: [ /// Namespace of the feature we want to represent. var: String = "var" => required, ]); @@ -185,21 +187,23 @@ impl From for Element { } } -generate_element_with_only_attributes!( +generate_element!( /// Structure representing a `` element. /// /// It should only be used in an ``, as it can only represent /// the request, and not a result. -DiscoItemsQuery, "query", DISCO_ITEMS, [ +DiscoItemsQuery, "query", DISCO_ITEMS, +attributes: [ /// Node on which we are doing the discovery. node: Option = "node" => optional, ]); impl IqGetPayload for DiscoItemsQuery {} -generate_element_with_only_attributes!( +generate_element!( /// Structure representing an `` element. -Item, "item", DISCO_ITEMS, [ +Item, "item", DISCO_ITEMS, +attributes: [ /// JID of the entity pointed by this item. jid: Jid = "jid" => required, /// Node of the entity pointed by this item. diff --git a/src/eme.rs b/src/eme.rs index ff3737b06d9b57c80c64fda8bf7c297987ddc8fb..a276ba25e13a6fd683d5b29d66127d613e790301 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -4,9 +4,10 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -generate_element_with_only_attributes!( +generate_element!( /// Structure representing an `` element. -ExplicitMessageEncryption, "encryption", EME, [ +ExplicitMessageEncryption, "encryption", EME, +attributes: [ /// Namespace of the encryption scheme used. namespace: String = "namespace" => required, diff --git a/src/ibb.rs b/src/ibb.rs index e33ca3bf7c33321c10d35a06165c86fa4251d4c5..2edb8ee5020cd549ab2159aa4e13ede90e9fde74 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -12,7 +12,8 @@ generate_attribute!(Stanza, "stanza", { Message => "message", }, Default = Iq); -generate_element_with_only_attributes!(Open, "open", IBB, [ +generate_element!(Open, "open", IBB, +attributes: [ block_size: u16 = "block-size" => required, sid: String = "sid" => required, stanza: Stanza = "stanza" => default, @@ -30,7 +31,8 @@ generate_element_with_text!(Data, "data", IBB, impl IqSetPayload for Data {} -generate_element_with_only_attributes!(Close, "close", IBB, [ +generate_element!(Close, "close", IBB, +attributes: [ sid: String = "sid" => required, ]); diff --git a/src/idle.rs b/src/idle.rs index 1312efb96fd60200f5c16f4e587f468c8179bf1c..22e844601178aec040acafc97a1fefed006724a3 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -6,7 +6,8 @@ use date::DateTime; -generate_element_with_only_attributes!(Idle, "idle", IDLE, [ +generate_element!(Idle, "idle", IDLE, +attributes: [ since: DateTime = "since" => required, ]); diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index eb7e9169e0aeb71bfec2eb301b8da270d0f43fe9..b9e1d3ef7dfad82f79bc5db8ec8ea2d1c5862bbe 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -287,7 +287,8 @@ impl From for Element { } } -generate_element_with_only_attributes!(Received, "received", JINGLE_FT, [ +generate_element!(Received, "received", JINGLE_FT, +attributes: [ name: ContentId = "name" => required, creator: Creator = "creator" => required, ]); diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 2e68e685b0b84e6ce4102621fa05e393198f95c0..156ba4d314879c32c53bb9a0dd51362293bf400f 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -8,7 +8,8 @@ use ibb::Stanza; generate_id!(StreamId); -generate_element_with_only_attributes!(Transport, "transport", JINGLE_IBB, [ +generate_element!(Transport, "transport", JINGLE_IBB, +attributes: [ block_size: u16 = "block-size" => required, sid: StreamId = "sid" => required, stanza: Stanza = "stanza" => default, diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index fe9eaa104aef19039584dc1c6dbf0b6cc535d0ad..94f3c0c741ffeec2fe6445c8bccb3c963e469648 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -30,7 +30,8 @@ generate_id!(CandidateId); generate_id!(StreamId); -generate_element_with_only_attributes!(Candidate, "candidate", JINGLE_S5B, [ +generate_element!(Candidate, "candidate", JINGLE_S5B, +attributes: [ cid: CandidateId = "cid" => required, host: IpAddr = "host" => required, jid: Jid = "jid" => required, diff --git a/src/macros.rs b/src/macros.rs index 3c07f180f2979aeb69fdadaa1f3085fe2d5371aa..9b9501042e2db2d57ec9fae876f9e9394b4fb311 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -285,15 +285,6 @@ macro_rules! generate_empty_element { ); } -macro_rules! generate_element_with_only_attributes { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []); - ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []); - ); -} - macro_rules! generate_id { ($elem:ident) => ( #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -473,6 +464,15 @@ macro_rules! generate_serialiser { ); } +macro_rules! generate_element { + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => ( + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []); + ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => ( + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []); + ); +} + macro_rules! generate_element_with_children { ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => ( generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]); diff --git a/src/message_correct.rs b/src/message_correct.rs index 3dbb86e49b5838bbfbf732bf376a8f2b0d772122..f301979764db485e21343fc0fb2ab3f0fc6bce02 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -4,7 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -generate_element_with_only_attributes!(Replace, "replace", MESSAGE_CORRECT, [ +generate_element!(Replace, "replace", MESSAGE_CORRECT, +attributes: [ id: String = "id" => required, ]); diff --git a/src/muc/user.rs b/src/muc/user.rs index 69060cef781823e0a1457d09cca79ec291a32b57..15cea9e419811c8df1c5b771d63b7f8717cff89b 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -120,7 +120,8 @@ impl From for Element { } } -generate_element_with_only_attributes!(Continue, "continue", MUC_USER, [ +generate_element!(Continue, "continue", MUC_USER, +attributes: [ thread: Option = "thread" => optional, ]); diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index 840232b0f2c15c804cd343e77ac0b0a4af3b754e..807ce9083de35501f3263d7f041dd60151df645e 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -58,9 +58,10 @@ generate_attribute!( } ); -generate_element_with_only_attributes!( +generate_element!( /// An affiliation element. - Affiliation, "affiliation", PUBSUB, [ + Affiliation, "affiliation", PUBSUB, + attributes: [ /// The node this affiliation pertains to. node: NodeName = "node" => required, @@ -78,17 +79,19 @@ generate_element_with_children!( ] ); -generate_element_with_only_attributes!( +generate_element!( /// Request to create a new node. - Create, "create", PUBSUB, [ + Create, "create", PUBSUB, + attributes: [ /// The node name to create, if `None` the service will generate one. node: Option = "node" => optional, ] ); -generate_element_with_only_attributes!( +generate_element!( /// Request for a default node configuration. - Default, "default", PUBSUB, [ + Default, "default", PUBSUB, + attributes: [ /// The node targetted by this request, otherwise the entire service. node: Option = "node" => optional, @@ -260,9 +263,10 @@ impl From for Element { } } -generate_element_with_only_attributes!( +generate_element!( /// A request to subscribe a JID to a node. - Subscribe, "subscribe", PUBSUB, [ + Subscribe, "subscribe", PUBSUB, + attributes: [ /// The JID being subscribed. jid: Jid = "jid" => required, @@ -306,9 +310,10 @@ generate_element_with_children!( ] ); -generate_element_with_only_attributes!( +generate_element!( /// An unsubscribe request. - Unsubscribe, "unsubscribe", PUBSUB, [ + Unsubscribe, "unsubscribe", PUBSUB, + attributes: [ /// The JID affected by this request. jid: Jid = "jid" => required, diff --git a/src/receipts.rs b/src/receipts.rs index dfaf54734a12f012208e5e2b95795ddb5b73410e..a557d008336c49e6fe0b9c2d835bf6680dac2cdd 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -6,7 +6,8 @@ generate_empty_element!(Request, "request", RECEIPTS); -generate_element_with_only_attributes!(Received, "received", RECEIPTS, [ +generate_element!(Received, "received", RECEIPTS, +attributes: [ id: Option = "id" => optional, ]); diff --git a/src/sm.rs b/src/sm.rs index 4030bf71c3eabb919e4b17aca796b6e2a97632e7..3558c8e72c75e987c954dbd10a170b597bcc3b8a 100644 --- a/src/sm.rs +++ b/src/sm.rs @@ -6,8 +6,9 @@ use stanza_error::DefinedCondition; -generate_element_with_only_attributes!( - A, "a", SM, [ +generate_element!( + A, "a", SM, + attributes: [ h: u32 = "h" => required, ] ); @@ -20,8 +21,9 @@ impl A { generate_attribute!(ResumeAttr, "resume", bool); -generate_element_with_only_attributes!( - Enable, "enable", SM, [ +generate_element!( + Enable, "enable", SM, + attributes: [ // TODO: should be the infinite integer set ≥ 1. max: Option = "max" => optional, resume: ResumeAttr = "resume" => default, @@ -47,8 +49,9 @@ impl Enable { } } -generate_element_with_only_attributes!( - Enabled, "enabled", SM, [ +generate_element!( + Enabled, "enabled", SM, + attributes: [ id: Option = "id" => optional, location: Option = "location" => optional, // TODO: should be the infinite integer set ≥ 1. @@ -72,15 +75,17 @@ generate_empty_element!( R, "r", SM ); -generate_element_with_only_attributes!( - Resume, "resume", SM, [ +generate_element!( + Resume, "resume", SM, + attributes: [ h: u32 = "h" => required, previd: String = "previd" => required, ] ); -generate_element_with_only_attributes!( - Resumed, "resumed", SM, [ +generate_element!( + Resumed, "resumed", SM, + attributes: [ h: u32 = "h" => required, previd: String = "previd" => required, ] diff --git a/src/stanza_id.rs b/src/stanza_id.rs index b321b46c8c19c4523131d65b37f4222a2113952f..16a8d9fc7e3e6f86b85f5dc1b162b1f2b15e4566 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -6,12 +6,14 @@ use jid::Jid; -generate_element_with_only_attributes!(StanzaId, "stanza-id", SID, [ +generate_element!(StanzaId, "stanza-id", SID, +attributes: [ id: String = "id" => required, by: Jid = "by" => required, ]); -generate_element_with_only_attributes!(OriginId, "origin-id", SID, [ +generate_element!(OriginId, "origin-id", SID, +attributes: [ id: String = "id" => required, ]); diff --git a/src/stream.rs b/src/stream.rs index 93a71c41d9a96249e93a19967bd16168f37f330b..aa48ad9f29ebcd97db28cd3098b436c6dcebfa6a 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -6,7 +6,8 @@ use jid::Jid; -generate_element_with_only_attributes!(Stream, "stream", STREAM, [ +generate_element!(Stream, "stream", STREAM, +attributes: [ from: Option = "from" => optional, to: Option = "to" => optional, id: Option = "id" => optional, diff --git a/src/websocket.rs b/src/websocket.rs index b5aa7903457af5d89ff46813471bd8ff1aa0a20a..78d0cada5a8ed3dfba02aefc568f65466f839f74 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -6,7 +6,8 @@ use jid::Jid; -generate_element_with_only_attributes!(Open, "open", WEBSOCKET, [ +generate_element!(Open, "open", WEBSOCKET, +attributes: [ from: Option = "from" => optional, to: Option = "to" => optional, id: Option = "id" => optional, From 3fb99988d54ba2d23d07a678183d74382be3753e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 28 May 2018 16:45:13 +0200 Subject: [PATCH 450/698] macros: Merge generate_element_with_children!() into generate_element!(). --- src/disco.rs | 2 +- src/ecaps2.rs | 2 +- src/jingle.rs | 2 +- src/jingle_ft.rs | 2 +- src/macros.rs | 11 ++++------- src/mam.rs | 6 +++--- src/media_element.rs | 2 +- src/muc/muc.rs | 2 +- src/muc/user.rs | 4 ++-- src/pubsub/pubsub.rs | 18 +++++++++--------- src/roster.rs | 4 ++-- src/sm.rs | 2 +- src/version.rs | 2 +- 13 files changed, 28 insertions(+), 31 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index 679b06e5e197b6556a1728f36c67a8b5b3d9a302..b6b653c59f865dfd58d4863877dea230f196c0a6 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -212,7 +212,7 @@ attributes: [ name: Option = "name" => optional, ]); -generate_element_with_children!( +generate_element!( /// Structure representing a `` element. /// diff --git a/src/ecaps2.rs b/src/ecaps2.rs index b96585bcd2d7bee710cab5b634d3473cc9142e4d..4caa7d35e785ad42fae8e885e77c120583265c48 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -16,7 +16,7 @@ use sha3::{Sha3_256, Sha3_512}; use blake2::Blake2b; use digest::{Digest, VariableOutput}; -generate_element_with_children!( +generate_element!( ECaps2, "c", ECAPS2, children: [ hashes: Vec = ("hash", HASHES) => Hash diff --git a/src/jingle.rs b/src/jingle.rs index 951a2fa1d72a570f9baa8f08c4029b1adb1ebe3f..1600fed1824a20dd7324e5a4eddf0437681c9a0c 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -66,7 +66,7 @@ generate_attribute!(Disposition, "disposition", { generate_id!(ContentId); -generate_element_with_children!( +generate_element!( Content, "content", JINGLE, attributes: [ creator: Creator = "creator" => required, diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index b9e1d3ef7dfad82f79bc5db8ec8ea2d1c5862bbe..4cb0fa52365d350bcc729cef354947dab1a8c573 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -18,7 +18,7 @@ use minidom::Element; use error::Error; use ns; -generate_element_with_children!( +generate_element!( #[derive(PartialEq, Default)] Range, "range", JINGLE_FT, attributes: [ diff --git a/src/macros.rs b/src/macros.rs index 9b9501042e2db2d57ec9fae876f9e9394b4fb311..48a2702d3574df765c2fc8b75e5a6d7f41f3f841 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -466,19 +466,16 @@ macro_rules! generate_serialiser { macro_rules! generate_element { ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []); + generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []); ); ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []); + generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []); ); -} - -macro_rules! generate_element_with_children { ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]); + generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]); ); ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]); + generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]); ); ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => ( $(#[$meta])* diff --git a/src/mam.rs b/src/mam.rs index b835fc505e688c56924979f2ff7f8656d63420b3..b1965667a8790f07391cd85f83285d56e6aafd9e 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -18,7 +18,7 @@ use forwarding::Forwarded; use ns; -generate_element_with_children!( +generate_element!( Query, "query", MAM, attributes: [ queryid: Option = "queryid" => optional, @@ -34,7 +34,7 @@ impl IqGetPayload for Query {} impl IqSetPayload for Query {} impl IqResultPayload for Query {} -generate_element_with_children!( +generate_element!( Result_, "result", MAM, attributes: [ id: String = "id" => required, @@ -49,7 +49,7 @@ generate_attribute!( Complete, "complete", bool ); -generate_element_with_children!( +generate_element!( Fin, "fin", MAM, attributes: [ complete: Complete = "complete" => default diff --git a/src/media_element.rs b/src/media_element.rs index 43f0d7acd101e18f254e8c020a43396fe73911df..9b9133c552abf633371ea7d0514754e9fd98d898 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -13,7 +13,7 @@ generate_element_with_text!(URI, "uri", MEDIA_ELEMENT, uri: TrimmedPlainText ); -generate_element_with_children!(MediaElement, "media", MEDIA_ELEMENT, +generate_element!(MediaElement, "media", MEDIA_ELEMENT, attributes: [ width: Option = "width" => optional, height: Option = "height" => optional diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 80905a20679928d1cf29f6a171b8fd88ee919a2c..355b089fffcb004eb587dad7b64f13f6a9c69aa9 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -5,7 +5,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -generate_element_with_children!( +generate_element!( Muc, "x", MUC, children: [ password: Option = ("password", MUC) => String ] diff --git a/src/muc/user.rs b/src/muc/user.rs index 15cea9e419811c8df1c5b771d63b7f8717cff89b..8baa62710039bfed01987ad0974173032cd0dd49 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -142,7 +142,7 @@ generate_attribute!(Role, "role", { None => "none", }, Default = None); -generate_element_with_children!( +generate_element!( Item, "item", MUC_USER, attributes: [ affiliation: Affiliation = "affiliation" => required, jid: Option = "jid" => optional, @@ -155,7 +155,7 @@ generate_element_with_children!( ] ); -generate_element_with_children!( +generate_element!( MucUser, "x", MUC_USER, children: [ status: Vec = ("status", MUC_USER) => Status, items: Vec = ("item", MUC_USER) => Item diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index 807ce9083de35501f3263d7f041dd60151df645e..8ecb89b6d8f116cad794e17a33c1a303c7f8dff4 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -22,7 +22,7 @@ use pubsub::{NodeName, ItemId, Subscription, SubscriptionId}; // TODO: a better solution would be to split this into a query and a result elements, like for // XEP-0030. -generate_element_with_children!( +generate_element!( /// A list of affiliations you have on a service, or on a node. Affiliations, "affiliations", PUBSUB, attributes: [ @@ -70,7 +70,7 @@ generate_element!( ] ); -generate_element_with_children!( +generate_element!( /// Request to configure a new node. Configure, "configure", PUBSUB, children: [ @@ -100,7 +100,7 @@ generate_element!( ] ); -generate_element_with_children!( +generate_element!( /// A request for a list of items. Items, "items", PUBSUB, attributes: [ @@ -158,7 +158,7 @@ impl From for Element { } } -generate_element_with_children!( +generate_element!( /// The options associated to a subscription request. Options, "options", PUBSUB, attributes: [ @@ -177,7 +177,7 @@ generate_element_with_children!( ] ); -generate_element_with_children!( +generate_element!( /// Request to publish items to a node. Publish, "publish", PUBSUB, attributes: [ @@ -190,7 +190,7 @@ generate_element_with_children!( ] ); -generate_element_with_children!( +generate_element!( /// The options associated to a publish request. PublishOptions, "publish-options", PUBSUB, children: [ @@ -204,7 +204,7 @@ generate_attribute!( Notify, "notify", bool ); -generate_element_with_children!( +generate_element!( /// A request to retract some items from a node. Retract, "retract", PUBSUB, attributes: [ @@ -275,7 +275,7 @@ generate_element!( ] ); -generate_element_with_children!( +generate_element!( /// A request for current subscriptions. Subscriptions, "subscriptions", PUBSUB, attributes: [ @@ -288,7 +288,7 @@ generate_element_with_children!( ] ); -generate_element_with_children!( +generate_element!( /// A subscription element, describing the state of a subscription. SubscriptionElem, "subscription", PUBSUB, attributes: [ diff --git a/src/roster.rs b/src/roster.rs index e851c485874890d7b242dacaa338a70ca61807b7..bbd0c39967411c99f0c3f6eb21b9f3bc3cc30589 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -17,7 +17,7 @@ generate_attribute!(Subscription, "subscription", { Remove => "remove", }, Default = None); -generate_element_with_children!( +generate_element!( /// Contact from the user’s contact list. #[derive(PartialEq)] Item, "item", ROSTER, @@ -38,7 +38,7 @@ generate_element_with_children!( ] ); -generate_element_with_children!( +generate_element!( /// The contact list of the user. Roster, "query", ROSTER, attributes: [ diff --git a/src/sm.rs b/src/sm.rs index 3558c8e72c75e987c954dbd10a170b597bcc3b8a..20e9fc6183cfaabefc131751e70d27f2c9d10896 100644 --- a/src/sm.rs +++ b/src/sm.rs @@ -60,7 +60,7 @@ generate_element!( ] ); -generate_element_with_children!( +generate_element!( Failed, "failed", SM, attributes: [ h: Option = "h" => optional, diff --git a/src/version.rs b/src/version.rs index 94cfcbb28c95f08c42420291983e220545e4e800..1265c1629bc90ed3ad8073290c4bc9fc4d9aa581 100644 --- a/src/version.rs +++ b/src/version.rs @@ -6,7 +6,7 @@ use iq::{IqGetPayload, IqResultPayload}; -generate_element_with_children!( +generate_element!( Version, "query", VERSION, children: [ name: Required = ("name", VERSION) => String, From 45fdb3d5e2a34926195cf59a9159089d6f282c34 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 28 May 2018 17:04:40 +0200 Subject: [PATCH 451/698] macros: Improve error messages. --- src/macros.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index 48a2702d3574df765c2fc8b75e5a6d7f41f3f841..971fe7606da2ca49c47ace48207adcc6cb06e727 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -419,29 +419,32 @@ macro_rules! do_parse { } macro_rules! do_parse_elem { - ($temp:ident: Vec = $constructor:ident => $elem:ident) => ( + ($temp:ident: Vec = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => ( $temp.push(do_parse!($elem, $constructor)); ); - ($temp:ident: Option = $constructor:ident => $elem:ident) => ( + ($temp:ident: Option = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => ( if $temp.is_some() { - return Err(::error::Error::ParseError(concat!("coucou", " must not have more than one ", "coucou", "."))); + return Err(::error::Error::ParseError(concat!("Element ", $parent_name, " must not have more than one ", $name, " child."))); } $temp = Some(do_parse!($elem, $constructor)); ); - ($temp:ident: Required = $constructor:ident => $elem:ident) => ( + ($temp:ident: Required = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => ( + if $temp.is_some() { + return Err(::error::Error::ParseError(concat!("Element ", $parent_name, " must not have more than one ", $name, " child."))); + } $temp = Some(do_parse!($elem, $constructor)); ); } macro_rules! finish_parse_elem { - ($temp:ident: Vec = $name:tt) => ( + ($temp:ident: Vec = $name:tt, $parent_name:tt) => ( $temp ); - ($temp:ident: Option = $name:tt) => ( + ($temp:ident: Option = $name:tt, $parent_name:tt) => ( $temp ); - ($temp:ident: Required = $name:tt) => ( - $temp.ok_or(::error::Error::ParseError(concat!("Missing child coucou in ", $name, " element.")))? + ($temp:ident: Required = $name:tt, $parent_name:tt) => ( + $temp.ok_or(::error::Error::ParseError(concat!("Missing child ", $name, " in ", $parent_name, " element.")))? ); } @@ -503,7 +506,7 @@ macro_rules! generate_element { for _child in elem.children() { $( if _child.is($child_name, ::ns::$child_ns) { - do_parse_elem!($child_ident: $coucou = $child_constructor => _child); + do_parse_elem!($child_ident: $coucou = $child_constructor => _child, $child_name, $name); continue; } )* @@ -514,7 +517,7 @@ macro_rules! generate_element { $attr: get_attr!(elem, $attr_name, $attr_action), )* $( - $child_ident: finish_parse_elem!($child_ident: $coucou = $child_name), + $child_ident: finish_parse_elem!($child_ident: $coucou = $child_name, $name), )* }) } From bb60af153163214befc0c4a127a33fdb27df365c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 28 May 2018 17:05:04 +0200 Subject: [PATCH 452/698] data_forms: Use a macro for ` element. pub fn new(h: u32) -> A { A { h } } } -generate_attribute!(ResumeAttr, "resume", bool); +generate_attribute!( + /// Whether to allow resumption of a previous stream. + ResumeAttr, "resume", bool +); generate_element!( + /// Client request for enabling stream management. Enable, "enable", SM, attributes: [ + /// The preferred resumption time in seconds by the client. // TODO: should be the infinite integer set ≥ 1. max: Option = "max" => optional, + + /// Whether the client wants to be allowed to resume the stream. resume: ResumeAttr = "resume" => default, ] ); impl Enable { + /// Generates a new `` element. pub fn new() -> Self { Enable { max: None, @@ -40,56 +49,86 @@ impl Enable { } } + /// Sets the preferred resumption time in seconds. pub fn with_max(mut self, max: u32) -> Self { self.max = Some(max); self } + /// Asks for resumption to be possible. pub fn with_resume(mut self) -> Self { self.resume = ResumeAttr::True; self } } +generate_id!( + /// A random identifier used for stream resumption. + StreamId +); + generate_element!( + /// Server response once stream management is enabled. Enabled, "enabled", SM, attributes: [ - id: Option = "id" => optional, + /// A random identifier used for stream resumption. + id: Option = "id" => optional, + + /// The preferred IP, domain, IP:port or domain:port location for + /// resumption. location: Option = "location" => optional, + + /// The preferred resumption time in seconds by the server. // TODO: should be the infinite integer set ≥ 1. max: Option = "max" => optional, + + /// Whether stream resumption is allowed. resume: ResumeAttr = "resume" => default, ] ); generate_element!( + /// A stream management error happened. Failed, "failed", SM, attributes: [ + /// The last handled stanza. h: Option = "h" => optional, ], children: [ + /// The error returned. // XXX: implement the * handling. error: Option = ("*", XMPP_STANZAS) => DefinedCondition ] ); generate_empty_element!( + /// Requests the currently received stanzas by the other party. R, "r", SM ); generate_element!( + /// Requests a stream resumption. Resume, "resume", SM, attributes: [ + /// The last handled stanza. h: u32 = "h" => required, - previd: String = "previd" => required, + + /// The previous id given by the server on + /// [enabled](struct.Enabled.html). + previd: StreamId = "previd" => required, ] ); generate_element!( + /// The response by the server for a successfully resumed stream. Resumed, "resumed", SM, attributes: [ + /// The last handled stanza. h: u32 = "h" => required, - previd: String = "previd" => required, + + /// The previous id given by the server on + /// [enabled](struct.Enabled.html). + previd: StreamId = "previd" => required, ] ); @@ -117,4 +156,30 @@ mod tests { let elem: Element = "".parse().unwrap(); StreamManagement::try_from(elem).unwrap(); } + + #[test] + fn resume() { + let elem: Element = "".parse().unwrap(); + let enable = Enable::try_from(elem).unwrap(); + assert_eq!(enable.max, None); + assert_eq!(enable.resume, ResumeAttr::True); + + let elem: Element = "".parse().unwrap(); + let enabled = Enabled::try_from(elem).unwrap(); + let previd = enabled.id.unwrap(); + assert_eq!(enabled.resume, ResumeAttr::True); + assert_eq!(previd, StreamId(String::from("coucou"))); + assert_eq!(enabled.max, Some(600)); + assert_eq!(enabled.location, None); + + let elem: Element = "".parse().unwrap(); + let resume = Resume::try_from(elem).unwrap(); + assert_eq!(resume.h, 5); + assert_eq!(resume.previd, previd); + + let elem: Element = "".parse().unwrap(); + let resumed = Resumed::try_from(elem).unwrap(); + assert_eq!(resumed.h, 5); + assert_eq!(resumed.previd, previd); + } } From eeeae25cb10b94528abf55daf8b90c0f499e9151 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 8 Aug 2018 20:52:27 +0200 Subject: [PATCH 516/698] jingle_ft: Document this module. --- src/jingle_ft.rs | 63 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 75656e5043ca6c5f8528c674935fcfc22f4ad83b..e206ab3c55c8f29b2507dee502cebdcd86dfbb68 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -#![allow(missing_docs)] - use try_from::TryFrom; use std::str::FromStr; @@ -21,18 +19,25 @@ use error::Error; use ns; generate_element!( + /// Represents a range in a file. #[derive(PartialEq, Default)] Range, "range", JINGLE_FT, attributes: [ + /// The offset in bytes from the beginning of the file. offset: u64 = "offset" => default, + + /// The length in bytes of the range, or None to be the entire + /// remaining of the file. length: Option = "length" => optional ], children: [ + /// List of hashes for this range. hashes: Vec = ("hash", HASHES) => Hash ] ); impl Range { + /// Creates a new range. pub fn new() -> Range { Default::default() } @@ -40,20 +45,38 @@ impl Range { type Lang = String; -generate_id!(Desc); +generate_id!( + /// Wrapper for a file description. + Desc +); +/// Represents a file to be transferred. #[derive(Debug, Clone)] pub struct File { + /// The date of last modification of this file. pub date: Option, + + /// The MIME type of this file. pub media_type: Option, + + /// The name of this file. pub name: Option, + + /// The description of this file, possibly localised. pub descs: BTreeMap, + + /// The size of this file, in bytes. pub size: Option, + + /// Used to request only a part of this file. pub range: Option, + + /// A list of hashes matching this entire file. pub hashes: Vec, } impl File { + /// Creates a new file descriptor. pub fn new() -> File { File { date: None, @@ -66,41 +89,50 @@ impl File { } } + /// Sets the date of last modification on this file. pub fn with_date(mut self, date: DateTime) -> File { self.date = Some(date); self } + /// Sets the date of last modification on this file from an ISO-8601 + /// string. pub fn with_date_str(mut self, date: &str) -> Result { self.date = Some(DateTime::from_str(date)?); Ok(self) } + /// Sets the MIME type of this file. pub fn with_media_type(mut self, media_type: String) -> File { self.media_type = Some(media_type); self } + /// Sets the name of this file. pub fn with_name(mut self, name: String) -> File { self.name = Some(name); self } + /// Sets a description for this file. pub fn add_desc(mut self, lang: &str, desc: Desc) -> File { self.descs.insert(Lang::from(lang), desc); self } + /// Sets the file size of this file, in bytes. pub fn with_size(mut self, size: u64) -> File { self.size = Some(size); self } + /// Request only a range of this file. pub fn with_range(mut self, range: Range) -> File { self.range = Some(range); self } + /// Add a hash on this file. pub fn add_hash(mut self, hash: Hash) -> File { self.hashes.push(hash); self @@ -211,8 +243,11 @@ impl From for Element { root.build() } } + +/// A wrapper element for a file. #[derive(Debug, Clone)] pub struct Description { + /// The actual file descriptor. pub file: File, } @@ -247,10 +282,16 @@ impl From for Element { } } +/// A checksum for checking that the file has been transferred correctly. #[derive(Debug, Clone)] pub struct Checksum { + /// The identifier of the file transfer content. pub name: ContentId, + + /// The creator of this file transfer. pub creator: Creator, + + /// The file being checksummed. pub file: File, } @@ -289,11 +330,17 @@ impl From for Element { } } -generate_element!(Received, "received", JINGLE_FT, -attributes: [ - name: ContentId = "name" => required, - creator: Creator = "creator" => required, -]); +generate_element!( + /// A notice that the file transfer has been completed. + Received, "received", JINGLE_FT, + attributes: [ + /// The content identifier of this Jingle session. + name: ContentId = "name" => required, + + /// The creator of this file transfer. + creator: Creator = "creator" => required, + ] +); #[cfg(test)] mod tests { From 22b424f43a1e86497f715a9c07d0bebcd4da34d8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 8 Aug 2018 21:07:22 +0200 Subject: [PATCH 517/698] jingle_s5b: Document this module. --- src/jingle_s5b.rs | 111 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 88 insertions(+), 23 deletions(-) diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 92627d3b726b4b0d59782c9b1a93b4b7d52da4d0..f6f05be3416394f0e50d0fc4a8d23c0f48c466b9 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -#![allow(missing_docs)] - use try_from::TryFrom; use std::net::IpAddr; @@ -16,33 +14,72 @@ use error::Error; use ns; -generate_attribute!(Type, "type", { - Assisted => "assisted", - Direct => "direct", - Proxy => "proxy", - Tunnel => "tunnel", -}, Default = Direct); +generate_attribute!( + /// The type of the connection being proposed by this candidate. + Type, "type", { + /// Direct connection using NAT assisting technologies like NAT-PMP or + /// UPnP-IGD. + Assisted => "assisted", + + /// Direct connection using the given interface. + Direct => "direct", + + /// SOCKS5 relay. + Proxy => "proxy", + + /// Tunnel protocol such as Teredo. + Tunnel => "tunnel", + }, Default = Direct +); + +generate_attribute!( + /// Which mode to use for the connection. + Mode, "mode", { + /// Use TCP, which is the default. + Tcp => "tcp", + + /// Use UDP. + Udp => "udp", + }, Default = Tcp +); + +generate_id!( + /// An identifier for a candidate. + CandidateId +); + +generate_id!( + /// An identifier for a stream. + StreamId +); + +generate_element!( + /// A candidate for a connection. + Candidate, "candidate", JINGLE_S5B, + attributes: [ + /// The identifier for this candidate. + cid: CandidateId = "cid" => required, -generate_attribute!(Mode, "mode", { - Tcp => "tcp", - Udp => "udp", -}, Default = Tcp); + /// The host to connect to. + host: IpAddr = "host" => required, -generate_id!(CandidateId); + /// The JID to request at the given end. + jid: Jid = "jid" => required, -generate_id!(StreamId); + /// The port to connect to. + port: Option = "port" => optional, -generate_element!(Candidate, "candidate", JINGLE_S5B, -attributes: [ - cid: CandidateId = "cid" => required, - host: IpAddr = "host" => required, - jid: Jid = "jid" => required, - port: Option = "port" => optional, - priority: u32 = "priority" => required, - type_: Type = "type" => default, -]); + /// The priority of this candidate, computed using this formula: + /// priority = (2^16)*(type preference) + (local preference) + priority: u32 = "priority" => required, + + /// The type of the connection being proposed by this candidate. + type_: Type = "type" => default, + ] +); impl Candidate { + /// Creates a new candidate with the given parameters. pub fn new(cid: CandidateId, host: IpAddr, jid: Jid, priority: u32) -> Candidate { Candidate { cid, @@ -54,36 +91,61 @@ impl Candidate { } } + /// Sets the port of this candidate. pub fn with_port(mut self, port: u16) -> Candidate { self.port = Some(port); self } + /// Sets the type of this candidate. pub fn with_type(mut self, type_: Type) -> Candidate { self.type_ = type_; self } } +/// The payload of a transport. #[derive(Debug, Clone)] pub enum TransportPayload { + /// The responder informs the initiator that the bytestream pointed by this + /// candidate has been activated. Activated(CandidateId), + + /// A list of suggested candidates. Candidates(Vec), + + /// Both parties failed to use a candidate, they should fallback to another + /// transport. CandidateError, + + /// The candidate pointed here should be used by both parties. CandidateUsed(CandidateId), + + /// This entity can’t connect to the SOCKS5 proxy. ProxyError, + + /// XXX: Invalid, should not be found in the wild. None, } +/// Describes a Jingle transport using a direct or proxied connection. #[derive(Debug, Clone)] pub struct Transport { + /// The stream identifier for this transport. pub sid: StreamId, + + /// The destination address. pub dstaddr: Option, + + /// The mode to be used for the transfer. pub mode: Mode, + + /// The payload of this transport. pub payload: TransportPayload, } impl Transport { + /// Creates a new transport element. pub fn new(sid: StreamId) -> Transport { Transport { sid, @@ -93,16 +155,19 @@ impl Transport { } } + /// Sets the destination address of this transport. pub fn with_dstaddr(mut self, dstaddr: String) -> Transport { self.dstaddr = Some(dstaddr); self } + /// Sets the mode of this transport. pub fn with_mode(mut self, mode: Mode) -> Transport { self.mode = mode; self } + /// Sets the payload of this transport. pub fn with_payload(mut self, payload: TransportPayload) -> Transport { self.payload = payload; self From 388941b483bac1f56604d49805ab8321e1370cba Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 19 Sep 2018 21:26:21 +0200 Subject: [PATCH 518/698] stanza_error: Document this module. --- src/stanza_error.rs | 221 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 187 insertions(+), 34 deletions(-) diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 85396cac5465a867c508bed3c2addf3842c51cbe..6b2cff9baa8e2fed158dede225ef643e0ddb03cc 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -#![allow(missing_docs)] - use try_from::TryFrom; use std::collections::BTreeMap; @@ -15,47 +13,202 @@ use error::Error; use jid::Jid; use ns; -generate_attribute!(ErrorType, "type", { - Auth => "auth", - Cancel => "cancel", - Continue => "continue", - Modify => "modify", - Wait => "wait", -}); - -generate_element_enum!(DefinedCondition, "condition", XMPP_STANZAS, { - BadRequest => "bad-request", - Conflict => "conflict", - FeatureNotImplemented => "feature-not-implemented", - Forbidden => "forbidden", - Gone => "gone", - InternalServerError => "internal-server-error", - ItemNotFound => "item-not-found", - JidMalformed => "jid-malformed", - NotAcceptable => "not-acceptable", - NotAllowed => "not-allowed", - NotAuthorized => "not-authorized", - PolicyViolation => "policy-violation", - RecipientUnavailable => "recipient-unavailable", - Redirect => "redirect", - RegistrationRequired => "registration-required", - RemoteServerNotFound => "remote-server-not-found", - RemoteServerTimeout => "remote-server-timeout", - ResourceConstraint => "resource-constraint", - ServiceUnavailable => "service-unavailable", - SubscriptionRequired => "subscription-required", - UndefinedCondition => "undefined-condition", - UnexpectedRequest => "unexpected-request", -}); +generate_attribute!( + /// The type of the error. + ErrorType, "type", { + /// Retry after providing credentials. + Auth => "auth", + + /// Do not retry (the error cannot be remedied). + Cancel => "cancel", + + /// Proceed (the condition was only a warning). + Continue => "continue", + + /// Retry after changing the data sent. + Modify => "modify", + + /// Retry after waiting (the error is temporary). + Wait => "wait", + } +); + +generate_element_enum!( + /// List of valid error conditions. + DefinedCondition, "condition", XMPP_STANZAS, { + /// The sender has sent a stanza containing XML that does not conform + /// to the appropriate schema or that cannot be processed (e.g., an IQ + /// stanza that includes an unrecognized value of the 'type' attribute, + /// or an element that is qualified by a recognized namespace but that + /// violates the defined syntax for the element); the associated error + /// type SHOULD be "modify". + BadRequest => "bad-request", + + /// Access cannot be granted because an existing resource exists with + /// the same name or address; the associated error type SHOULD be + /// "cancel". + Conflict => "conflict", + + /// The feature represented in the XML stanza is not implemented by the + /// intended recipient or an intermediate server and therefore the + /// stanza cannot be processed (e.g., the entity understands the + /// namespace but does not recognize the element name); the associated + /// error type SHOULD be "cancel" or "modify". + FeatureNotImplemented => "feature-not-implemented", + + /// The requesting entity does not possess the necessary permissions to + /// perform an action that only certain authorized roles or individuals + /// are allowed to complete (i.e., it typically relates to + /// authorization rather than authentication); the associated error + /// type SHOULD be "auth". + Forbidden => "forbidden", + + /// The recipient or server can no longer be contacted at this address, + /// typically on a permanent basis (as opposed to the error + /// condition, which is used for temporary addressing failures); the + /// associated error type SHOULD be "cancel" and the error stanza + /// SHOULD include a new address (if available) as the XML character + /// data of the element (which MUST be a Uniform Resource + /// Identifier [URI] or Internationalized Resource Identifier [IRI] at + /// which the entity can be contacted, typically an XMPP IRI as + /// specified in [XMPP‑URI]). + Gone => "gone", + + /// The server has experienced a misconfiguration or other internal + /// error that prevents it from processing the stanza; the associated + /// error type SHOULD be "cancel". + InternalServerError => "internal-server-error", + + /// The addressed JID or item requested cannot be found; the associated + /// error type SHOULD be "cancel". + ItemNotFound => "item-not-found", + + /// The sending entity has provided (e.g., during resource binding) or + /// communicated (e.g., in the 'to' address of a stanza) an XMPP + /// address or aspect thereof that violates the rules defined in + /// [XMPP‑ADDR]; the associated error type SHOULD be "modify". + JidMalformed => "jid-malformed", + + /// The recipient or server understands the request but cannot process + /// it because the request does not meet criteria defined by the + /// recipient or server (e.g., a request to subscribe to information + /// that does not simultaneously include configuration parameters + /// needed by the recipient); the associated error type SHOULD be + /// "modify". + NotAcceptable => "not-acceptable", + + /// The recipient or server does not allow any entity to perform the + /// action (e.g., sending to entities at a blacklisted domain); the + /// associated error type SHOULD be "cancel". + NotAllowed => "not-allowed", + + /// The sender needs to provide credentials before being allowed to + /// perform the action, or has provided improper credentials (the name + /// "not-authorized", which was borrowed from the "401 Unauthorized" + /// error of [HTTP], might lead the reader to think that this condition + /// relates to authorization, but instead it is typically used in + /// relation to authentication); the associated error type SHOULD be + /// "auth". + NotAuthorized => "not-authorized", + + /// The entity has violated some local service policy (e.g., a message + /// contains words that are prohibited by the service) and the server + /// MAY choose to specify the policy in the element or in an + /// application-specific condition element; the associated error type + /// SHOULD be "modify" or "wait" depending on the policy being + /// violated. + PolicyViolation => "policy-violation", + + /// The intended recipient is temporarily unavailable, undergoing + /// maintenance, etc.; the associated error type SHOULD be "wait". + RecipientUnavailable => "recipient-unavailable", + + /// The recipient or server is redirecting requests for this + /// information to another entity, typically in a temporary fashion (as + /// opposed to the error condition, which is used for permanent + /// addressing failures); the associated error type SHOULD be "modify" + /// and the error stanza SHOULD contain the alternate address in the + /// XML character data of the element (which MUST be a URI + /// or IRI with which the sender can communicate, typically an XMPP IRI + /// as specified in [XMPP‑URI]). + Redirect => "redirect", + + /// The requesting entity is not authorized to access the requested + /// service because prior registration is necessary (examples of prior + /// registration include members-only rooms in XMPP multi-user chat + /// [XEP‑0045] and gateways to non-XMPP instant messaging services, + /// which traditionally required registration in order to use the + /// gateway [XEP‑0100]); the associated error type SHOULD be "auth". + RegistrationRequired => "registration-required", + + /// A remote server or service specified as part or all of the JID of + /// the intended recipient does not exist or cannot be resolved (e.g., + /// there is no _xmpp-server._tcp DNS SRV record, the A or AAAA + /// fallback resolution fails, or A/AAAA lookups succeed but there is + /// no response on the IANA-registered port 5269); the associated error + /// type SHOULD be "cancel". + RemoteServerNotFound => "remote-server-not-found", + + /// A remote server or service specified as part or all of the JID of + /// the intended recipient (or needed to fulfill a request) was + /// resolved but communications could not be established within a + /// reasonable amount of time (e.g., an XML stream cannot be + /// established at the resolved IP address and port, or an XML stream + /// can be established but stream negotiation fails because of problems + /// with TLS, SASL, Server Dialback, etc.); the associated error type + /// SHOULD be "wait" (unless the error is of a more permanent nature, + /// e.g., the remote server is found but it cannot be authenticated or + /// it violates security policies). + RemoteServerTimeout => "remote-server-timeout", + + /// The server or recipient is busy or lacks the system resources + /// necessary to service the request; the associated error type SHOULD + /// be "wait". + ResourceConstraint => "resource-constraint", + + /// The server or recipient does not currently provide the requested + /// service; the associated error type SHOULD be "cancel". + ServiceUnavailable => "service-unavailable", + + /// The requesting entity is not authorized to access the requested + /// service because a prior subscription is necessary (examples of + /// prior subscription include authorization to receive presence + /// information as defined in [XMPP‑IM] and opt-in data feeds for XMPP + /// publish-subscribe as defined in [XEP‑0060]); the associated error + /// type SHOULD be "auth". + SubscriptionRequired => "subscription-required", + + /// The error condition is not one of those defined by the other + /// conditions in this list; any error type can be associated with this + /// condition, and it SHOULD NOT be used except in conjunction with an + /// application-specific condition. + UndefinedCondition => "undefined-condition", + + /// The recipient or server understood the request but was not + /// expecting it at this time (e.g., the request was out of order); the + /// associated error type SHOULD be "wait" or "modify". + UnexpectedRequest => "unexpected-request", + } +); pub type Lang = String; +/// The representation of a stanza error. #[derive(Debug, Clone)] pub struct StanzaError { + /// The type of this error. pub type_: ErrorType, + + /// The JID of the entity who set this error. pub by: Option, + + /// One of the defined conditions for this error to happen. pub defined_condition: DefinedCondition, + + /// Human-readable description of this error. pub texts: BTreeMap, + + /// A protocol-specific extension for this error. pub other: Option, } From f3366b94bbc43f109b8d02b6871eae75cdc55add Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Sep 2018 20:28:29 +0200 Subject: [PATCH 519/698] stanza_error: Fix compilation error due to an extra pub. --- src/stanza_error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 6b2cff9baa8e2fed158dede225ef643e0ddb03cc..edec4d5bd6fdb0853ffe9cb19a9372f4774764be 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -191,7 +191,7 @@ generate_element_enum!( } ); -pub type Lang = String; +type Lang = String; /// The representation of a stanza error. #[derive(Debug, Clone)] From 709666bb911b93e73d095e7122921f891f1eacff Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Sep 2018 20:28:50 +0200 Subject: [PATCH 520/698] jingle: Document most of this module. --- src/jingle.rs | 255 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 203 insertions(+), 52 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index 264aa1820bef2b935c7df7e7c82b4f599718b2b6..dab53712a34aa59ecee96a0f37668704da766710 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -16,64 +16,170 @@ use error::Error; use ns; use iq::IqSetPayload; -generate_attribute!(Action, "action", { - ContentAccept => "content-accept", - ContentAdd => "content-add", - ContentModify => "content-modify", - ContentReject => "content-reject", - ContentRemove => "content-remove", - DescriptionInfo => "description-info", - SecurityInfo => "security-info", - SessionAccept => "session-accept", - SessionInfo => "session-info", - SessionInitiate => "session-initiate", - SessionTerminate => "session-terminate", - TransportAccept => "transport-accept", - TransportInfo => "transport-info", - TransportReject => "transport-reject", - TransportReplace => "transport-replace", -}); - -generate_attribute!(Creator, "creator", { - Initiator => "initiator", - Responder => "responder", -}); - -generate_attribute!(Senders, "senders", { - Both => "both", - Initiator => "initiator", - None => "none", - Responder => "responder", -}, Default = Both); - -// From https://www.iana.org/assignments/cont-disp/cont-disp.xhtml -generate_attribute!(Disposition, "disposition", { - Inline => "inline", - Attachment => "attachment", - FormData => "form-data", - Signal => "signal", - Alert => "alert", - Icon => "icon", - Render => "render", - RecipientListHistory => "recipient-list-history", - Session => "session", - Aib => "aib", - EarlySession => "early-session", - RecipientList => "recipient-list", - Notification => "notification", - ByReference => "by-reference", - InfoPackage => "info-package", - RecordingSession => "recording-session", -}, Default = Session); - -generate_id!(ContentId); +generate_attribute!( + /// The action attribute. + Action, "action", { + /// Accept a content-add action received from another party. + ContentAccept => "content-accept", + + /// Add one or more new content definitions to the session. + ContentAdd => "content-add", + + /// Change the directionality of media sending. + ContentModify => "content-modify", + + /// Reject a content-add action received from another party. + ContentReject => "content-reject", + + /// Remove one or more content definitions from the session. + ContentRemove => "content-remove", + + /// Exchange information about parameters for an application type. + DescriptionInfo => "description-info", + + /// Exchange information about security preconditions. + SecurityInfo => "security-info", + + /// Definitively accept a session negotiation. + SessionAccept => "session-accept", + + /// Send session-level information, such as a ping or a ringing message. + SessionInfo => "session-info", + + /// Request negotiation of a new Jingle session. + SessionInitiate => "session-initiate", + + /// End an existing session. + SessionTerminate => "session-terminate", + + /// Accept a transport-replace action received from another party. + TransportAccept => "transport-accept", + + /// Exchange transport candidates. + TransportInfo => "transport-info", + + /// Reject a transport-replace action received from another party. + TransportReject => "transport-reject", + + /// Redefine a transport method or replace it with a different method. + TransportReplace => "transport-replace", + } +); + +generate_attribute!( + /// Which party originally generated the content type. + Creator, "creator", { + /// This content was created by the initiator of this session. + Initiator => "initiator", + + /// This content was created by the responder of this session. + Responder => "responder", + } +); + +generate_attribute!( + /// Which parties in the session will be generating content. + Senders, "senders", { + /// Both parties can send for this content. + Both => "both", + + /// Only the initiator can send for this content. + Initiator => "initiator", + + /// No one can send for this content. + None => "none", + + /// Only the responder can send for this content. + Responder => "responder", + }, Default = Both +); + +generate_attribute!( + /// How the content definition is to be interpreted by the recipient. The + /// meaning of this attribute matches the "Content-Disposition" header as + /// defined in RFC 2183 and applied to SIP by RFC 3261. + /// + /// Possible values are defined here: + /// https://www.iana.org/assignments/cont-disp/cont-disp.xhtml + Disposition, "disposition", { + /// Displayed automatically. + Inline => "inline", + + /// User controlled display. + Attachment => "attachment", + + /// Process as form response. + FormData => "form-data", + + /// Tunneled content to be processed silently. + Signal => "signal", + + /// The body is a custom ring tone to alert the user. + Alert => "alert", + + /// The body is displayed as an icon to the user. + Icon => "icon", + + /// The body should be displayed to the user. + Render => "render", + + /// The body contains a list of URIs that indicates the recipients of + /// the request. + RecipientListHistory => "recipient-list-history", + + /// The body describes a communications session, for example, an + /// RFC2327 SDP body. + Session => "session", + + /// Authenticated Identity Body. + Aib => "aib", + + /// The body describes an early communications session, for example, + /// and [RFC2327] SDP body. + EarlySession => "early-session", + + /// The body includes a list of URIs to which URI-list services are to + /// be applied. + RecipientList => "recipient-list", + + /// The payload of the message carrying this Content-Disposition header + /// field value is an Instant Message Disposition Notification as + /// requested in the corresponding Instant Message. + Notification => "notification", + + /// The body needs to be handled according to a reference to the body + /// that is located in the same SIP message as the body. + ByReference => "by-reference", + + /// The body contains information associated with an Info Package. + InfoPackage => "info-package", + + /// The body describes either metadata about the RS or the reason for + /// the metadata snapshot request as determined by the MIME value + /// indicated in the Content-Type. + RecordingSession => "recording-session", + }, Default = Session +); + +generate_id!( + /// An unique identifier in a session, referencing a + /// [struct.Content.html](Content element). + ContentId +); generate_element!( Content, "content", JINGLE, attributes: [ + /// Who created this content. creator: Creator = "creator" => required, + + /// How the content definition is to be interpreted by the recipient. disposition: Disposition = "disposition" => default, + + /// A per-session unique identifier for this content. name: ContentId = "name" => required, + + /// Who can send data for this content. senders: Senders = "senders" => default ], children: [ @@ -124,22 +230,64 @@ impl Content { #[derive(Debug, Clone, PartialEq)] pub enum Reason { + /// The party prefers to use an existing session with the peer rather than + /// initiate a new session; the Jingle session ID of the alternative + /// session SHOULD be provided as the XML character data of the + /// child. AlternativeSession, //(String), + + /// The party is busy and cannot accept a session. Busy, + + /// The initiator wishes to formally cancel the session initiation request. Cancel, + + /// The action is related to connectivity problems. ConnectivityError, + + /// The party wishes to formally decline the session. Decline, + + /// The session length has exceeded a pre-defined time limit (e.g., a + /// meeting hosted at a conference service). Expired, + + /// The party has been unable to initialize processing related to the + /// application type. FailedApplication, + + /// The party has been unable to establish connectivity for the transport + /// method. FailedTransport, + + /// The action is related to a non-specific application error. GeneralError, + + /// The entity is going offline or is no longer available. Gone, + + /// The party supports the offered application type but does not support + /// the offered or negotiated parameters. IncompatibleParameters, + + /// The action is related to media processing problems. MediaError, + + /// The action is related to a violation of local security policies. SecurityError, + + /// The action is generated during the normal course of state management + /// and does not reflect any error. Success, + + /// A request has not been answered so the sender is timing out the + /// request. Timeout, + + /// The party supports none of the offered application types. UnsupportedApplications, + + /// The party supports none of the offered transport methods. UnsupportedTransports, } @@ -244,7 +392,10 @@ impl From for Element { } } -generate_id!(SessionId); +generate_id!( + /// Unique identifier for a session between two JIDs. + SessionId +); #[derive(Debug, Clone)] pub struct Jingle { From e41de29d9d9bb6a77cc740164bbcab41e42f9e0c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Sep 2018 20:51:48 +0200 Subject: [PATCH 521/698] presence: Make PresencePayload a trait, and implement it on every payload. --- src/caps.rs | 3 +++ src/delay.rs | 3 +++ src/ecaps2.rs | 3 +++ src/idle.rs | 3 +++ src/muc/muc.rs | 3 +++ src/presence.rs | 63 ++------------------------------------------- src/stanza_error.rs | 3 +++ 7 files changed, 20 insertions(+), 61 deletions(-) diff --git a/src/caps.rs b/src/caps.rs index a63ab7f736e9759d3e45603b91fafa2b827b545b..77901ba165ad9aa2b0e828aa71fb8593e410d33b 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -6,6 +6,7 @@ use try_from::TryFrom; +use presence::PresencePayload; use disco::{Feature, Identity, DiscoInfoResult, DiscoInfoQuery}; use data_forms::DataForm; use hashes::{Hash, Algo}; @@ -39,6 +40,8 @@ pub struct Caps { pub hash: Hash, } +impl PresencePayload for Caps {} + impl TryFrom for Caps { type Err = Error; diff --git a/src/delay.rs b/src/delay.rs index 21450da0382225a7d2b950434087ec4ba45e6ced..cd0a85f2b9616bbe1e60511eab28283023995f8b 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -4,6 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use presence::PresencePayload; use date::DateTime; use jid::Jid; @@ -26,6 +27,8 @@ generate_element!( ) ); +impl PresencePayload for Delay {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 4e40484d581f4148f082d5e946afe3938d698099..82ab1c4adfac58d1b293c0313f23e9430835b0f8 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -4,6 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use presence::PresencePayload; use disco::{Feature, Identity, DiscoInfoResult, DiscoInfoQuery}; use data_forms::DataForm; use hashes::{Hash, Algo}; @@ -27,6 +28,8 @@ generate_element!( ] ); +impl PresencePayload for ECaps2 {} + fn compute_item(field: &str) -> Vec { let mut bytes = field.as_bytes().to_vec(); bytes.push(0x1f); diff --git a/src/idle.rs b/src/idle.rs index f96dadb5e5047e5d6ee97972a1c5d794838e438e..2704654735b03784db8855fa558a4f7aa9e548e6 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -4,6 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use presence::PresencePayload; use date::DateTime; generate_element!( @@ -15,6 +16,8 @@ generate_element!( ] ); +impl PresencePayload for Idle {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 4d81b85c2a57776b741f7524769df4942b8e1f34..c363107a4f3f818a2293fa3a565c0700f9f0776d 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -5,6 +5,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use presence::PresencePayload; use date::DateTime; generate_element!( @@ -36,6 +37,8 @@ generate_element!( ] ); +impl PresencePayload for Muc {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/presence.rs b/src/presence.rs index 639bf04632c5efc58c340c4036d054f300bfa4e3..e81358fb8853fa83d3e379b93ade6a3467e27678 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -19,12 +19,8 @@ use error::Error; use ns; -use stanza_error::StanzaError; -use muc::Muc; -use caps::Caps; -use delay::Delay; -use idle::Idle; -use ecaps2::ECaps2; +/// Should be implemented on every known payload of a ``. +pub trait PresencePayload: TryFrom + Into {} #[derive(Debug, Clone, PartialEq)] pub enum Show { @@ -79,61 +75,6 @@ pub type Status = String; pub type Priority = i8; -/// Lists every known payload of a ``. -#[derive(Debug, Clone)] -pub enum PresencePayload { - StanzaError(StanzaError), - Muc(Muc), - Caps(Caps), - Delay(Delay), - Idle(Idle), - ECaps2(ECaps2), - - Unknown(Element), -} - -impl TryFrom for PresencePayload { - type Err = Error; - - fn try_from(elem: Element) -> Result { - Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { - ("error", ns::DEFAULT_NS) => PresencePayload::StanzaError(StanzaError::try_from(elem)?), - - // XEP-0045 - ("x", ns::MUC) => PresencePayload::Muc(Muc::try_from(elem)?), - - // XEP-0115 - ("c", ns::CAPS) => PresencePayload::Caps(Caps::try_from(elem)?), - - // XEP-0203 - ("delay", ns::DELAY) => PresencePayload::Delay(Delay::try_from(elem)?), - - // XEP-0319 - ("idle", ns::IDLE) => PresencePayload::Idle(Idle::try_from(elem)?), - - // XEP-0390 - ("c", ns::ECAPS2) => PresencePayload::ECaps2(ECaps2::try_from(elem)?), - - _ => PresencePayload::Unknown(elem), - }) - } -} - -impl From for Element { - fn from(payload: PresencePayload) -> Element { - match payload { - PresencePayload::StanzaError(stanza_error) => stanza_error.into(), - PresencePayload::Muc(muc) => muc.into(), - PresencePayload::Caps(caps) => caps.into(), - PresencePayload::Delay(delay) => delay.into(), - PresencePayload::Idle(idle) => idle.into(), - PresencePayload::ECaps2(ecaps2) => ecaps2.into(), - - PresencePayload::Unknown(elem) => elem, - } - } -} - #[derive(Debug, Clone, PartialEq)] pub enum Type { /// This value is not an acceptable 'type' attribute, it is only used diff --git a/src/stanza_error.rs b/src/stanza_error.rs index edec4d5bd6fdb0853ffe9cb19a9372f4774764be..be2766144081942a90d7ffce153e2e457423b34e 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -9,6 +9,7 @@ use std::collections::BTreeMap; use minidom::Element; +use presence::PresencePayload; use error::Error; use jid::Jid; use ns; @@ -212,6 +213,8 @@ pub struct StanzaError { pub other: Option, } +impl PresencePayload for StanzaError {} + impl TryFrom for StanzaError { type Err = Error; From 07cccad356365a521dd57be065674814a516cf4d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Sep 2018 20:58:27 +0200 Subject: [PATCH 522/698] message: Make MessagePayload a trait, and implement it on every payload. --- src/attention.rs | 4 ++ src/chatstates.rs | 4 ++ src/delay.rs | 2 + src/eme.rs | 4 ++ src/mam.rs | 3 ++ src/message.rs | 91 +----------------------------------------- src/message_correct.rs | 4 ++ src/receipts.rs | 6 +++ src/stanza_error.rs | 2 + src/stanza_id.rs | 5 +++ 10 files changed, 36 insertions(+), 89 deletions(-) diff --git a/src/attention.rs b/src/attention.rs index 7b4226fb974e1b57e63c3278b01eabd5d0741ce5..398b2bfd2f3424189dca01d8875bfe0ea8025e7e 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -4,11 +4,15 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use message::MessagePayload; + generate_empty_element!( /// Requests the attention of the recipient. Attention, "attention", ATTENTION ); +impl MessagePayload for Attention {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/chatstates.rs b/src/chatstates.rs index 339ed2b534a094d5aa7e8bbdc527ea577c7e6008..c9fabd5264285d188ce3752cab97777bdd1d4454 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use message::MessagePayload; + generate_element_enum!( /// Enum representing chatstate elements part of the /// `http://jabber.org/protocol/chatstates` namespace. @@ -25,6 +27,8 @@ generate_element_enum!( } ); +impl MessagePayload for ChatState {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/delay.rs b/src/delay.rs index cd0a85f2b9616bbe1e60511eab28283023995f8b..581e72e26184428f4320bb80429fa4f55f481d43 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -4,6 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use message::MessagePayload; use presence::PresencePayload; use date::DateTime; @@ -27,6 +28,7 @@ generate_element!( ) ); +impl MessagePayload for Delay {} impl PresencePayload for Delay {} #[cfg(test)] diff --git a/src/eme.rs b/src/eme.rs index a276ba25e13a6fd683d5b29d66127d613e790301..f596dc8f8b4c17751121a364bc09d5ccc1cc84e7 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use message::MessagePayload; + generate_element!( /// Structure representing an `` element. ExplicitMessageEncryption, "encryption", EME, @@ -16,6 +18,8 @@ attributes: [ name: Option = "name" => optional, ]); +impl MessagePayload for ExplicitMessageEncryption {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/mam.rs b/src/mam.rs index ad4eb92e7ad28334ab429da45073b007fc9deb00..11850a29177e557c2a00d5c0c6ca11bdf237cb8b 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -11,6 +11,7 @@ use jid::Jid; use error::Error; +use message::MessagePayload; use iq::{IqGetPayload, IqSetPayload, IqResultPayload}; use data_forms::DataForm; use rsm::{SetQuery, SetResult}; @@ -65,6 +66,8 @@ generate_element!( ] ); +impl MessagePayload for Result_ {} + generate_attribute!( /// True when the end of a MAM query has been reached. Complete, "complete", bool diff --git a/src/message.rs b/src/message.rs index 7600d1049ad81567b76c027dfa35f970a645c42c..dab55a28a12c4ee5ec7cec0a12d9116ad8f9da37 100644 --- a/src/message.rs +++ b/src/message.rs @@ -17,95 +17,8 @@ use error::Error; use ns; -use stanza_error::StanzaError; -use chatstates::ChatState; -use receipts::{Request as ReceiptRequest, Received as ReceiptReceived}; -use delay::Delay; -use attention::Attention; -use message_correct::Replace; -use eme::ExplicitMessageEncryption; -use stanza_id::{StanzaId, OriginId}; -use mam::Result_ as MamResult; - -/// Lists every known payload of a ``. -#[derive(Debug, Clone)] -pub enum MessagePayload { - StanzaError(StanzaError), - ChatState(ChatState), - ReceiptRequest(ReceiptRequest), - ReceiptReceived(ReceiptReceived), - Delay(Delay), - Attention(Attention), - MessageCorrect(Replace), - ExplicitMessageEncryption(ExplicitMessageEncryption), - StanzaId(StanzaId), - OriginId(OriginId), - MamResult(MamResult), - - Unknown(Element), -} - -impl TryFrom for MessagePayload { - type Err = Error; - - fn try_from(elem: Element) -> Result { - Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { - ("error", ns::DEFAULT_NS) => MessagePayload::StanzaError(StanzaError::try_from(elem)?), - - // XEP-0085 - ("active", ns::CHATSTATES) - | ("inactive", ns::CHATSTATES) - | ("composing", ns::CHATSTATES) - | ("paused", ns::CHATSTATES) - | ("gone", ns::CHATSTATES) => MessagePayload::ChatState(ChatState::try_from(elem)?), - - // XEP-0184 - ("request", ns::RECEIPTS) => MessagePayload::ReceiptRequest(ReceiptRequest::try_from(elem)?), - ("received", ns::RECEIPTS) => MessagePayload::ReceiptReceived(ReceiptReceived::try_from(elem)?), - - // XEP-0203 - ("delay", ns::DELAY) => MessagePayload::Delay(Delay::try_from(elem)?), - - // XEP-0224 - ("attention", ns::ATTENTION) => MessagePayload::Attention(Attention::try_from(elem)?), - - // XEP-0308 - ("replace", ns::MESSAGE_CORRECT) => MessagePayload::MessageCorrect(Replace::try_from(elem)?), - - // XEP-0313 - ("result", ns::MAM) => MessagePayload::MamResult(MamResult::try_from(elem)?), - - // XEP-0359 - ("stanza-id", ns::SID) => MessagePayload::StanzaId(StanzaId::try_from(elem)?), - ("origin-id", ns::SID) => MessagePayload::OriginId(OriginId::try_from(elem)?), - - // XEP-0380 - ("encryption", ns::EME) => MessagePayload::ExplicitMessageEncryption(ExplicitMessageEncryption::try_from(elem)?), - - _ => MessagePayload::Unknown(elem), - }) - } -} - -impl From for Element { - fn from(payload: MessagePayload) -> Element { - match payload { - MessagePayload::StanzaError(stanza_error) => stanza_error.into(), - MessagePayload::Attention(attention) => attention.into(), - MessagePayload::ChatState(chatstate) => chatstate.into(), - MessagePayload::ReceiptRequest(request) => request.into(), - MessagePayload::ReceiptReceived(received) => received.into(), - MessagePayload::Delay(delay) => delay.into(), - MessagePayload::MessageCorrect(replace) => replace.into(), - MessagePayload::ExplicitMessageEncryption(eme) => eme.into(), - MessagePayload::StanzaId(stanza_id) => stanza_id.into(), - MessagePayload::OriginId(origin_id) => origin_id.into(), - MessagePayload::MamResult(result) => result.into(), - - MessagePayload::Unknown(elem) => elem, - } - } -} +/// Should be implemented on every known payload of a ``. +pub trait MessagePayload: TryFrom + Into {} generate_attribute!( /// The type of a message. diff --git a/src/message_correct.rs b/src/message_correct.rs index 6ff06bc3f1242b138cb71268624005015d3f39d7..04834518d8cb01f37ed43a8bcc014d0e82408888 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use message::MessagePayload; + generate_element!( /// Defines that the message containing this payload should replace a /// previous message, identified by the id. @@ -14,6 +16,8 @@ generate_element!( ] ); +impl MessagePayload for Replace {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/receipts.rs b/src/receipts.rs index 10c0e51c64b23b2b4753fdc1ca2133d4b6bf01fe..898a5dd65cc351801f9c828d90f8764c2b1f291f 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -4,12 +4,16 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use message::MessagePayload; + generate_empty_element!( /// Requests that this message is acked by the final recipient once /// received. Request, "request", RECEIPTS ); +impl MessagePayload for Request {} + generate_element!( /// Notes that a previous message has correctly been received, it is /// referenced by its 'id' attribute. @@ -20,6 +24,8 @@ generate_element!( ] ); +impl MessagePayload for Received {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/stanza_error.rs b/src/stanza_error.rs index be2766144081942a90d7ffce153e2e457423b34e..37e71be0209127d278183f57cd60cfb8d45cdac0 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -9,6 +9,7 @@ use std::collections::BTreeMap; use minidom::Element; +use message::MessagePayload; use presence::PresencePayload; use error::Error; use jid::Jid; @@ -213,6 +214,7 @@ pub struct StanzaError { pub other: Option, } +impl MessagePayload for StanzaError {} impl PresencePayload for StanzaError {} impl TryFrom for StanzaError { diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 07815107c6c9738920e238a0c888cea8a9fe557c..07e947a3e403e1fba0cc2130e4e1755d1f9fc860 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -4,6 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use message::MessagePayload; use jid::Jid; generate_element!( @@ -19,6 +20,8 @@ generate_element!( ] ); +impl MessagePayload for StanzaId {} + generate_element!( /// A hack for MUC before version 1.31 to track a message which may have /// its 'id' attribute changed. @@ -29,6 +32,8 @@ generate_element!( ] ); +impl MessagePayload for OriginId {} + #[cfg(test)] mod tests { use super::*; From fbe22e6db17e65b5e20a9d7a0efcb51c6c4c7145 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Sep 2018 20:58:53 +0200 Subject: [PATCH 523/698] eme: Fix indentation. --- src/eme.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/eme.rs b/src/eme.rs index f596dc8f8b4c17751121a364bc09d5ccc1cc84e7..51eb2a6d3483135fe0977a26e597a695a3e1e12f 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -7,16 +7,17 @@ use message::MessagePayload; generate_element!( -/// Structure representing an `` element. -ExplicitMessageEncryption, "encryption", EME, -attributes: [ - /// Namespace of the encryption scheme used. - namespace: String = "namespace" => required, + /// Structure representing an `` element. + ExplicitMessageEncryption, "encryption", EME, + attributes: [ + /// Namespace of the encryption scheme used. + namespace: String = "namespace" => required, - /// User-friendly name for the encryption scheme, should be `None` for OTR, - /// legacy OpenPGP and OX. - name: Option = "name" => optional, -]); + /// User-friendly name for the encryption scheme, should be `None` for OTR, + /// legacy OpenPGP and OX. + name: Option = "name" => optional, + ] +); impl MessagePayload for ExplicitMessageEncryption {} From c508275f40d7cc80fb80d7e3b8a8a963d9c760d1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Sep 2018 21:08:46 +0200 Subject: [PATCH 524/698] presence: Finish to document this module. --- src/presence.rs | 64 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index e81358fb8853fa83d3e379b93ade6a3467e27678..8bde17801226156970b6065ebdb4f546632412d3 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -5,8 +5,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -#![allow(missing_docs)] - use try_from::TryFrom; use std::str::FromStr; use std::collections::BTreeMap; @@ -22,12 +20,23 @@ use ns; /// Should be implemented on every known payload of a ``. pub trait PresencePayload: TryFrom + Into {} +/// Specifies the availability of an entity or resource. #[derive(Debug, Clone, PartialEq)] pub enum Show { + /// Not an actual show value, but an indication there is no show set. None, + + /// The entity or resource is temporarily away. Away, + + /// The entity or resource is actively interested in chatting. Chat, + + /// The entity or resource is busy (dnd = "Do Not Disturb"). Dnd, + + /// The entity or resource is away for an extended period (xa = "eXtended + /// Away"). Xa, } @@ -70,22 +79,41 @@ impl IntoElements for Show { } } -pub type Lang = String; -pub type Status = String; +type Lang = String; +type Status = String; -pub type Priority = i8; +type Priority = i8; +/// #[derive(Debug, Clone, PartialEq)] pub enum Type { /// This value is not an acceptable 'type' attribute, it is only used /// internally to signal the absence of 'type'. None, + + /// An error has occurred regarding processing of a previously sent + /// presence stanza; if the presence stanza is of type "error", it MUST + /// include an child element (refer to [XMPP‑CORE]). Error, + + /// A request for an entity's current presence; SHOULD be generated only by + /// a server on behalf of a user. Probe, + + /// The sender wishes to subscribe to the recipient's presence. Subscribe, + + /// The sender has allowed the recipient to receive their presence. Subscribed, + + /// The sender is no longer available for communication. Unavailable, + + /// The sender is unsubscribing from the receiver's presence. Unsubscribe, + + /// The subscription request has been denied or a previously granted + /// subscription has been canceled. Unsubscribed, } @@ -132,17 +160,34 @@ impl IntoAttributeValue for Type { /// The main structure representing the `` stanza. #[derive(Debug, Clone)] pub struct Presence { + /// The sender of this presence. pub from: Option, + + /// The recipient of this presence. pub to: Option, + + /// The identifier, unique on this stream, of this stanza. pub id: Option, + + /// The type of this presence stanza. pub type_: Type, + + /// The availability of the sender of this presence. pub show: Show, + + /// A localised list of statuses defined in this presence. pub statuses: BTreeMap, + + /// The sender’s resource priority, if negative it won’t receive messages + /// that haven’t been directed to it. pub priority: Priority, + + /// A list of payloads contained in this presence. pub payloads: Vec, } impl Presence { + /// Create a new presence of this type. pub fn new(type_: Type) -> Presence { Presence { from: None, @@ -156,31 +201,40 @@ impl Presence { } } + /// Set the emitter of this presence, this should only be useful for + /// servers and components, as clients can only send presences from their + /// own resource (which is implicit). pub fn with_from(mut self, from: Option) -> Presence { self.from = from; self } + /// Set the recipient of this presence, this is only useful for directed + /// presences. pub fn with_to(mut self, to: Option) -> Presence { self.to = to; self } + /// Set the identifier for this presence. pub fn with_id(mut self, id: Option) -> Presence { self.id = id; self } + /// Set the availability information of this presence. pub fn with_show(mut self, show: Show) -> Presence { self.show = show; self } + /// Set the priority of this presence. pub fn with_priority(mut self, priority: i8) -> Presence { self.priority = priority; self } + /// Set the payloads of this presence. pub fn with_payloads(mut self, payloads: Vec) -> Presence { self.payloads = payloads; self From 5582a48b4e49f74ab3b5e6024cae9c7da31e635f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Sep 2018 21:09:05 +0200 Subject: [PATCH 525/698] message: Everything is already documented, remove the allow(missing_docs). --- src/message.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/message.rs b/src/message.rs index dab55a28a12c4ee5ec7cec0a12d9116ad8f9da37..7506a3fa513b612b3afdddbf8b010dd48078fa02 100644 --- a/src/message.rs +++ b/src/message.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -#![allow(missing_docs)] - use try_from::TryFrom; use std::collections::BTreeMap; From 0da5639be521a26a346bc6f768912164bda9d1ff Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Sep 2018 21:15:50 +0200 Subject: [PATCH 526/698] jingle: Document this module. --- src/jingle.rs | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index dab53712a34aa59ecee96a0f37668704da766710..3102b1052abc3c3287f4713e4310cb9f83f3ba34 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -#![allow(missing_docs)] - use try_from::TryFrom; use std::str::FromStr; @@ -168,6 +166,8 @@ generate_id!( ); generate_element!( + /// Describes a session’s content, there can be multiple content in one + /// session. Content, "content", JINGLE, attributes: [ /// Who created this content. @@ -183,13 +183,19 @@ generate_element!( senders: Senders = "senders" => default ], children: [ + /// What to send. description: Option = ("description", JINGLE) => Element, + + /// How to send it. transport: Option = ("transport", JINGLE) => Element, + + /// With which security. security: Option = ("security", JINGLE) => Element ] ); impl Content { + /// Create a new content. pub fn new(creator: Creator, name: ContentId) -> Content { Content { creator, @@ -202,32 +208,38 @@ impl Content { } } + /// Set how the content is to be interpreted by the recipient. pub fn with_disposition(mut self, disposition: Disposition) -> Content { self.disposition = disposition; self } + /// Specify who can send data for this content. pub fn with_senders(mut self, senders: Senders) -> Content { self.senders = senders; self } + /// Set the description of this content. pub fn with_description(mut self, description: Element) -> Content { self.description = Some(description); self } + /// Set the transport of this content. pub fn with_transport(mut self, transport: Element) -> Content { self.transport = Some(transport); self } + /// Set the security of this content. pub fn with_security(mut self, security: Element) -> Content { self.security = Some(security); self } } +/// Lists the possible reasons to be included in a Jingle iq. #[derive(Debug, Clone, PartialEq)] pub enum Reason { /// The party prefers to use an existing session with the peer rather than @@ -343,9 +355,13 @@ impl From for Element { } } +/// Informs the recipient of something. #[derive(Debug, Clone)] pub struct ReasonElement { + /// The list of possible reasons to be included in a Jingle iq. pub reason: Reason, + + /// A human-readable description of this reason. pub text: Option, } @@ -397,20 +413,35 @@ generate_id!( SessionId ); +/// The main Jingle container, to be included in an iq stanza. #[derive(Debug, Clone)] pub struct Jingle { + /// The action to execute on both ends. pub action: Action, + + /// Who the initiator is. pub initiator: Option, + + /// Who the responder is. pub responder: Option, + + /// Unique session identifier between two entities. pub sid: SessionId, + + /// A list of contents to be negociated in this session. pub contents: Vec, + + /// An optional reason. pub reason: Option, + + /// Payloads to be included. pub other: Vec, } impl IqSetPayload for Jingle {} impl Jingle { + /// Create a new Jingle element. pub fn new(action: Action, sid: SessionId) -> Jingle { Jingle { action: action, @@ -423,21 +454,25 @@ impl Jingle { } } + /// Set the initiator’s JID. pub fn with_initiator(mut self, initiator: Jid) -> Jingle { self.initiator = Some(initiator); self } + /// Set the responder’s JID. pub fn with_responder(mut self, responder: Jid) -> Jingle { self.responder = Some(responder); self } + /// Add a content to this Jingle container. pub fn add_content(mut self, content: Content) -> Jingle { self.contents.push(content); self } + /// Set the reason in this Jingle container. pub fn set_reason(mut self, content: Content) -> Jingle { self.contents.push(content); self From 2878b0c5465b1155d2b92c1f71e8bf1a32e34e2c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Sep 2018 21:20:02 +0200 Subject: [PATCH 527/698] ChangeLog: Add imminent version 0.11.1. --- ChangeLog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ChangeLog b/ChangeLog index 1827f093c51ab54210dd88dcccd83cd07a17cff1..ffe9b95f53389a7bd5aaaa98599a07f9c57dde39 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Version 0.11.1: +2018-09-20 Emmanuel Gil Peyrot + * Improvements: + - Document all of the modules. + Version 0.11.0: 2018-08-03 Emmanuel Gil Peyrot * Breaking changes: From 4567b66d26ad0a116e9445ebe6fa38ed44d5a1de Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Sep 2018 21:20:18 +0200 Subject: [PATCH 528/698] Cargo.toml: Release version 0.11.1. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 2c2a34f947583b9d640f073e4d395d6b9a5c3bf6..2e5bba5f6352d271ff149f7f224374f0c9092662 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.11.0" +version = "0.11.1" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", From 9cb4f003418ae56d0db0c148a6309167dfe5c7d9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 12 Oct 2018 17:23:34 +0200 Subject: [PATCH 530/698] =?UTF-8?q?caps,=20ecaps2:=20Update=20to=20RustCry?= =?UTF-8?q?pto=C2=A00.8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 10 +++++----- src/caps.rs | 16 ++++++---------- src/ecaps2.rs | 16 ++++++---------- 3 files changed, 17 insertions(+), 25 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2e5bba5f6352d271ff149f7f224374f0c9092662..a438baec39d7e80575db8297beac0a521c737301 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,11 +16,11 @@ license = "MPL-2.0" minidom = "0.9.1" jid = { version = "0.5.2", features = ["minidom"] } base64 = "0.9.2" -digest = "0.7.5" -sha-1 = "0.7.0" -sha2 = "0.7.1" -sha3 = "0.7.3" -blake2 = "0.7.1" +digest = "0.8" +sha-1 = "0.8" +sha2 = "0.8" +sha3 = "0.8" +blake2 = "0.8" chrono = "0.4.5" try_from = "0.2.2" diff --git a/src/caps.rs b/src/caps.rs index 77901ba165ad9aa2b0e828aa71fb8593e410d33b..25c851fbaba2d0906840d6711fc5a229f7ab2245 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -19,8 +19,8 @@ use base64; use sha1::Sha1; use sha2::{Sha256, Sha512}; use sha3::{Sha3_256, Sha3_512}; -use blake2::Blake2b; -use digest::{Digest, VariableOutput}; +use blake2::VarBlake2b; +use digest::{Digest, VariableOutput, Input}; /// Represents a capability hash for a given client. #[derive(Debug, Clone)] @@ -182,18 +182,14 @@ pub fn hash_caps(data: &[u8], algo: Algo) -> Result { get_hash_vec(hash.as_slice()) }, Algo::Blake2b_256 => { - let mut hasher = Blake2b::default(); + let mut hasher = VarBlake2b::new(32).unwrap(); hasher.input(data); - let mut buf: [u8; 32] = [0; 32]; - let hash = hasher.variable_result(&mut buf).unwrap(); - get_hash_vec(hash) + hasher.vec_result() }, Algo::Blake2b_512 => { - let mut hasher = Blake2b::default(); + let mut hasher = VarBlake2b::new(64).unwrap(); hasher.input(data); - let mut buf: [u8; 64] = [0; 64]; - let hash = hasher.variable_result(&mut buf).unwrap(); - get_hash_vec(hash) + hasher.vec_result() }, Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)), }, diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 82ab1c4adfac58d1b293c0313f23e9430835b0f8..6e71040d51c86afc85f50b3d511f4ddfc2241118 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -14,8 +14,8 @@ use base64; use sha2::{Sha256, Sha512}; use sha3::{Sha3_256, Sha3_512}; -use blake2::Blake2b; -use digest::{Digest, VariableOutput}; +use blake2::VarBlake2b; +use digest::{Digest, VariableOutput, Input}; generate_element!( /// Represents a set of capability hashes, all of them must correspond to @@ -121,18 +121,14 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { get_hash_vec(hash.as_slice()) }, Algo::Blake2b_256 => { - let mut hasher = Blake2b::default(); + let mut hasher = VarBlake2b::new(32).unwrap(); hasher.input(data); - let mut buf: [u8; 32] = [0; 32]; - let hash = hasher.variable_result(&mut buf).unwrap(); - get_hash_vec(hash) + hasher.vec_result() }, Algo::Blake2b_512 => { - let mut hasher = Blake2b::default(); + let mut hasher = VarBlake2b::new(64).unwrap(); hasher.input(data); - let mut buf: [u8; 64] = [0; 64]; - let hash = hasher.variable_result(&mut buf).unwrap(); - get_hash_vec(hash) + hasher.vec_result() }, Algo::Sha_1 => return Err(String::from("Disabled algorithm sha-1: unsafe.")), Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)), From 40aedcf184e5c4f70255f2f2cebbb9e9c9b9dfee Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 26 Oct 2018 14:26:16 +0200 Subject: [PATCH 531/698] Test the size of every struct defined here. --- src/attention.rs | 5 +++++ src/bind.rs | 5 +++++ src/blocking.rs | 8 ++++++++ src/bookmarks.rs | 7 +++++++ src/caps.rs | 5 +++++ src/chatstates.rs | 5 +++++ src/component.rs | 5 +++++ src/data_forms.rs | 9 +++++++++ src/date.rs | 5 +++++ src/delay.rs | 5 +++++ src/disco.rs | 12 ++++++++++++ src/ecaps2.rs | 5 +++++ src/eme.rs | 5 +++++ src/forwarding.rs | 5 +++++ src/hashes.rs | 6 ++++++ src/ibb.rs | 8 ++++++++ src/ibr.rs | 5 +++++ src/idle.rs | 5 +++++ src/iq.rs | 6 ++++++ src/jingle.rs | 14 ++++++++++++++ src/jingle_ft.rs | 9 +++++++++ src/jingle_ibb.rs | 5 +++++ src/jingle_message.rs | 5 +++++ src/jingle_s5b.rs | 11 +++++++++++ src/macros.rs | 6 ++++++ src/mam.rs | 11 +++++++++++ src/media_element.rs | 6 ++++++ src/message.rs | 9 +++++++++ src/message_correct.rs | 5 +++++ src/mood.rs | 6 ++++++ src/nick.rs | 5 +++++ src/ping.rs | 5 +++++ src/presence.rs | 7 +++++++ src/receipts.rs | 6 ++++++ src/roster.rs | 8 ++++++++ src/rsm.rs | 6 ++++++ src/sasl.rs | 12 ++++++++++++ src/sm.rs | 14 ++++++++++++++ src/stanza_error.rs | 7 +++++++ src/stanza_id.rs | 6 ++++++ src/stream.rs | 5 +++++ src/version.rs | 6 ++++++ src/websocket.rs | 5 +++++ 43 files changed, 295 insertions(+) diff --git a/src/attention.rs b/src/attention.rs index 398b2bfd2f3424189dca01d8875bfe0ea8025e7e..4bfffd7eb7fe725943adfbcb1c3289cc330c5b65 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -20,6 +20,11 @@ mod tests { use minidom::Element; use error::Error; + #[test] + fn test_size() { + assert_size!(Attention, 0); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/bind.rs b/src/bind.rs index 1922093b303c8dadcc14337bdac2e903a635a120..6bfa8a310edc4a765fb8cab864dab159eb339fb0 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -98,6 +98,11 @@ impl From for Element { mod tests { use super::*; + #[test] + fn test_size() { + assert_size!(Bind, 80); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/blocking.rs b/src/blocking.rs index 6b6894c9ad5c14e68c1321d7ad4114b8395c94e2..87a2fbd2bcdc002d897bc33a9d969c84ebb6e62c 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -98,6 +98,14 @@ generate_empty_element!( mod tests { use super::*; + #[test] + fn test_size() { + assert_size!(BlocklistRequest, 0); + assert_size!(BlocklistResult, 24); + assert_size!(Block, 24); + assert_size!(Unblock, 24); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/bookmarks.rs b/src/bookmarks.rs index 11b4a42131af17549f18115bf90e1d8beac4da30..8ae0639205358f30feb399b0a9e93750f2e9dacf 100644 --- a/src/bookmarks.rs +++ b/src/bookmarks.rs @@ -74,6 +74,13 @@ mod tests { use minidom::Element; use compare_elements::NamespaceAwareCompare; + #[test] + fn test_size() { + assert_size!(Conference, 152); + assert_size!(Url, 48); + assert_size!(Storage, 48); + } + #[test] fn empty() { let elem: Element = "".parse().unwrap(); diff --git a/src/caps.rs b/src/caps.rs index 25c851fbaba2d0906840d6711fc5a229f7ab2245..7fcaea801731e2ae4f238d3b14cae73c4d67a34d 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -211,6 +211,11 @@ mod tests { use caps; use base64; + #[test] + fn test_size() { + assert_size!(Caps, 104); + } + #[test] fn test_parse() { let elem: Element = "".parse().unwrap(); diff --git a/src/chatstates.rs b/src/chatstates.rs index c9fabd5264285d188ce3752cab97777bdd1d4454..477bdded08d5b9083ac95e3995acb2bde604ea85 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -37,6 +37,11 @@ mod tests { use error::Error; use ns; + #[test] + fn test_size() { + assert_size!(ChatState, 1); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/component.rs b/src/component.rs index 27229f25aaeab1bc8befc999a83a11f5c5561b32..503e103423c05bfff07fcbd9d51289661450c131 100644 --- a/src/component.rs +++ b/src/component.rs @@ -47,6 +47,11 @@ mod tests { use try_from::TryFrom; use minidom::Element; + #[test] + fn test_size() { + assert_size!(Handshake, 24); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/data_forms.rs b/src/data_forms.rs index a43977974eb84b0efbccfbac91893fe19ef142cb..3fccd72622e8983631b495192cd1e93d0dbf11db 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -268,6 +268,15 @@ impl From for Element { mod tests { use super::*; + #[test] + fn test_size() { + assert_size!(Option_, 48); + assert_size!(FieldType, 1); + assert_size!(Field, 128); + assert_size!(DataFormType, 1); + assert_size!(DataForm, 104); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/date.rs b/src/date.rs index a344eb58d97dc4ad297de5fd471811c8594fd2f9..5f2038de7a4576fec17c8a5f1a618bfa3d6343fa 100644 --- a/src/date.rs +++ b/src/date.rs @@ -43,6 +43,11 @@ mod tests { use chrono::{Datelike, Timelike}; use std::error::Error as StdError; + #[test] + fn test_size() { + assert_size!(DateTime, 16); + } + #[test] fn test_simple() { let date: DateTime = "2002-09-10T23:08:25Z".parse().unwrap(); diff --git a/src/delay.rs b/src/delay.rs index 581e72e26184428f4320bb80429fa4f55f481d43..c4f7518f6f85da2fcef3fd698fb770d8f00a5e5d 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -39,6 +39,11 @@ mod tests { use error::Error; use std::str::FromStr; + #[test] + fn test_size() { + assert_size!(Delay, 112); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/disco.rs b/src/disco.rs index 59166eb82bd2d3db50663a287edccbdb5074b9d3..7b435530a6c72bec563000c459fd94c153106413 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -235,6 +235,18 @@ mod tests { use compare_elements::NamespaceAwareCompare; use std::str::FromStr; + #[test] + fn test_size() { + assert_size!(Identity, 96); + assert_size!(Feature, 24); + assert_size!(DiscoInfoQuery, 24); + assert_size!(DiscoInfoResult, 96); + + assert_size!(Item, 120); + assert_size!(DiscoItemsQuery, 24); + assert_size!(DiscoItemsResult, 48); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 6e71040d51c86afc85f50b3d511f4ddfc2241118..d4811b5cc917d1ba59081a25d4dfd218dda80f57 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -152,6 +152,11 @@ mod tests { use minidom::Element; use error::Error; + #[test] + fn test_size() { + assert_size!(ECaps2, 24); + } + #[test] fn test_parse() { let elem: Element = "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=".parse().unwrap(); diff --git a/src/eme.rs b/src/eme.rs index 51eb2a6d3483135fe0977a26e597a695a3e1e12f..271b842e38ae0e24b41727660cb1b689844296fe 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -28,6 +28,11 @@ mod tests { use minidom::Element; use error::Error; + #[test] + fn test_size() { + assert_size!(ExplicitMessageEncryption, 48); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/forwarding.rs b/src/forwarding.rs index b65b450367bf195ed29ce2135fc7f62088792812..fbfacb11ec2caaabc3a7afe5b9579598cedab932 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -30,6 +30,11 @@ mod tests { use minidom::Element; use error::Error; + #[test] + fn test_size() { + assert_size!(Forwarded, 392); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/hashes.rs b/src/hashes.rs index bf7fafbd797d6b2e5b991cecdc335c2ccd430d5c..7063dbc66ff754e8716b974329564581cd29c5e3 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -133,6 +133,12 @@ mod tests { use try_from::TryFrom; use minidom::Element; + #[test] + fn test_size() { + assert_size!(Algo, 32); + assert_size!(Hash, 56); + } + #[test] fn test_simple() { let elem: Element = "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=".parse().unwrap(); diff --git a/src/ibb.rs b/src/ibb.rs index bede2f0b8ba148a57524621f775ca04370e5665b..e595fe4ab6a7709bd0b4d48ba8a82bc22e511e2c 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -76,6 +76,14 @@ mod tests { use error::Error; use std::error::Error as StdError; + #[test] + fn test_size() { + assert_size!(Stanza, 1); + assert_size!(Open, 32); + assert_size!(Data, 56); + assert_size!(Close, 24); + } + #[test] fn test_simple() { let sid = StreamId(String::from("coucou")); diff --git a/src/ibr.rs b/src/ibr.rs index 72324deeec3d7dbe86d7cdce05595a6a3ba29325..71256b642a0de42366eb438d15827e25df6be909 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -96,6 +96,11 @@ mod tests { use super::*; use compare_elements::NamespaceAwareCompare; + #[test] + fn test_size() { + assert_size!(Query, 152); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/idle.rs b/src/idle.rs index 2704654735b03784db8855fa558a4f7aa9e548e6..45352a229a58509a5ed7e6a04d90bf458a9a04f0 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -27,6 +27,11 @@ mod tests { use std::str::FromStr; use std::error::Error as StdError; + #[test] + fn test_size() { + assert_size!(Idle, 16); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/iq.rs b/src/iq.rs index aeeb836cc112ef5904d0196a911fc362038046b3..b5f27bf06a545720576a160abaa9f71e349f6c4f 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -226,6 +226,12 @@ mod tests { use compare_elements::NamespaceAwareCompare; use disco::DiscoInfoQuery; + #[test] + fn test_size() { + assert_size!(IqType, 216); + assert_size!(Iq, 384); + } + #[test] fn test_require_type() { #[cfg(not(feature = "component"))] diff --git a/src/jingle.rs b/src/jingle.rs index 3102b1052abc3c3287f4713e4310cb9f83f3ba34..38577f8415e86fc9fe1591a1a97eb91119e9fc10 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -533,6 +533,20 @@ impl From for Element { mod tests { use super::*; + #[test] + fn test_size() { + assert_size!(Action, 1); + assert_size!(Creator, 1); + assert_size!(Senders, 1); + assert_size!(Disposition, 1); + assert_size!(ContentId, 24); + assert_size!(Content, 344); + assert_size!(Reason, 1); + assert_size!(ReasonElement, 32); + assert_size!(SessionId, 24); + assert_size!(Jingle, 256); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index e206ab3c55c8f29b2507dee502cebdcd86dfbb68..0a3c022a1ab6518e459d17e9be26f73ab9bc754e 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -348,6 +348,15 @@ mod tests { use hashes::Algo; use base64; + #[test] + fn test_size() { + assert_size!(Range, 48); + assert_size!(File, 184); + assert_size!(Description, 184); + assert_size!(Checksum, 216); + assert_size!(Received, 32); + } + #[test] fn test_description() { let elem: Element = r#" diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 7438133c56681055a5d52236bb01fe7564324afd..c56c73d490a3907512b668e29a52f2af1477ff7b 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -29,6 +29,11 @@ mod tests { use error::Error; use std::error::Error as StdError; + #[test] + fn test_size() { + assert_size!(Transport, 32); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/jingle_message.rs b/src/jingle_message.rs index d8e30382f61871c1bc00b33e5aa0f5ec32560888..150e7a03134367da32a384d78827147921a1e52c 100644 --- a/src/jingle_message.rs +++ b/src/jingle_message.rs @@ -121,6 +121,11 @@ impl From for Element { mod tests { use super::*; + #[test] + fn test_size() { + assert_size!(JingleMI, 136); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index f6f05be3416394f0e50d0fc4a8d23c0f48c466b9..a5fdff0deb3b63dad66b32a5b6727344819c7a1b 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -277,6 +277,17 @@ mod tests { use std::str::FromStr; use compare_elements::NamespaceAwareCompare; + #[test] + fn test_size() { + assert_size!(Type, 1); + assert_size!(Mode, 1); + assert_size!(CandidateId, 24); + assert_size!(StreamId, 24); + assert_size!(Candidate, 128); + assert_size!(TransportPayload, 32); + assert_size!(Transport, 88); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/macros.rs b/src/macros.rs index 5793fef04ee05c21a427cef9dc101fa621546e01..3deade1d361315e1d3692e5e13a87947798b565d 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -510,3 +510,9 @@ macro_rules! generate_element { } ); } + +macro_rules! assert_size ( + ($t:ty, $sz:expr) => ( + assert_eq!(::std::mem::size_of::<$t>(), $sz); + ); +); diff --git a/src/mam.rs b/src/mam.rs index 11850a29177e557c2a00d5c0c6ca11bdf237cb8b..44564120a727b058a6f58740ecda9e3dbb612ae0 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -191,6 +191,17 @@ mod tests { use super::*; use std::str::FromStr; + #[test] + fn test_size() { + assert_size!(QueryId, 24); + assert_size!(Query, 232); + assert_size!(Result_, 440); + assert_size!(Complete, 1); + assert_size!(Fin, 88); + assert_size!(DefaultPrefs, 1); + assert_size!(Prefs, 56); + } + #[test] fn test_query() { let elem: Element = "".parse().unwrap(); diff --git a/src/media_element.rs b/src/media_element.rs index 7ad264b2251d6c184c380893275081a062a27c1b..722780f2f03ceb12fb0b8dcaa96e9eee362cdb86 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -51,6 +51,12 @@ mod tests { use data_forms::DataForm; use std::error::Error as StdError; + #[test] + fn test_size() { + assert_size!(URI, 48); + assert_size!(MediaElement, 56); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/message.rs b/src/message.rs index 7506a3fa513b612b3afdddbf8b010dd48078fa02..2e9d9c659d9509cfc5da8e71400e82bdf1a12905 100644 --- a/src/message.rs +++ b/src/message.rs @@ -234,6 +234,15 @@ mod tests { use std::str::FromStr; use compare_elements::NamespaceAwareCompare; + #[test] + fn test_size() { + assert_size!(MessageType, 1); + assert_size!(Body, 24); + assert_size!(Subject, 24); + assert_size!(Thread, 24); + assert_size!(Message, 272); + } + #[test] fn test_simple() { #[cfg(not(feature = "component"))] diff --git a/src/message_correct.rs b/src/message_correct.rs index 04834518d8cb01f37ed43a8bcc014d0e82408888..e8233ee632d6b5295e1c180d1e14aff328958e54 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -25,6 +25,11 @@ mod tests { use minidom::Element; use error::Error; + #[test] + fn test_size() { + assert_size!(Replace, 24); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/mood.rs b/src/mood.rs index 8612a42c18caa9511169b73c89ed5d831eaacc9b..6bf8bd2383cf1fce59068131b62872132cd3a205 100644 --- a/src/mood.rs +++ b/src/mood.rs @@ -272,6 +272,12 @@ mod tests { use try_from::TryFrom; use minidom::Element; + #[test] + fn test_size() { + assert_size!(MoodEnum, 1); + assert_size!(Text, 24); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/nick.rs b/src/nick.rs index 9844e9318c8670319e3f032e9717c10d9ffbfade..2d2f90c4c7d212d491c244e4b5aa97a8c752d0e4 100644 --- a/src/nick.rs +++ b/src/nick.rs @@ -16,6 +16,11 @@ mod tests { use minidom::Element; use error::Error; + #[test] + fn test_size() { + assert_size!(Nick, 24); + } + #[test] fn test_simple() { let elem: Element = "Link Mauve".parse().unwrap(); diff --git a/src/ping.rs b/src/ping.rs index 0af81c0da3f2b06cd4a0e68be9c9a7b63820c4c7..557f535a7038ab4417cefed415e5ad22b9c7e7dd 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -22,6 +22,11 @@ mod tests { use minidom::Element; use error::Error; + #[test] + fn test_size() { + assert_size!(Ping, 0); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/presence.rs b/src/presence.rs index 8bde17801226156970b6065ebdb4f546632412d3..02a9a9c7c890c95e6dad4173f70a3fb378c42901 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -323,6 +323,13 @@ mod tests { use super::*; use compare_elements::NamespaceAwareCompare; + #[test] + fn test_size() { + assert_size!(Show, 1); + assert_size!(Type, 1); + assert_size!(Presence, 224); + } + #[test] fn test_simple() { #[cfg(not(feature = "component"))] diff --git a/src/receipts.rs b/src/receipts.rs index 898a5dd65cc351801f9c828d90f8764c2b1f291f..ecf0a21cabe54828889a5dcfba0e002f8765486a 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -33,6 +33,12 @@ mod tests { use minidom::Element; use ns; + #[test] + fn test_size() { + assert_size!(Request, 0); + assert_size!(Received, 24); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/roster.rs b/src/roster.rs index 0bb743932007edd30a7881c3123d323c388f040d..eeebddf80cf192dc77db449c23567241c7affca4 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -85,6 +85,14 @@ mod tests { use std::str::FromStr; use compare_elements::NamespaceAwareCompare; + #[test] + fn test_size() { + assert_size!(Group, 24); + assert_size!(Subscription, 1); + assert_size!(Item, 128); + assert_size!(Roster, 48); + } + #[test] fn test_get() { let elem: Element = "".parse().unwrap(); diff --git a/src/rsm.rs b/src/rsm.rs index d16751a1f6890e910d74edd25072e87bb18d6369..2f176932430ff022cc9136282b1b4f5fc2852987 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -158,6 +158,12 @@ mod tests { use super::*; use compare_elements::NamespaceAwareCompare; + #[test] + fn test_size() { + assert_size!(SetQuery, 80); + assert_size!(SetResult, 80); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/sasl.rs b/src/sasl.rs index f27167b1fdaaff082b22af98f3ecad9a8318c17d..5272fd0d315b786196877e542ee3d9a7de809551 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -215,6 +215,18 @@ mod tests { use try_from::TryFrom; use minidom::Element; + #[test] + fn test_size() { + assert_size!(Mechanism, 1); + assert_size!(Auth, 32); + assert_size!(Challenge, 24); + assert_size!(Response, 24); + assert_size!(Abort, 0); + assert_size!(Success, 24); + assert_size!(DefinedCondition, 1); + assert_size!(Failure, 32); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/sm.rs b/src/sm.rs index e93aa24f189115dea05edee7ff5a573df6075416..dfc3dff204493cb77f5b2339aa441396098dd06b 100644 --- a/src/sm.rs +++ b/src/sm.rs @@ -144,6 +144,20 @@ mod tests { use try_from::TryFrom; use minidom::Element; + #[test] + fn test_size() { + assert_size!(A, 4); + assert_size!(ResumeAttr, 1); + assert_size!(Enable, 12); + assert_size!(StreamId, 24); + assert_size!(Enabled, 64); + assert_size!(Failed, 12); + assert_size!(R, 0); + assert_size!(Resume, 32); + assert_size!(Resumed, 32); + assert_size!(StreamManagement, 0); + } + #[test] fn a() { let elem: Element = " for Element { mod tests { use super::*; + #[test] + fn test_size() { + assert_size!(ErrorType, 1); + assert_size!(DefinedCondition, 1); + assert_size!(StanzaError, 208); + } + #[test] fn test_simple() { #[cfg(not(feature = "component"))] diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 07e947a3e403e1fba0cc2130e4e1755d1f9fc860..9dc3698a6c64896a3cb88074ca98b88ed5228ea8 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -42,6 +42,12 @@ mod tests { use error::Error; use std::str::FromStr; + #[test] + fn test_size() { + assert_size!(StanzaId, 96); + assert_size!(OriginId, 24); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/stream.rs b/src/stream.rs index 693796dcb2022f23a4c7328339c1aeb3a87229d9..b81ba7710208e552c94e66af77125e7bb286063c 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -76,6 +76,11 @@ mod tests { use try_from::TryFrom; use minidom::Element; + #[test] + fn test_size() { + assert_size!(Stream, 216); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/version.rs b/src/version.rs index 4354df50d282cf7a14a365f201f6a6972e5647b1..7ed89743bf4bf35a4e6346728ebfa74658474e1b 100644 --- a/src/version.rs +++ b/src/version.rs @@ -43,6 +43,12 @@ mod tests { use minidom::Element; use compare_elements::NamespaceAwareCompare; + #[test] + fn test_size() { + assert_size!(VersionQuery, 0); + assert_size!(VersionResult, 72); + } + #[test] fn simple() { let elem: Element = "xmpp-rs0.3.0".parse().unwrap(); diff --git a/src/websocket.rs b/src/websocket.rs index 102690cde582f37a5d8a06e329369fa36343c91b..2f87437b1c1a3074db3919b454d0a64cd8f5137f 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -75,6 +75,11 @@ mod tests { use try_from::TryFrom; use minidom::Element; + #[test] + fn test_size() { + assert_size!(Open, 216); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); From 7a2dcbee017a1757e5f581df7fa1363dea9dc223 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 28 Oct 2018 13:10:48 +0100 Subject: [PATCH 532/698] Test struct sizes on 32-bit too. --- src/bind.rs | 7 +++++++ src/blocking.rs | 10 ++++++++++ src/bookmarks.rs | 9 +++++++++ src/caps.rs | 7 +++++++ src/component.rs | 7 +++++++ src/data_forms.rs | 11 +++++++++++ src/delay.rs | 7 +++++++ src/disco.rs | 14 ++++++++++++++ src/ecaps2.rs | 7 +++++++ src/eme.rs | 7 +++++++ src/forwarding.rs | 7 +++++++ src/hashes.rs | 8 ++++++++ src/ibb.rs | 10 ++++++++++ src/ibr.rs | 7 +++++++ src/iq.rs | 8 ++++++++ src/jingle.rs | 16 ++++++++++++++++ src/jingle_ft.rs | 11 +++++++++++ src/jingle_ibb.rs | 7 +++++++ src/jingle_message.rs | 7 +++++++ src/jingle_s5b.rs | 13 +++++++++++++ src/mam.rs | 13 +++++++++++++ src/media_element.rs | 8 ++++++++ src/message.rs | 11 +++++++++++ src/message_correct.rs | 7 +++++++ src/mood.rs | 8 ++++++++ src/nick.rs | 7 +++++++ src/presence.rs | 9 +++++++++ src/receipts.rs | 8 ++++++++ src/roster.rs | 10 ++++++++++ src/rsm.rs | 8 ++++++++ src/sasl.rs | 14 ++++++++++++++ src/sm.rs | 16 ++++++++++++++++ src/stanza_error.rs | 9 +++++++++ src/stanza_id.rs | 8 ++++++++ src/stream.rs | 7 +++++++ src/version.rs | 8 ++++++++ src/websocket.rs | 7 +++++++ 37 files changed, 338 insertions(+) diff --git a/src/bind.rs b/src/bind.rs index 6bfa8a310edc4a765fb8cab864dab159eb339fb0..b6ec55d44f291ec3c03b4b681cd2401e22365171 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -98,6 +98,13 @@ impl From for Element { mod tests { use super::*; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Bind, 40); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Bind, 80); diff --git a/src/blocking.rs b/src/blocking.rs index 87a2fbd2bcdc002d897bc33a9d969c84ebb6e62c..cd3f8fd83f7e38c806034c07d69e6fd9cd73a5e3 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -98,6 +98,16 @@ generate_empty_element!( mod tests { use super::*; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(BlocklistRequest, 0); + assert_size!(BlocklistResult, 12); + assert_size!(Block, 12); + assert_size!(Unblock, 12); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(BlocklistRequest, 0); diff --git a/src/bookmarks.rs b/src/bookmarks.rs index 8ae0639205358f30feb399b0a9e93750f2e9dacf..7f68930dd7cb72ffd2450acf08526f10a9e676de 100644 --- a/src/bookmarks.rs +++ b/src/bookmarks.rs @@ -74,6 +74,15 @@ mod tests { use minidom::Element; use compare_elements::NamespaceAwareCompare; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Conference, 76); + assert_size!(Url, 24); + assert_size!(Storage, 24); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Conference, 152); diff --git a/src/caps.rs b/src/caps.rs index 7fcaea801731e2ae4f238d3b14cae73c4d67a34d..22ac1930e678763bb8f905ccd6062fed04362c6a 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -211,6 +211,13 @@ mod tests { use caps; use base64; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Caps, 52); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Caps, 104); diff --git a/src/component.rs b/src/component.rs index 503e103423c05bfff07fcbd9d51289661450c131..0c9319cdb655d1bebc951b23fe34cf516bf02591 100644 --- a/src/component.rs +++ b/src/component.rs @@ -47,6 +47,13 @@ mod tests { use try_from::TryFrom; use minidom::Element; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Handshake, 12); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Handshake, 24); diff --git a/src/data_forms.rs b/src/data_forms.rs index 3fccd72622e8983631b495192cd1e93d0dbf11db..edf2608ce43ed8801b50feabd88883b01db6926b 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -268,6 +268,17 @@ impl From for Element { mod tests { use super::*; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Option_, 24); + assert_size!(FieldType, 1); + assert_size!(Field, 64); + assert_size!(DataFormType, 1); + assert_size!(DataForm, 52); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Option_, 48); diff --git a/src/delay.rs b/src/delay.rs index c4f7518f6f85da2fcef3fd698fb770d8f00a5e5d..46fec0fbdfd252d76dfc294a243bdd47c85a7d58 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -39,6 +39,13 @@ mod tests { use error::Error; use std::str::FromStr; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Delay, 64); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Delay, 112); diff --git a/src/disco.rs b/src/disco.rs index 7b435530a6c72bec563000c459fd94c153106413..7be0efc77bf6ed1f578886b349150e2172198361 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -235,6 +235,20 @@ mod tests { use compare_elements::NamespaceAwareCompare; use std::str::FromStr; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Identity, 48); + assert_size!(Feature, 12); + assert_size!(DiscoInfoQuery, 12); + assert_size!(DiscoInfoResult, 48); + + assert_size!(Item, 60); + assert_size!(DiscoItemsQuery, 12); + assert_size!(DiscoItemsResult, 24); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Identity, 96); diff --git a/src/ecaps2.rs b/src/ecaps2.rs index d4811b5cc917d1ba59081a25d4dfd218dda80f57..ea5e03df463b725498c035c1dcb74e9a994d5897 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -152,6 +152,13 @@ mod tests { use minidom::Element; use error::Error; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(ECaps2, 12); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(ECaps2, 24); diff --git a/src/eme.rs b/src/eme.rs index 271b842e38ae0e24b41727660cb1b689844296fe..417bbe5214b0343cce932ba4086ce6dc2cf6bfa4 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -28,6 +28,13 @@ mod tests { use minidom::Element; use error::Error; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(ExplicitMessageEncryption, 24); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(ExplicitMessageEncryption, 48); diff --git a/src/forwarding.rs b/src/forwarding.rs index fbfacb11ec2caaabc3a7afe5b9579598cedab932..11efb610caa330668b512c2780b985f90c2e083d 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -30,6 +30,13 @@ mod tests { use minidom::Element; use error::Error; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Forwarded, 204); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Forwarded, 392); diff --git a/src/hashes.rs b/src/hashes.rs index 7063dbc66ff754e8716b974329564581cd29c5e3..5fbf5b90933e827506c11d5d225c276f1194fe13 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -133,6 +133,14 @@ mod tests { use try_from::TryFrom; use minidom::Element; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Algo, 16); + assert_size!(Hash, 28); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Algo, 32); diff --git a/src/ibb.rs b/src/ibb.rs index e595fe4ab6a7709bd0b4d48ba8a82bc22e511e2c..87b335ec1e9b3de8381793175b9e6149279db292 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -76,6 +76,16 @@ mod tests { use error::Error; use std::error::Error as StdError; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Stanza, 1); + assert_size!(Open, 16); + assert_size!(Data, 28); + assert_size!(Close, 12); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Stanza, 1); diff --git a/src/ibr.rs b/src/ibr.rs index 71256b642a0de42366eb438d15827e25df6be909..4d76a9d826db9d6e9cccdd2533080a67936e552c 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -96,6 +96,13 @@ mod tests { use super::*; use compare_elements::NamespaceAwareCompare; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Query, 88); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Query, 152); diff --git a/src/iq.rs b/src/iq.rs index b5f27bf06a545720576a160abaa9f71e349f6c4f..ff1e7b1fe47929cf7634df4b1946f75150e76dbe 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -226,6 +226,14 @@ mod tests { use compare_elements::NamespaceAwareCompare; use disco::DiscoInfoQuery; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(IqType, 108); + assert_size!(Iq, 192); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(IqType, 216); diff --git a/src/jingle.rs b/src/jingle.rs index 38577f8415e86fc9fe1591a1a97eb91119e9fc10..7e4ea82b5ee6331d14ba853ce2d807c96ca776f5 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -533,6 +533,22 @@ impl From for Element { mod tests { use super::*; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Action, 1); + assert_size!(Creator, 1); + assert_size!(Senders, 1); + assert_size!(Disposition, 1); + assert_size!(ContentId, 12); + assert_size!(Content, 172); + assert_size!(Reason, 1); + assert_size!(ReasonElement, 16); + assert_size!(SessionId, 12); + assert_size!(Jingle, 128); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Action, 1); diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 0a3c022a1ab6518e459d17e9be26f73ab9bc754e..604e6ee5c716981f4d5a0b5f75438c2fb192a10e 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -348,6 +348,17 @@ mod tests { use hashes::Algo; use base64; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Range, 40); + assert_size!(File, 128); + assert_size!(Description, 128); + assert_size!(Checksum, 144); + assert_size!(Received, 16); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Range, 48); diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index c56c73d490a3907512b668e29a52f2af1477ff7b..e02a43474cb691efe25d9cfc2ad8239844302262 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -29,6 +29,13 @@ mod tests { use error::Error; use std::error::Error as StdError; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Transport, 16); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Transport, 32); diff --git a/src/jingle_message.rs b/src/jingle_message.rs index 150e7a03134367da32a384d78827147921a1e52c..1bef32698422508e9b25511095f8cd8656fd4612 100644 --- a/src/jingle_message.rs +++ b/src/jingle_message.rs @@ -121,6 +121,13 @@ impl From for Element { mod tests { use super::*; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(JingleMI, 68); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(JingleMI, 136); diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index a5fdff0deb3b63dad66b32a5b6727344819c7a1b..904a4d77f9db5743ed1d80c6aa1bbb21ad9d6b89 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -277,6 +277,19 @@ mod tests { use std::str::FromStr; use compare_elements::NamespaceAwareCompare; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Type, 1); + assert_size!(Mode, 1); + assert_size!(CandidateId, 12); + assert_size!(StreamId, 12); + assert_size!(Candidate, 80); + assert_size!(TransportPayload, 16); + assert_size!(Transport, 44); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Type, 1); diff --git a/src/mam.rs b/src/mam.rs index 44564120a727b058a6f58740ecda9e3dbb612ae0..819bc46261af2237b84d7e9cee700f8ab62bc1af 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -191,6 +191,19 @@ mod tests { use super::*; use std::str::FromStr; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(QueryId, 12); + assert_size!(Query, 116); + assert_size!(Result_, 228); + assert_size!(Complete, 1); + assert_size!(Fin, 44); + assert_size!(DefaultPrefs, 1); + assert_size!(Prefs, 28); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(QueryId, 24); diff --git a/src/media_element.rs b/src/media_element.rs index 722780f2f03ceb12fb0b8dcaa96e9eee362cdb86..dc0c2af7a7e86a580ddba48bd60b43cb929766e7 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -51,6 +51,14 @@ mod tests { use data_forms::DataForm; use std::error::Error as StdError; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(URI, 24); + assert_size!(MediaElement, 28); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(URI, 48); diff --git a/src/message.rs b/src/message.rs index 2e9d9c659d9509cfc5da8e71400e82bdf1a12905..9c392b790991c6d3f35f0fa96487f553c781d3b4 100644 --- a/src/message.rs +++ b/src/message.rs @@ -234,6 +234,17 @@ mod tests { use std::str::FromStr; use compare_elements::NamespaceAwareCompare; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(MessageType, 1); + assert_size!(Body, 12); + assert_size!(Subject, 12); + assert_size!(Thread, 12); + assert_size!(Message, 136); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(MessageType, 1); diff --git a/src/message_correct.rs b/src/message_correct.rs index e8233ee632d6b5295e1c180d1e14aff328958e54..06b433f9b047702aae15111a31614b0d562d1fc2 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -25,6 +25,13 @@ mod tests { use minidom::Element; use error::Error; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Replace, 12); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Replace, 24); diff --git a/src/mood.rs b/src/mood.rs index 6bf8bd2383cf1fce59068131b62872132cd3a205..59204322be515e7f3ea7ff4bbba16f280e45e5d3 100644 --- a/src/mood.rs +++ b/src/mood.rs @@ -272,6 +272,14 @@ mod tests { use try_from::TryFrom; use minidom::Element; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(MoodEnum, 1); + assert_size!(Text, 12); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(MoodEnum, 1); diff --git a/src/nick.rs b/src/nick.rs index 2d2f90c4c7d212d491c244e4b5aa97a8c752d0e4..7a3666571039a7005541dc04b739d53d4f91fefc 100644 --- a/src/nick.rs +++ b/src/nick.rs @@ -16,6 +16,13 @@ mod tests { use minidom::Element; use error::Error; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Nick, 12); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Nick, 24); diff --git a/src/presence.rs b/src/presence.rs index 02a9a9c7c890c95e6dad4173f70a3fb378c42901..db6ac7a60c171d848f5d87c22403b092426cf365 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -323,6 +323,15 @@ mod tests { use super::*; use compare_elements::NamespaceAwareCompare; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Show, 1); + assert_size!(Type, 1); + assert_size!(Presence, 112); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Show, 1); diff --git a/src/receipts.rs b/src/receipts.rs index ecf0a21cabe54828889a5dcfba0e002f8765486a..49759c773fe7590890490a709c0d4ffda4f55886 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -33,6 +33,14 @@ mod tests { use minidom::Element; use ns; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Request, 0); + assert_size!(Received, 12); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Request, 0); diff --git a/src/roster.rs b/src/roster.rs index eeebddf80cf192dc77db449c23567241c7affca4..c1f8641eda53b54c626752d5a083f98b4cb63c9c 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -85,6 +85,16 @@ mod tests { use std::str::FromStr; use compare_elements::NamespaceAwareCompare; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Group, 12); + assert_size!(Subscription, 1); + assert_size!(Item, 64); + assert_size!(Roster, 24); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Group, 24); diff --git a/src/rsm.rs b/src/rsm.rs index 2f176932430ff022cc9136282b1b4f5fc2852987..fb321fa55cb76a1b1a02460d0c98df335367167c 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -158,6 +158,14 @@ mod tests { use super::*; use compare_elements::NamespaceAwareCompare; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(SetQuery, 40); + assert_size!(SetResult, 40); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(SetQuery, 80); diff --git a/src/sasl.rs b/src/sasl.rs index 5272fd0d315b786196877e542ee3d9a7de809551..1ec44af087b4b457aa56ec7296d2534aa3eeb9f2 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -215,6 +215,20 @@ mod tests { use try_from::TryFrom; use minidom::Element; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Mechanism, 1); + assert_size!(Auth, 16); + assert_size!(Challenge, 12); + assert_size!(Response, 12); + assert_size!(Abort, 0); + assert_size!(Success, 12); + assert_size!(DefinedCondition, 1); + assert_size!(Failure, 16); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Mechanism, 1); diff --git a/src/sm.rs b/src/sm.rs index dfc3dff204493cb77f5b2339aa441396098dd06b..404b54eb64e2b076a917c4501d2b39285906b284 100644 --- a/src/sm.rs +++ b/src/sm.rs @@ -144,6 +144,22 @@ mod tests { use try_from::TryFrom; use minidom::Element; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(A, 4); + assert_size!(ResumeAttr, 1); + assert_size!(Enable, 12); + assert_size!(StreamId, 12); + assert_size!(Enabled, 36); + assert_size!(Failed, 12); + assert_size!(R, 0); + assert_size!(Resume, 16); + assert_size!(Resumed, 16); + assert_size!(StreamManagement, 0); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(A, 4); diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 480b1d8092c40d8c3b6442a4c573efaa3318e662..750823d382b7ea5ce723222c22a0ca8e5b18f93d 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -289,6 +289,15 @@ impl From for Element { mod tests { use super::*; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(ErrorType, 1); + assert_size!(DefinedCondition, 1); + assert_size!(StanzaError, 104); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(ErrorType, 1); diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 9dc3698a6c64896a3cb88074ca98b88ed5228ea8..e45b528c0c16c665908c3dab11a1b89179b4294b 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -42,6 +42,14 @@ mod tests { use error::Error; use std::str::FromStr; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(StanzaId, 48); + assert_size!(OriginId, 12); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(StanzaId, 96); diff --git a/src/stream.rs b/src/stream.rs index b81ba7710208e552c94e66af77125e7bb286063c..ad8ec4f1cf8371565b31f9ac72a4dfe7c572104d 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -76,6 +76,13 @@ mod tests { use try_from::TryFrom; use minidom::Element; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Stream, 108); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Stream, 216); diff --git a/src/version.rs b/src/version.rs index 7ed89743bf4bf35a4e6346728ebfa74658474e1b..cbd958395d24e2b949c5c995c33dc90472d8d8f9 100644 --- a/src/version.rs +++ b/src/version.rs @@ -43,6 +43,14 @@ mod tests { use minidom::Element; use compare_elements::NamespaceAwareCompare; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(VersionQuery, 0); + assert_size!(VersionResult, 36); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(VersionQuery, 0); diff --git a/src/websocket.rs b/src/websocket.rs index 2f87437b1c1a3074db3919b454d0a64cd8f5137f..f2d28ebfe2892ff84787a67375b53de73134822c 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -75,6 +75,13 @@ mod tests { use try_from::TryFrom; use minidom::Element; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Open, 108); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Open, 216); From e5c1be68d066f7bff37ad9a69de19e62224d0540 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 1 Nov 2018 17:25:24 +0100 Subject: [PATCH 533/698] muc: Add constructors. --- src/muc/muc.rs | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/muc/muc.rs b/src/muc/muc.rs index c363107a4f3f818a2293fa3a565c0700f9f0776d..def14baea0f470a5c801d8de164fab189a2598fa 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -10,6 +10,7 @@ use date::DateTime; generate_element!( /// Represents the query for messages before our join. + #[derive(PartialEq)] History, "history", MUC, attributes: [ /// How many characters of history to send, in XML characters. @@ -26,8 +27,40 @@ generate_element!( ] ); +impl History { + pub fn new() -> Self { + History { + maxchars: None, + maxstanzas: None, + seconds: None, + since: None, + } + } + + pub fn with_maxchars(mut self, maxchars: u32) -> Self { + self.maxchars = Some(maxchars); + self + } + + pub fn with_maxstanzas(mut self, maxstanzas: u32) -> Self { + self.maxstanzas = Some(maxstanzas); + self + } + + pub fn with_seconds(mut self, seconds: u32) -> Self { + self.seconds = Some(seconds); + self + } + + pub fn with_since(mut self, since: DateTime) -> Self { + self.since = Some(since); + self + } +} + generate_element!( /// Represents a room join request. + #[derive(PartialEq)] Muc, "x", MUC, children: [ /// Password to use when the room is protected by a password. password: Option = ("password", MUC) => String, @@ -39,6 +72,25 @@ generate_element!( impl PresencePayload for Muc {} +impl Muc { + pub fn new() -> Self { + Muc { + password: None, + history: None, + } + } + + pub fn with_password(mut self, password: String) -> Self { + self.password = Some(password); + self + } + + pub fn with_history(mut self, history: History) -> Self { + self.history = Some(history); + self + } +} + #[cfg(test)] mod tests { use super::*; @@ -110,6 +162,9 @@ mod tests { " .parse().unwrap(); let muc = Muc::try_from(elem).unwrap(); + let muc2 = Muc::new().with_history(History::new().with_maxstanzas(0)); + assert_eq!(muc, muc2); + let history = muc.history.unwrap(); assert_eq!(history.maxstanzas, Some(0)); assert_eq!(history.maxchars, None); From e2c0068af9cb582762d0f13cd3e9db72328516e5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 2 Nov 2018 16:27:51 +0100 Subject: [PATCH 534/698] disco: Remove errors for out-of-order elements, allowed in XEP-0030 2.5rc3. --- src/disco.rs | 45 ++++++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index 7be0efc77bf6ed1f578886b349150e2172198361..e76b21f21c52b3e3a59c002c8d712c6b9566a2d2 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -126,26 +126,15 @@ impl TryFrom for DiscoInfoResult { features: vec!(), extensions: vec!(), }; - let mut parsing_identities_done = false; - let mut parsing_features_done = false; for child in elem.children() { if child.is("identity", ns::DISCO_INFO) { - if parsing_identities_done { - return Err(Error::ParseError("Identity found after features or data forms in disco#info.")); - } let identity = Identity::try_from(child.clone())?; result.identities.push(identity); } else if child.is("feature", ns::DISCO_INFO) { - parsing_identities_done = true; - if parsing_features_done { - return Err(Error::ParseError("Feature found after data forms in disco#info.")); - } let feature = Feature::try_from(child.clone())?; result.features.push(feature); } else if child.is("x", ns::DATA_FORMS) { - parsing_identities_done = true; - parsing_features_done = true; let data_form = DataForm::try_from(child.clone())?; if data_form.type_ != DataFormType::Result_ { return Err(Error::ParseError("Data form must have a 'result' type in disco#info.")); @@ -271,6 +260,24 @@ mod tests { assert!(query.extensions.is_empty()); } + #[test] + fn test_identity_after_feature() { + let elem: Element = "".parse().unwrap(); + let query = DiscoInfoResult::try_from(elem).unwrap(); + assert_eq!(query.identities.len(), 1); + assert_eq!(query.features.len(), 1); + assert!(query.extensions.is_empty()); + } + + #[test] + fn test_feature_after_dataform() { + let elem: Element = "coucou".parse().unwrap(); + let query = DiscoInfoResult::try_from(elem).unwrap(); + assert_eq!(query.identities.len(), 1); + assert_eq!(query.features.len(), 1); + assert_eq!(query.extensions.len(), 1); + } + #[test] fn test_extension() { let elem: Element = "example".parse().unwrap(); @@ -368,22 +375,6 @@ mod tests { _ => panic!(), }; assert_eq!(message, "disco#info feature not present in disco#info."); - - let elem: Element = "".parse().unwrap(); - let error = DiscoInfoResult::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Identity found after features or data forms in disco#info."); - - let elem: Element = "coucou".parse().unwrap(); - let error = DiscoInfoResult::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Feature found after data forms in disco#info."); } #[test] From c420c87bf5d7d15c80fc599e7c3a0a038204fadb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 2 Nov 2018 16:28:40 +0100 Subject: [PATCH 535/698] muc: Document more constructors. --- src/muc/muc.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/muc/muc.rs b/src/muc/muc.rs index def14baea0f470a5c801d8de164fab189a2598fa..5e5d65e86fdb220c5b16e0668ba77bccd6ea08e3 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -28,6 +28,7 @@ generate_element!( ); impl History { + /// Create a new empty history element. pub fn new() -> Self { History { maxchars: None, @@ -37,21 +38,25 @@ impl History { } } + /// Set how many characters of history to send. pub fn with_maxchars(mut self, maxchars: u32) -> Self { self.maxchars = Some(maxchars); self } + /// Set how many messages to send. pub fn with_maxstanzas(mut self, maxstanzas: u32) -> Self { self.maxstanzas = Some(maxstanzas); self } + /// Only send messages received in these last seconds. pub fn with_seconds(mut self, seconds: u32) -> Self { self.seconds = Some(seconds); self } + /// Only send messages received since this date. pub fn with_since(mut self, since: DateTime) -> Self { self.since = Some(since); self @@ -73,6 +78,7 @@ generate_element!( impl PresencePayload for Muc {} impl Muc { + /// Create a new MUC join element. pub fn new() -> Self { Muc { password: None, @@ -80,11 +86,13 @@ impl Muc { } } + /// Join a room with this password. pub fn with_password(mut self, password: String) -> Self { self.password = Some(password); self } + /// Join a room with only that much history. pub fn with_history(mut self, history: History) -> Self { self.history = Some(history); self From 3f4586cabad93314659553c6e11931396cf91ad3 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 2 Nov 2018 16:29:11 +0100 Subject: [PATCH 536/698] presence: Add status and payload insertion helpers. --- src/presence.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/presence.rs b/src/presence.rs index db6ac7a60c171d848f5d87c22403b092426cf365..2cb115f5270043d5026251ac84be06be76d21f72 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -239,6 +239,16 @@ impl Presence { self.payloads = payloads; self } + + /// Set the availability information of this presence. + pub fn set_status(&mut self, lang: Lang, status: Status) { + self.statuses.insert(lang, status); + } + + /// Add a payload to this presence. + pub fn add_payload(&mut self, payload: P) { + self.payloads.push(payload.into()); + } } impl TryFrom for Presence { From 6b2dd8fe5e27a168b96e89432a5c1eb7fc2f90c2 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 18 Dec 2018 15:27:30 +0100 Subject: [PATCH 537/698] Run `cargo fix --edition` to move to Edition 2018. --- Cargo.toml | 1 + src/attention.rs | 4 +- src/bind.rs | 6 +-- src/blocking.rs | 6 +-- src/bookmarks.rs | 2 +- src/caps.rs | 14 +++---- src/chatstates.rs | 6 +-- src/component.rs | 2 +- src/data_forms.rs | 6 +-- src/date.rs | 2 +- src/delay.rs | 10 ++--- src/disco.rs | 10 ++--- src/ecaps2.rs | 12 +++--- src/eme.rs | 4 +- src/forwarding.rs | 6 +-- src/hashes.rs | 4 +- src/helpers.rs | 2 +- src/ibb.rs | 6 +-- src/ibr.rs | 10 ++--- src/idle.rs | 6 +-- src/iq.rs | 12 +++--- src/jingle.rs | 6 +-- src/jingle_ft.rs | 12 +++--- src/jingle_ibb.rs | 4 +- src/jingle_message.rs | 6 +-- src/jingle_s5b.rs | 6 +-- src/macros.rs | 90 +++++++++++++++++++++--------------------- src/mam.rs | 16 ++++---- src/media_element.rs | 6 +-- src/message.rs | 6 +-- src/message_correct.rs | 4 +- src/muc/muc.rs | 8 ++-- src/muc/user.rs | 6 +-- src/nick.rs | 2 +- src/ping.rs | 4 +- src/presence.rs | 6 +-- src/pubsub/event.rs | 12 +++--- src/pubsub/pubsub.rs | 12 +++--- src/receipts.rs | 4 +- src/roster.rs | 6 +-- src/rsm.rs | 6 +-- src/sasl.rs | 6 +-- src/sm.rs | 2 +- src/stanza_error.rs | 8 ++-- src/stanza_id.rs | 4 +- src/version.rs | 4 +- 46 files changed, 189 insertions(+), 188 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a438baec39d7e80575db8297beac0a521c737301..8fd7253bf291da6fcd186a53058766ae6d1c6611 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ repository = "https://hg.linkmauve.fr/xmpp-parsers" keywords = ["xmpp", "xml"] categories = ["parsing", "network-programming"] license = "MPL-2.0" +edition = "2018" [dependencies] minidom = "0.9.1" diff --git a/src/attention.rs b/src/attention.rs index 4bfffd7eb7fe725943adfbcb1c3289cc330c5b65..e71b26446f9a2335c5a548b04aad51de44edd5a0 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use message::MessagePayload; +use crate::message::MessagePayload; generate_empty_element!( /// Requests the attention of the recipient. @@ -18,7 +18,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; + use crate::error::Error; #[test] fn test_size() { diff --git a/src/bind.rs b/src/bind.rs index b6ec55d44f291ec3c03b4b681cd2401e22365171..81187ae3e76123b369d065448c5fe2e0045c1c98 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -9,10 +9,10 @@ use try_from::TryFrom; use minidom::Element; -use error::Error; +use crate::error::Error; use jid::Jid; -use ns; -use iq::{IqSetPayload, IqResultPayload}; +use crate::ns; +use crate::iq::{IqSetPayload, IqResultPayload}; /// The request for resource binding, which is the process by which a client /// can obtain a full JID and start exchanging on the XMPP network. diff --git a/src/blocking.rs b/src/blocking.rs index cd3f8fd83f7e38c806034c07d69e6fd9cd73a5e3..fd89c4846e38b2562ebf23ee0e5458aac46cb361 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -9,10 +9,10 @@ use try_from::TryFrom; use jid::Jid; use minidom::Element; -use error::Error; +use crate::error::Error; -use ns; -use iq::{IqGetPayload, IqSetPayload, IqResultPayload}; +use crate::ns; +use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload}; generate_empty_element!( /// The element requesting the blocklist, the result iq will contain a diff --git a/src/bookmarks.rs b/src/bookmarks.rs index 7f68930dd7cb72ffd2450acf08526f10a9e676de..8f88845fe81d2c476906aa1dfeff01287a1190e5 100644 --- a/src/bookmarks.rs +++ b/src/bookmarks.rs @@ -72,7 +72,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use compare_elements::NamespaceAwareCompare; + use crate::compare_elements::NamespaceAwareCompare; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/caps.rs b/src/caps.rs index 22ac1930e678763bb8f905ccd6062fed04362c6a..ce44e54c4749c2db1d470c9440d6c08300b44380 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -6,14 +6,14 @@ use try_from::TryFrom; -use presence::PresencePayload; -use disco::{Feature, Identity, DiscoInfoResult, DiscoInfoQuery}; -use data_forms::DataForm; -use hashes::{Hash, Algo}; +use crate::presence::PresencePayload; +use crate::disco::{Feature, Identity, DiscoInfoResult, DiscoInfoQuery}; +use crate::data_forms::DataForm; +use crate::hashes::{Hash, Algo}; use minidom::Element; -use error::Error; -use ns; +use crate::error::Error; +use crate::ns; use base64; use sha1::Sha1; @@ -208,7 +208,7 @@ pub fn query_caps(caps: Caps) -> DiscoInfoQuery { #[cfg(test)] mod tests { use super::*; - use caps; + use crate::caps; use base64; #[cfg(target_pointer_width = "32")] diff --git a/src/chatstates.rs b/src/chatstates.rs index 477bdded08d5b9083ac95e3995acb2bde604ea85..2d4d3830cacc60c3df5a6b105447327e1426194b 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use message::MessagePayload; +use crate::message::MessagePayload; generate_element_enum!( /// Enum representing chatstate elements part of the @@ -34,8 +34,8 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; - use ns; + use crate::error::Error; + use crate::ns; #[test] fn test_size() { diff --git a/src/component.rs b/src/component.rs index 0c9319cdb655d1bebc951b23fe34cf516bf02591..1fbb7ff35710b108c53d39282bbbe8f2652cbd38 100644 --- a/src/component.rs +++ b/src/component.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use helpers::PlainText; +use crate::helpers::PlainText; use sha1::Sha1; use digest::Digest; diff --git a/src/data_forms.rs b/src/data_forms.rs index edf2608ce43ed8801b50feabd88883b01db6926b..622820d329d446b38db7b57032a652bdd75d69f4 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -8,10 +8,10 @@ use try_from::TryFrom; use minidom::Element; -use error::Error; -use ns; +use crate::error::Error; +use crate::ns; -use media_element::MediaElement; +use crate::media_element::MediaElement; generate_element!( /// Represents one of the possible values for a list- field. diff --git a/src/date.rs b/src/date.rs index 5f2038de7a4576fec17c8a5f1a618bfa3d6343fa..34bef2425ea81879cfb350107b95717189ad0860 100644 --- a/src/date.rs +++ b/src/date.rs @@ -9,7 +9,7 @@ use std::str::FromStr; use minidom::{IntoAttributeValue, IntoElements, ElementEmitter}; use chrono::{DateTime as ChronoDateTime, FixedOffset}; -use error::Error; +use crate::error::Error; /// Implements the DateTime profile of XEP-0082, which represents a /// non-recurring moment in time, with an accuracy of seconds or fraction of diff --git a/src/delay.rs b/src/delay.rs index 46fec0fbdfd252d76dfc294a243bdd47c85a7d58..e67b290691dd2f0ab2e08f58de02d1c00d9366d5 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -4,13 +4,13 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use message::MessagePayload; -use presence::PresencePayload; -use date::DateTime; +use crate::message::MessagePayload; +use crate::presence::PresencePayload; +use crate::date::DateTime; use jid::Jid; -use helpers::PlainText; +use crate::helpers::PlainText; generate_element!( /// Notes when and by whom a message got stored for later delivery. @@ -36,7 +36,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; + use crate::error::Error; use std::str::FromStr; #[cfg(target_pointer_width = "32")] diff --git a/src/disco.rs b/src/disco.rs index e76b21f21c52b3e3a59c002c8d712c6b9566a2d2..7c765fb1c5fce85c13eedba597d84c77394eadbd 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -9,11 +9,11 @@ use try_from::TryFrom; use minidom::Element; use jid::Jid; -use error::Error; -use ns; +use crate::error::Error; +use crate::ns; -use iq::{IqGetPayload, IqResultPayload}; -use data_forms::{DataForm, DataFormType}; +use crate::iq::{IqGetPayload, IqResultPayload}; +use crate::data_forms::{DataForm, DataFormType}; generate_element!( /// Structure representing a `` element. @@ -221,7 +221,7 @@ impl IqResultPayload for DiscoItemsResult {} #[cfg(test)] mod tests { use super::*; - use compare_elements::NamespaceAwareCompare; + use crate::compare_elements::NamespaceAwareCompare; use std::str::FromStr; #[cfg(target_pointer_width = "32")] diff --git a/src/ecaps2.rs b/src/ecaps2.rs index ea5e03df463b725498c035c1dcb74e9a994d5897..ecadc1531d2517e73fafff164d952e8a775fba1f 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -4,12 +4,12 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use presence::PresencePayload; -use disco::{Feature, Identity, DiscoInfoResult, DiscoInfoQuery}; -use data_forms::DataForm; -use hashes::{Hash, Algo}; +use crate::presence::PresencePayload; +use crate::disco::{Feature, Identity, DiscoInfoResult, DiscoInfoQuery}; +use crate::data_forms::DataForm; +use crate::hashes::{Hash, Algo}; -use ns; +use crate::ns; use base64; use sha2::{Sha256, Sha512}; @@ -150,7 +150,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; + use crate::error::Error; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/eme.rs b/src/eme.rs index 417bbe5214b0343cce932ba4086ce6dc2cf6bfa4..5c13f5107ae0b955c49ef3d14fbf203cd3105ce3 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use message::MessagePayload; +use crate::message::MessagePayload; generate_element!( /// Structure representing an `` element. @@ -26,7 +26,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; + use crate::error::Error; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/forwarding.rs b/src/forwarding.rs index 11efb610caa330668b512c2780b985f90c2e083d..9f725132d40e832d9b8aff96024e951f950ba556 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -4,8 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use delay::Delay; -use message::Message; +use crate::delay::Delay; +use crate::message::Message; generate_element!( /// Contains a forwarded stanza, either standalone or part of another @@ -28,7 +28,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; + use crate::error::Error; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/hashes.rs b/src/hashes.rs index 5fbf5b90933e827506c11d5d225c276f1194fe13..93a17291f127b3c78b2b725556dfff1ab261bbc0 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -8,9 +8,9 @@ use std::str::FromStr; use minidom::IntoAttributeValue; -use error::Error; +use crate::error::Error; -use helpers::Base64; +use crate::helpers::Base64; use base64; /// List of the algorithms we support, or Unknown. diff --git a/src/helpers.rs b/src/helpers.rs index 4db2d2c1c729512d6772a9bd2a075b0024ed28b5..9d4f3ecb405771063854168e88414c4977aabf7f 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -5,7 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use base64; -use error::Error; +use crate::error::Error; /// Codec for plain text content. pub struct PlainText; diff --git a/src/ibb.rs b/src/ibb.rs index 87b335ec1e9b3de8381793175b9e6149279db292..19af588174dce9c0d1c488a519cd625cb48dc072 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -4,8 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use helpers::Base64; -use iq::IqSetPayload; +use crate::helpers::Base64; +use crate::iq::IqSetPayload; generate_id!( /// An identifier matching a stream. @@ -73,7 +73,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; + use crate::error::Error; use std::error::Error as StdError; #[cfg(target_pointer_width = "32")] diff --git a/src/ibr.rs b/src/ibr.rs index 4d76a9d826db9d6e9cccdd2533080a67936e552c..4afe719e38ee4fa2c1eaa4700a29ecbd8f3920fc 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -9,12 +9,12 @@ use try_from::TryFrom; use minidom::Element; -use error::Error; +use crate::error::Error; -use iq::{IqGetPayload, IqSetPayload, IqResultPayload}; -use data_forms::DataForm; +use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload}; +use crate::data_forms::DataForm; -use ns; +use crate::ns; /// Query for registering against a service. #[derive(Debug, Clone)] @@ -94,7 +94,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use compare_elements::NamespaceAwareCompare; + use crate::compare_elements::NamespaceAwareCompare; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/idle.rs b/src/idle.rs index 45352a229a58509a5ed7e6a04d90bf458a9a04f0..53ededdad8797cb9a29ba0c65c5700c19bafb82e 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -4,8 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use presence::PresencePayload; -use date::DateTime; +use crate::presence::PresencePayload; +use crate::date::DateTime; generate_element!( /// Represents the last time the user interacted with their system. @@ -23,7 +23,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; + use crate::error::Error; use std::str::FromStr; use std::error::Error as StdError; diff --git a/src/iq.rs b/src/iq.rs index ff1e7b1fe47929cf7634df4b1946f75150e76dbe..1657fcb650d3c89e83f6644f37622c954b819350 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -12,11 +12,11 @@ use minidom::IntoAttributeValue; use jid::Jid; -use error::Error; +use crate::error::Error; -use ns; +use crate::ns; -use stanza_error::StanzaError; +use crate::stanza_error::StanzaError; /// Should be implemented on every known payload of an ``. pub trait IqGetPayload: TryFrom + Into {} @@ -222,9 +222,9 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use stanza_error::{ErrorType, DefinedCondition}; - use compare_elements::NamespaceAwareCompare; - use disco::DiscoInfoQuery; + use crate::stanza_error::{ErrorType, DefinedCondition}; + use crate::compare_elements::NamespaceAwareCompare; + use crate::disco::DiscoInfoQuery; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/jingle.rs b/src/jingle.rs index 7e4ea82b5ee6331d14ba853ce2d807c96ca776f5..720eae864dda1c5f708ce17c6e3288e4aa27df55 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -10,9 +10,9 @@ use std::str::FromStr; use minidom::Element; use jid::Jid; -use error::Error; -use ns; -use iq::IqSetPayload; +use crate::error::Error; +use crate::ns; +use crate::iq::IqSetPayload; generate_attribute!( /// The action attribute. diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 604e6ee5c716981f4d5a0b5f75438c2fb192a10e..15bb4f3285ce6981e66b3215de0235311b6ffcb5 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -9,14 +9,14 @@ use std::str::FromStr; use std::collections::BTreeMap; -use hashes::Hash; -use jingle::{Creator, ContentId}; -use date::DateTime; +use crate::hashes::Hash; +use crate::jingle::{Creator, ContentId}; +use crate::date::DateTime; use minidom::Element; -use error::Error; -use ns; +use crate::error::Error; +use crate::ns; generate_element!( /// Represents a range in a file. @@ -345,7 +345,7 @@ generate_element!( #[cfg(test)] mod tests { use super::*; - use hashes::Algo; + use crate::hashes::Algo; use base64; #[cfg(target_pointer_width = "32")] diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index e02a43474cb691efe25d9cfc2ad8239844302262..129f7ad40d0efc3078bfbcd27a5dc90fd7eb5c41 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use ibb::{Stanza, StreamId}; +use crate::ibb::{Stanza, StreamId}; generate_element!( /// Describes an [In-Band Bytestream](https://xmpp.org/extensions/xep-0047.html) @@ -26,7 +26,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; + use crate::error::Error; use std::error::Error as StdError; #[cfg(target_pointer_width = "32")] diff --git a/src/jingle_message.rs b/src/jingle_message.rs index 1bef32698422508e9b25511095f8cd8656fd4612..2f90d2a5f9da75427b3cb393a087658a9f26a547 100644 --- a/src/jingle_message.rs +++ b/src/jingle_message.rs @@ -8,11 +8,11 @@ use try_from::TryFrom; use minidom::Element; -use error::Error; +use crate::error::Error; -use jingle::SessionId; +use crate::jingle::SessionId; -use ns; +use crate::ns; /// Defines a protocol for broadcasting Jingle requests to all of the clients /// of a user. diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 904a4d77f9db5743ed1d80c6aa1bbb21ad9d6b89..4d660d281530e345f0fd0410cf54f61bf28f336d 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -10,9 +10,9 @@ use std::net::IpAddr; use minidom::Element; use jid::Jid; -use error::Error; +use crate::error::Error; -use ns; +use crate::ns; generate_attribute!( /// The type of the connection being proposed by this candidate. @@ -275,7 +275,7 @@ impl From for Element { mod tests { use super::*; use std::str::FromStr; - use compare_elements::NamespaceAwareCompare; + use crate::compare_elements::NamespaceAwareCompare; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/macros.rs b/src/macros.rs index 3deade1d361315e1d3692e5e13a87947798b565d..da96b67a61b227820390b8df7dbfec6fef79b689 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -24,7 +24,7 @@ macro_rules! get_attr { ($elem:ident, $attr:tt, required, $value:ident, $func:expr) => ( match $elem.attr($attr) { Some($value) => $func, - None => return Err(::error::Error::ParseError(concat!("Required attribute '", $attr, "' missing."))), + None => return Err(crate::error::Error::ParseError(concat!("Required attribute '", $attr, "' missing."))), } ); ($elem:ident, $attr:tt, default, $value:ident, $func:expr) => ( @@ -52,11 +52,11 @@ macro_rules! generate_attribute { ),+ } impl ::std::str::FromStr for $elem { - type Err = ::error::Error; - fn from_str(s: &str) -> Result<$elem, ::error::Error> { + type Err = crate::error::Error; + fn from_str(s: &str) -> Result<$elem, crate::error::Error> { Ok(match s { $($b => $elem::$a),+, - _ => return Err(::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + _ => return Err(crate::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), }) } } @@ -78,11 +78,11 @@ macro_rules! generate_attribute { ),+ } impl ::std::str::FromStr for $elem { - type Err = ::error::Error; - fn from_str(s: &str) -> Result<$elem, ::error::Error> { + type Err = crate::error::Error; + fn from_str(s: &str) -> Result<$elem, crate::error::Error> { Ok(match s { $($b => $elem::$a),+, - _ => return Err(::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + _ => return Err(crate::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), }) } } @@ -111,12 +111,12 @@ macro_rules! generate_attribute { False, } impl ::std::str::FromStr for $elem { - type Err = ::error::Error; - fn from_str(s: &str) -> Result { + type Err = crate::error::Error; + fn from_str(s: &str) -> Result { Ok(match s { "true" | "1" => $elem::True, "false" | "0" => $elem::False, - _ => return Err(::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + _ => return Err(crate::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), }) } } @@ -150,14 +150,14 @@ macro_rules! generate_element_enum { ),+ } impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = ::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { + type Err = crate::error::Error; + fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::error::Error> { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); Ok(match elem.name() { $($enum_name => $elem::$enum,)+ - _ => return Err(::error::Error::ParseError(concat!("This is not a ", $name, " element."))), + _ => return Err(crate::error::Error::ParseError(concat!("This is not a ", $name, " element."))), }) } } @@ -165,7 +165,7 @@ macro_rules! generate_element_enum { fn from(elem: $elem) -> ::minidom::Element { ::minidom::Element::builder(match elem { $($elem::$enum => $enum_name,)+ - }).ns(::ns::$ns) + }).ns(crate::ns::$ns) .build() } } @@ -186,21 +186,21 @@ macro_rules! generate_attribute_enum { ),+ } impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = ::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { + type Err = crate::error::Error; + fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::error::Error> { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); check_no_unknown_attributes!(elem, $name, [$attr]); Ok(match get_attr!(elem, $attr, required) { $($enum_name => $elem::$enum,)+ - _ => return Err(::error::Error::ParseError(concat!("Invalid ", $name, " ", $attr, " value."))), + _ => return Err(crate::error::Error::ParseError(concat!("Invalid ", $name, " ", $attr, " value."))), }) } } impl From<$elem> for ::minidom::Element { fn from(elem: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) - .ns(::ns::$ns) + .ns(crate::ns::$ns) .attr($attr, match elem { $($elem::$enum => $enum_name,)+ }) @@ -215,16 +215,16 @@ macro_rules! check_self { check_self!($elem, $name, $ns, $name); ); ($elem:ident, $name:tt, $ns:ident, $pretty_name:tt) => ( - if !$elem.is($name, ::ns::$ns) { - return Err(::error::Error::ParseError(concat!("This is not a ", $pretty_name, " element."))); + if !$elem.is($name, crate::ns::$ns) { + return Err(crate::error::Error::ParseError(concat!("This is not a ", $pretty_name, " element."))); } ); } macro_rules! check_ns_only { ($elem:ident, $name:tt, $ns:ident) => ( - if !$elem.has_ns(::ns::$ns) { - return Err(::error::Error::ParseError(concat!("This is not a ", $name, " element."))); + if !$elem.has_ns(crate::ns::$ns) { + return Err(crate::error::Error::ParseError(concat!("This is not a ", $name, " element."))); } ); } @@ -232,7 +232,7 @@ macro_rules! check_ns_only { macro_rules! check_no_children { ($elem:ident, $name:tt) => ( for _ in $elem.children() { - return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); + return Err(crate::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); } ); } @@ -240,7 +240,7 @@ macro_rules! check_no_children { macro_rules! check_no_attributes { ($elem:ident, $name:tt) => ( for _ in $elem.attrs() { - return Err(::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); + return Err(crate::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); } ); } @@ -253,7 +253,7 @@ macro_rules! check_no_unknown_attributes { continue; } )* - return Err(::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); + return Err(crate::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); } ); } @@ -265,9 +265,9 @@ macro_rules! generate_empty_element { pub struct $elem; impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = ::error::Error; + type Err = crate::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::error::Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); @@ -278,7 +278,7 @@ macro_rules! generate_empty_element { impl From<$elem> for ::minidom::Element { fn from(_: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) - .ns(::ns::$ns) + .ns(crate::ns::$ns) .build() } } @@ -291,8 +291,8 @@ macro_rules! generate_id { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $elem(pub String); impl ::std::str::FromStr for $elem { - type Err = ::error::Error; - fn from_str(s: &str) -> Result<$elem, ::error::Error> { + type Err = crate::error::Error; + fn from_str(s: &str) -> Result<$elem, crate::error::Error> { // TODO: add a way to parse that differently when needed. Ok($elem(String::from(s))) } @@ -311,15 +311,15 @@ macro_rules! generate_elem_id { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $elem(pub String); impl ::std::str::FromStr for $elem { - type Err = ::error::Error; - fn from_str(s: &str) -> Result<$elem, ::error::Error> { + type Err = crate::error::Error; + fn from_str(s: &str) -> Result<$elem, crate::error::Error> { // TODO: add a way to parse that differently when needed. Ok($elem(String::from(s))) } } impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = ::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { + type Err = crate::error::Error; + fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::error::Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); @@ -330,7 +330,7 @@ macro_rules! generate_elem_id { impl From<$elem> for ::minidom::Element { fn from(elem: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) - .ns(::ns::$ns) + .ns(crate::ns::$ns) .append(elem.0) .build() } @@ -380,13 +380,13 @@ macro_rules! do_parse_elem { ); ($temp:ident: Option = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => ( if $temp.is_some() { - return Err(::error::Error::ParseError(concat!("Element ", $parent_name, " must not have more than one ", $name, " child."))); + return Err(crate::error::Error::ParseError(concat!("Element ", $parent_name, " must not have more than one ", $name, " child."))); } $temp = Some(do_parse!($elem, $constructor)); ); ($temp:ident: Required = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => ( if $temp.is_some() { - return Err(::error::Error::ParseError(concat!("Element ", $parent_name, " must not have more than one ", $name, " child."))); + return Err(crate::error::Error::ParseError(concat!("Element ", $parent_name, " must not have more than one ", $name, " child."))); } $temp = Some(do_parse!($elem, $constructor)); ); @@ -400,21 +400,21 @@ macro_rules! finish_parse_elem { $temp ); ($temp:ident: Required = $name:tt, $parent_name:tt) => ( - $temp.ok_or(::error::Error::ParseError(concat!("Missing child ", $name, " in ", $parent_name, " element.")))? + $temp.ok_or(crate::error::Error::ParseError(concat!("Missing child ", $name, " in ", $parent_name, " element.")))? ); } macro_rules! generate_serialiser { ($parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => ( ::minidom::Element::builder($name) - .ns(::ns::$ns) + .ns(crate::ns::$ns) .append($parent.$elem) .build() ); ($parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => ( $parent.$elem.map(|elem| ::minidom::Element::builder($name) - .ns(::ns::$ns) + .ns(crate::ns::$ns) .append(elem) .build()) ); @@ -461,9 +461,9 @@ macro_rules! generate_element { } impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = ::error::Error; + type Err = crate::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::error::Error> { check_self!(elem, $name, $ns); check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); $( @@ -471,12 +471,12 @@ macro_rules! generate_element { )* for _child in elem.children() { $( - if _child.is($child_name, ::ns::$child_ns) { + if _child.is($child_name, crate::ns::$child_ns) { do_parse_elem!($child_ident: $coucou = $child_constructor => _child, $child_name, $name); continue; } )* - return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); + return Err(crate::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); } Ok($elem { $( @@ -495,7 +495,7 @@ macro_rules! generate_element { impl From<$elem> for ::minidom::Element { fn from(elem: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) - .ns(::ns::$ns) + .ns(crate::ns::$ns) $( .attr($attr_name, elem.$attr) )* diff --git a/src/mam.rs b/src/mam.rs index 819bc46261af2237b84d7e9cee700f8ab62bc1af..ec88ead6fb654e6c0022cbe59975d38959ed1c80 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -9,16 +9,16 @@ use try_from::TryFrom; use minidom::Element; use jid::Jid; -use error::Error; +use crate::error::Error; -use message::MessagePayload; -use iq::{IqGetPayload, IqSetPayload, IqResultPayload}; -use data_forms::DataForm; -use rsm::{SetQuery, SetResult}; -use forwarding::Forwarded; -use pubsub::NodeName; +use crate::message::MessagePayload; +use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload}; +use crate::data_forms::DataForm; +use crate::rsm::{SetQuery, SetResult}; +use crate::forwarding::Forwarded; +use crate::pubsub::NodeName; -use ns; +use crate::ns; generate_id!( /// An identifier matching a result message to the query requesting it. diff --git a/src/media_element.rs b/src/media_element.rs index dc0c2af7a7e86a580ddba48bd60b43cb929766e7..2bc512d8753ee61814a59d65f32197768b6f3188 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use helpers::TrimmedPlainText; +use crate::helpers::TrimmedPlainText; generate_element!( /// Represents an URI used in a media element. @@ -47,8 +47,8 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; - use data_forms::DataForm; + use crate::error::Error; + use crate::data_forms::DataForm; use std::error::Error as StdError; #[cfg(target_pointer_width = "32")] diff --git a/src/message.rs b/src/message.rs index 9c392b790991c6d3f35f0fa96487f553c781d3b4..caae40ab7c81107ea153bd8243b062df9fc1c3ae 100644 --- a/src/message.rs +++ b/src/message.rs @@ -11,9 +11,9 @@ use minidom::Element; use jid::Jid; -use error::Error; +use crate::error::Error; -use ns; +use crate::ns; /// Should be implemented on every known payload of a ``. pub trait MessagePayload: TryFrom + Into {} @@ -232,7 +232,7 @@ impl From for Element { mod tests { use super::*; use std::str::FromStr; - use compare_elements::NamespaceAwareCompare; + use crate::compare_elements::NamespaceAwareCompare; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/message_correct.rs b/src/message_correct.rs index 06b433f9b047702aae15111a31614b0d562d1fc2..24b56919132099984fa576f9bd4939c08ff066c7 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use message::MessagePayload; +use crate::message::MessagePayload; generate_element!( /// Defines that the message containing this payload should replace a @@ -23,7 +23,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; + use crate::error::Error; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 5e5d65e86fdb220c5b16e0668ba77bccd6ea08e3..a49c1bb85656d8fc11f222e8b515cd18d4a8ede2 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -5,8 +5,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use presence::PresencePayload; -use date::DateTime; +use crate::presence::PresencePayload; +use crate::date::DateTime; generate_element!( /// Represents the query for messages before our join. @@ -104,9 +104,9 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; + use crate::error::Error; use std::str::FromStr; - use compare_elements::NamespaceAwareCompare; + use crate::compare_elements::NamespaceAwareCompare; #[test] fn test_muc_simple() { diff --git a/src/muc/user.rs b/src/muc/user.rs index bf0fb3080625282a5d0a0aab474d9c0b1772e246..7d114abd63c82b2d33ccf82c4ed9e8a40c3fab6c 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -11,9 +11,9 @@ use minidom::Element; use jid::Jid; -use error::Error; +use crate::error::Error; -use ns; +use crate::ns; generate_attribute_enum!( /// Lists all of the possible status codes used in MUC presences. @@ -238,7 +238,7 @@ generate_element!( mod tests { use super::*; use std::error::Error as StdError; - use compare_elements::NamespaceAwareCompare; + use crate::compare_elements::NamespaceAwareCompare; #[test] fn test_simple() { diff --git a/src/nick.rs b/src/nick.rs index 7a3666571039a7005541dc04b739d53d4f91fefc..360348bd8761074ce8ff9fa1bad2b51dfaf88982 100644 --- a/src/nick.rs +++ b/src/nick.rs @@ -14,7 +14,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; + use crate::error::Error; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/ping.rs b/src/ping.rs index 557f535a7038ab4417cefed415e5ad22b9c7e7dd..ddb636d3f3fa91d6b7c7883202acd83e14f8bc24 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -5,7 +5,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use iq::IqGetPayload; +use crate::iq::IqGetPayload; generate_empty_element!( /// Represents a ping to the recipient, which must be answered with an @@ -20,7 +20,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; + use crate::error::Error; #[test] fn test_size() { diff --git a/src/presence.rs b/src/presence.rs index 2cb115f5270043d5026251ac84be06be76d21f72..aece0cff7c25c83db0b216c5f5a2e779d20d422e 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -13,9 +13,9 @@ use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; use jid::Jid; -use error::Error; +use crate::error::Error; -use ns; +use crate::ns; /// Should be implemented on every known payload of a ``. pub trait PresencePayload: TryFrom + Into {} @@ -331,7 +331,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use compare_elements::NamespaceAwareCompare; + use crate::compare_elements::NamespaceAwareCompare; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 61b4396ea25e61ebe5bb3c26ebb55c6e72dac82a..c1ccace3983e20bbf0da4fcc4a4898ef90c42c43 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -8,15 +8,15 @@ use try_from::TryFrom; use minidom::Element; use jid::Jid; -use date::DateTime; +use crate::date::DateTime; -use error::Error; +use crate::error::Error; -use ns; +use crate::ns; -use data_forms::DataForm; +use crate::data_forms::DataForm; -use pubsub::{NodeName, ItemId, Subscription, SubscriptionId}; +use crate::pubsub::{NodeName, ItemId, Subscription, SubscriptionId}; /// One PubSub item from a node. #[derive(Debug, Clone)] @@ -288,7 +288,7 @@ impl From for Element { mod tests { use super::*; use std::str::FromStr; - use compare_elements::NamespaceAwareCompare; + use crate::compare_elements::NamespaceAwareCompare; #[test] fn missing_items() { diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index 52f510079dbb6de251df7958e94412d40dd0da0f..cf3d980396cf1efc200985de342927e84b2c7c42 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -9,14 +9,14 @@ use try_from::TryFrom; use minidom::Element; use jid::Jid; -use error::Error; +use crate::error::Error; -use ns; +use crate::ns; -use iq::{IqGetPayload, IqSetPayload, IqResultPayload}; -use data_forms::DataForm; +use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload}; +use crate::data_forms::DataForm; -use pubsub::{NodeName, ItemId, Subscription, SubscriptionId}; +use crate::pubsub::{NodeName, ItemId, Subscription, SubscriptionId}; // TODO: a better solution would be to split this into a query and a result elements, like for // XEP-0030. @@ -497,7 +497,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use compare_elements::NamespaceAwareCompare; + use crate::compare_elements::NamespaceAwareCompare; #[test] fn create() { diff --git a/src/receipts.rs b/src/receipts.rs index 49759c773fe7590890490a709c0d4ffda4f55886..61877f576d2ebbb2d11aef84f53de0b39c20dec9 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use message::MessagePayload; +use crate::message::MessagePayload; generate_empty_element!( /// Requests that this message is acked by the final recipient once @@ -31,7 +31,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use ns; + use crate::ns; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/roster.rs b/src/roster.rs index c1f8641eda53b54c626752d5a083f98b4cb63c9c..86d023098767cb2a530bb2f16cc84d4a8276eaca 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -5,7 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use jid::Jid; -use iq::{IqGetPayload, IqSetPayload, IqResultPayload}; +use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload}; generate_elem_id!( /// Represents a group a contact is part of. @@ -81,9 +81,9 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; + use crate::error::Error; use std::str::FromStr; - use compare_elements::NamespaceAwareCompare; + use crate::compare_elements::NamespaceAwareCompare; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/rsm.rs b/src/rsm.rs index fb321fa55cb76a1b1a02460d0c98df335367167c..6bb02ba736f23d2be3ae3986907d50fb411b51de 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -8,9 +8,9 @@ use try_from::TryFrom; use minidom::Element; -use error::Error; +use crate::error::Error; -use ns; +use crate::ns; /// Requests paging through a potentially big set of items (represented by an /// UID). @@ -156,7 +156,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use compare_elements::NamespaceAwareCompare; + use crate::compare_elements::NamespaceAwareCompare; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/sasl.rs b/src/sasl.rs index 1ec44af087b4b457aa56ec7296d2534aa3eeb9f2..30fa653a6baca3df08534c76befbd3ad905a8de7 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -8,10 +8,10 @@ use std::collections::BTreeMap; use try_from::TryFrom; use minidom::Element; -use error::Error; -use ns; +use crate::error::Error; +use crate::ns; -use helpers::Base64; +use crate::helpers::Base64; generate_attribute!( /// The list of available SASL mechanisms. diff --git a/src/sm.rs b/src/sm.rs index 404b54eb64e2b076a917c4501d2b39285906b284..b0d08d3e62cca65162dab61c306206b3a8715c73 100644 --- a/src/sm.rs +++ b/src/sm.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use stanza_error::DefinedCondition; +use crate::stanza_error::DefinedCondition; generate_element!( /// Acknowledgement of the currently received stanzas. diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 750823d382b7ea5ce723222c22a0ca8e5b18f93d..ec31ecb1d5d23d7562b580af934eef458c6bd11c 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -9,11 +9,11 @@ use std::collections::BTreeMap; use minidom::Element; -use message::MessagePayload; -use presence::PresencePayload; -use error::Error; +use crate::message::MessagePayload; +use crate::presence::PresencePayload; +use crate::error::Error; use jid::Jid; -use ns; +use crate::ns; generate_attribute!( /// The type of the error. diff --git a/src/stanza_id.rs b/src/stanza_id.rs index e45b528c0c16c665908c3dab11a1b89179b4294b..87595d4324bc9a26ba8b13f6a76ff3f3ebfc0087 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use message::MessagePayload; +use crate::message::MessagePayload; use jid::Jid; generate_element!( @@ -39,7 +39,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; + use crate::error::Error; use std::str::FromStr; #[cfg(target_pointer_width = "32")] diff --git a/src/version.rs b/src/version.rs index cbd958395d24e2b949c5c995c33dc90472d8d8f9..4e969f4a2004dfa6457b543de1ffcfc593b4acea 100644 --- a/src/version.rs +++ b/src/version.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use iq::{IqGetPayload, IqResultPayload}; +use crate::iq::{IqGetPayload, IqResultPayload}; generate_empty_element!( /// Represents a query for the software version a remote entity is using. @@ -41,7 +41,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use compare_elements::NamespaceAwareCompare; + use crate::compare_elements::NamespaceAwareCompare; #[cfg(target_pointer_width = "32")] #[test] From d517b8e32e4fe4552a7811aaa1ca21bfa236eb03 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 18 Dec 2018 15:29:30 +0100 Subject: [PATCH 538/698] Bump base64 and try_from crates. --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8fd7253bf291da6fcd186a53058766ae6d1c6611..2d3122cb9084fd17e1434003c591f8e2036d1454 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,14 +16,14 @@ edition = "2018" [dependencies] minidom = "0.9.1" jid = { version = "0.5.2", features = ["minidom"] } -base64 = "0.9.2" +base64 = "0.10" digest = "0.8" sha-1 = "0.8" sha2 = "0.8" sha3 = "0.8" blake2 = "0.8" chrono = "0.4.5" -try_from = "0.2.2" +try_from = "0.3.2" [features] # Build xmpp-parsers to make components instead of clients. From efd7bd5f2f407d803ea6900e3565b9684af23a75 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 18 Dec 2018 15:32:05 +0100 Subject: [PATCH 539/698] Run `cargo fmt`. --- src/attention.rs | 16 +- src/bind.rs | 43 +++-- src/blocking.rs | 44 ++++-- src/bookmarks.rs | 13 +- src/caps.rs | 81 +++++----- src/chatstates.rs | 20 ++- src/compare_elements.rs | 23 +-- src/component.rs | 21 ++- src/data_forms.rs | 100 +++++++----- src/date.rs | 11 +- src/delay.rs | 32 ++-- src/disco.rs | 127 ++++++++++----- src/ecaps2.rs | 336 ++++++++++++++++++++-------------------- src/eme.rs | 25 ++- src/error.rs | 11 +- src/forwarding.rs | 13 +- src/hashes.rs | 27 ++-- src/helpers.rs | 6 +- src/ibb.rs | 35 +++-- src/ibr.rs | 93 +++++++---- src/idle.rs | 48 ++++-- src/iq.rs | 109 ++++++++----- src/jingle.rs | 74 +++++---- src/jingle_ft.rs | 196 +++++++++++++++-------- src/jingle_ibb.rs | 32 +++- src/jingle_message.rs | 68 ++++---- src/jingle_s5b.rs | 115 +++++++------- src/lib.rs | 18 +-- src/macros.rs | 161 ++++++++++++------- src/mam.rs | 112 +++++++++----- src/media_element.rs | 82 +++++++--- src/message.rs | 140 ++++++++++------- src/message_correct.rs | 28 +++- src/mood.rs | 14 +- src/muc/muc.rs | 38 +++-- src/muc/user.rs | 183 +++++++++++++++------- src/nick.rs | 24 ++- src/ping.rs | 12 +- src/presence.rs | 226 ++++++++++++++++++--------- src/pubsub/event.rs | 226 ++++++++++++++++----------- src/pubsub/pubsub.rs | 217 +++++++++++++++++--------- src/receipts.rs | 12 +- src/roster.rs | 84 +++++++--- src/rsm.rs | 94 ++++++++--- src/sasl.rs | 65 +++++--- src/sm.rs | 30 +++- src/stanza_error.rs | 60 ++++--- src/stanza_id.rs | 29 +++- src/stream.rs | 2 +- src/version.rs | 18 ++- src/websocket.rs | 6 +- 51 files changed, 2276 insertions(+), 1324 deletions(-) diff --git a/src/attention.rs b/src/attention.rs index e71b26446f9a2335c5a548b04aad51de44edd5a0..be33bb3b6f07d002163bfa2ee46d4f3a7bc6b6c3 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -8,7 +8,9 @@ use crate::message::MessagePayload; generate_empty_element!( /// Requests the attention of the recipient. - Attention, "attention", ATTENTION + Attention, + "attention", + ATTENTION ); impl MessagePayload for Attention {} @@ -16,9 +18,9 @@ impl MessagePayload for Attention {} #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; use crate::error::Error; + use minidom::Element; + use try_from::TryFrom; #[test] fn test_size() { @@ -33,7 +35,9 @@ mod tests { #[test] fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Attention::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -44,7 +48,9 @@ mod tests { #[test] fn test_invalid_attribute() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Attention::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, diff --git a/src/bind.rs b/src/bind.rs index 81187ae3e76123b369d065448c5fe2e0045c1c98..ddda0392b67b2105977923cf0f47e62a4dbf8e75 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -4,15 +4,13 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::str::FromStr; -use try_from::TryFrom; - -use minidom::Element; - use crate::error::Error; -use jid::Jid; +use crate::iq::{IqResultPayload, IqSetPayload}; use crate::ns; -use crate::iq::{IqSetPayload, IqResultPayload}; +use jid::Jid; +use minidom::Element; +use std::str::FromStr; +use try_from::TryFrom; /// The request for resource binding, which is the process by which a client /// can obtain a full JID and start exchanging on the XMPP network. @@ -74,23 +72,16 @@ impl TryFrom for Bind { impl From for Element { fn from(bind: Bind) -> Element { Element::builder("bind") - .ns(ns::BIND) - .append(match bind { - Bind::None => vec!(), - Bind::Resource(resource) => vec!( - Element::builder("resource") - .ns(ns::BIND) - .append(resource) - .build() - ), - Bind::Jid(jid) => vec!( - Element::builder("jid") - .ns(ns::BIND) - .append(jid) - .build() - ), - }) - .build() + .ns(ns::BIND) + .append(match bind { + Bind::None => vec![], + Bind::Resource(resource) => vec![Element::builder("resource") + .ns(ns::BIND) + .append(resource) + .build()], + Bind::Jid(jid) => vec![Element::builder("jid").ns(ns::BIND).append(jid).build()], + }) + .build() } } @@ -112,7 +103,9 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let bind = Bind::try_from(elem).unwrap(); assert_eq!(bind, Bind::None); } diff --git a/src/blocking.rs b/src/blocking.rs index fd89c4846e38b2562ebf23ee0e5458aac46cb361..d34fee3d97a1935c5d825e7bfc836408e74885a2 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -4,20 +4,19 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - -use jid::Jid; -use minidom::Element; - use crate::error::Error; - +use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; -use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload}; +use jid::Jid; +use minidom::Element; +use try_from::TryFrom; generate_empty_element!( /// The element requesting the blocklist, the result iq will contain a /// [BlocklistResult]. - BlocklistRequest, "blocklist", BLOCKING + BlocklistRequest, + "blocklist", + BLOCKING ); impl IqGetPayload for BlocklistRequest {} @@ -67,7 +66,8 @@ macro_rules! generate_blocking_element { generate_blocking_element!( /// The element containing the current blocklist, as a reply from /// [BlocklistRequest]. - BlocklistResult, "blocklist" + BlocklistResult, + "blocklist" ); impl IqResultPayload for BlocklistResult {} @@ -75,7 +75,8 @@ impl IqResultPayload for BlocklistResult {} // TODO: Prevent zero elements from being allowed. generate_blocking_element!( /// A query to block one or more JIDs. - Block, "block" + Block, + "block" ); impl IqSetPayload for Block {} @@ -84,14 +85,17 @@ generate_blocking_element!( /// A query to unblock one or more JIDs, or all of them. /// /// Warning: not putting any JID there means clearing out the blocklist. - Unblock, "unblock" + Unblock, + "unblock" ); impl IqSetPayload for Unblock {} generate_empty_element!( /// The application-specific error condition when a message is blocked. - Blocked, "blocked", BLOCKING_ERRORS + Blocked, + "blocked", + BLOCKING_ERRORS ); #[cfg(test)] @@ -138,7 +142,7 @@ mod tests { #[test] fn test_items() { let elem: Element = "".parse().unwrap(); - let two_items = vec!( + let two_items = vec![ Jid { node: Some(String::from("coucou")), domain: String::from("coucou"), @@ -149,7 +153,7 @@ mod tests { domain: String::from("domain"), resource: None, }, - ); + ]; let request_elem = elem.clone(); let error = BlocklistRequest::try_from(request_elem).unwrap_err(); @@ -174,7 +178,9 @@ mod tests { #[test] fn test_invalid() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let request_elem = elem.clone(); let error = BlocklistRequest::try_from(request_elem).unwrap_err(); let message = match error { @@ -191,7 +197,9 @@ mod tests { }; assert_eq!(message, "Unknown attribute in blocklist element."); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Block::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -199,7 +207,9 @@ mod tests { }; assert_eq!(message, "Unknown attribute in block element."); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Unblock::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, diff --git a/src/bookmarks.rs b/src/bookmarks.rs index 8f88845fe81d2c476906aa1dfeff01287a1190e5..6048b2f074023e7df09ef2103777d42fcdd6e0c9 100644 --- a/src/bookmarks.rs +++ b/src/bookmarks.rs @@ -8,7 +8,9 @@ use jid::Jid; generate_attribute!( /// Whether a conference bookmark should be joined automatically. - Autojoin, "autojoin", bool + Autojoin, + "autojoin", + bool ); generate_element!( @@ -70,9 +72,9 @@ impl Storage { #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; use crate::compare_elements::NamespaceAwareCompare; + use minidom::Element; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -111,7 +113,10 @@ mod tests { assert_eq!(storage.urls[0].url, "https://example.org/"); assert_eq!(storage.conferences.len(), 1); assert_eq!(storage.conferences[0].autojoin, Autojoin::True); - assert_eq!(storage.conferences[0].jid, Jid::bare("test-muc", "muc.localhost")); + assert_eq!( + storage.conferences[0].jid, + Jid::bare("test-muc", "muc.localhost") + ); assert_eq!(storage.conferences[0].name, "Test MUC"); assert_eq!(storage.conferences[0].clone().nick.unwrap(), "Coucou"); assert_eq!(storage.conferences[0].clone().password.unwrap(), "secret"); diff --git a/src/caps.rs b/src/caps.rs index ce44e54c4749c2db1d470c9440d6c08300b44380..72b04263f14bfd58a51276de57be39026c63d21e 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -4,23 +4,20 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - -use crate::presence::PresencePayload; -use crate::disco::{Feature, Identity, DiscoInfoResult, DiscoInfoQuery}; use crate::data_forms::DataForm; -use crate::hashes::{Hash, Algo}; - -use minidom::Element; +use crate::disco::{DiscoInfoQuery, DiscoInfoResult, Feature, Identity}; use crate::error::Error; +use crate::hashes::{Algo, Hash}; use crate::ns; +use crate::presence::PresencePayload; use base64; - +use blake2::VarBlake2b; +use digest::{Digest, Input, VariableOutput}; +use minidom::Element; use sha1::Sha1; use sha2::{Sha256, Sha512}; use sha3::{Sha3_256, Sha3_512}; -use blake2::VarBlake2b; -use digest::{Digest, VariableOutput, Input}; +use try_from::TryFrom; /// Represents a capability hash for a given client. #[derive(Debug, Clone)] @@ -65,12 +62,12 @@ impl TryFrom for Caps { impl From for Element { fn from(caps: Caps) -> Element { Element::builder("c") - .ns(ns::CAPS) - .attr("ext", caps.ext) - .attr("hash", caps.hash.algo) - .attr("node", caps.node) - .attr("ver", base64::encode(&caps.hash.hash)) - .build() + .ns(ns::CAPS) + .attr("ext", caps.ext) + .attr("hash", caps.hash.algo) + .attr("node", caps.node) + .attr("ver", base64::encode(&caps.hash.hash)) + .build() } } @@ -81,8 +78,8 @@ fn compute_item(field: &str) -> Vec { } fn compute_items Vec>(things: &[T], encode: F) -> Vec { - let mut string: Vec = vec!(); - let mut accumulator: Vec> = vec!(); + let mut string: Vec = vec![]; + let mut accumulator: Vec> = vec![]; for thing in things { let bytes = encode(thing); accumulator.push(bytes); @@ -114,7 +111,7 @@ fn compute_identities(identities: &[Identity]) -> Vec { fn compute_extensions(extensions: &[DataForm]) -> Vec { compute_items(extensions, |extension| { - let mut bytes = vec!(); + let mut bytes = vec![]; // TODO: maybe handle the error case? if let Some(ref form_type) = extension.form_type { bytes.extend_from_slice(form_type.as_bytes()); @@ -125,8 +122,9 @@ fn compute_extensions(extensions: &[DataForm]) -> Vec { continue; } bytes.append(&mut compute_item(&field.var)); - bytes.append(&mut compute_items(&field.values, - |value| compute_item(value))); + bytes.append(&mut compute_items(&field.values, |value| { + compute_item(value) + })); } bytes }) @@ -143,7 +141,7 @@ pub fn compute_disco(disco: &DiscoInfoResult) -> Vec { let features_string = compute_features(&disco.features); let extensions_string = compute_extensions(&disco.extensions); - let mut final_string = vec!(); + let mut final_string = vec![]; final_string.extend(identities_string); final_string.extend(features_string); final_string.extend(extensions_string); @@ -164,33 +162,33 @@ pub fn hash_caps(data: &[u8], algo: Algo) -> Result { Algo::Sha_1 => { let hash = Sha1::digest(data); get_hash_vec(hash.as_slice()) - }, + } Algo::Sha_256 => { let hash = Sha256::digest(data); get_hash_vec(hash.as_slice()) - }, + } Algo::Sha_512 => { let hash = Sha512::digest(data); get_hash_vec(hash.as_slice()) - }, + } Algo::Sha3_256 => { let hash = Sha3_256::digest(data); get_hash_vec(hash.as_slice()) - }, + } Algo::Sha3_512 => { let hash = Sha3_512::digest(data); get_hash_vec(hash.as_slice()) - }, + } Algo::Blake2b_256 => { let mut hasher = VarBlake2b::new(32).unwrap(); hasher.input(data); hasher.vec_result() - }, + } Algo::Blake2b_512 => { let mut hasher = VarBlake2b::new(64).unwrap(); hasher.input(data); hasher.vec_result() - }, + } Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)), }, algo: algo, @@ -229,7 +227,10 @@ mod tests { let caps = Caps::try_from(elem).unwrap(); assert_eq!(caps.node, String::from("coucou")); assert_eq!(caps.hash.algo, Algo::Sha_256); - assert_eq!(caps.hash.hash, base64::decode("K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=").unwrap()); + assert_eq!( + caps.hash.hash, + base64::decode("K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=").unwrap() + ); } #[test] @@ -262,8 +263,10 @@ mod tests { -"#.parse().unwrap(); - +"# + .parse() + .unwrap(); + let data = b"client/pc//Exodus 0.9.1".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); ChatState::try_from(elem).unwrap(); } #[test] fn test_invalid() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = ChatState::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -61,7 +65,9 @@ mod tests { #[test] fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = ChatState::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -72,7 +78,9 @@ mod tests { #[test] fn test_invalid_attribute() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = ChatState::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, diff --git a/src/compare_elements.rs b/src/compare_elements.rs index 85c81e75e8e5168870f9739a7f014886cbea1935..df2e4157cc2930f7bc6625280e352407bbe532ae 100644 --- a/src/compare_elements.rs +++ b/src/compare_elements.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::{Node, Element}; +use minidom::{Element, Node}; pub trait NamespaceAwareCompare { /// Namespace-aware comparison for tests @@ -14,10 +14,10 @@ pub trait NamespaceAwareCompare { impl NamespaceAwareCompare for Node { fn compare_to(&self, other: &Self) -> bool { match (self, other) { - (&Node::Element(ref elem1), &Node::Element(ref elem2)) => - Element::compare_to(elem1, elem2), - (&Node::Text(ref text1), &Node::Text(ref text2)) => - text1 == text2, + (&Node::Element(ref elem1), &Node::Element(ref elem2)) => { + Element::compare_to(elem1, elem2) + } + (&Node::Text(ref text1), &Node::Text(ref text2)) => text1 == text2, _ => false, } } @@ -25,20 +25,21 @@ impl NamespaceAwareCompare for Node { impl NamespaceAwareCompare for Element { fn compare_to(&self, other: &Self) -> bool { - if self.name() == other.name() && - self.ns() == other.ns() && - self.attrs().eq(other.attrs()) + if self.name() == other.name() && self.ns() == other.ns() && self.attrs().eq(other.attrs()) { let child_elems = self.children().count(); - let text_is_whitespace = self.texts() + let text_is_whitespace = self + .texts() .all(|text| text.chars().all(char::is_whitespace)); if child_elems > 0 && text_is_whitespace { // Ignore all the whitespace text nodes - self.children().zip(other.children()) + self.children() + .zip(other.children()) .all(|(node1, node2)| node1.compare_to(node2)) } else { // Compare with text nodes - self.nodes().zip(other.nodes()) + self.nodes() + .zip(other.nodes()) .all(|(node1, node2)| node1.compare_to(node2)) } } else { diff --git a/src/component.rs b/src/component.rs index 1fbb7ff35710b108c53d39282bbbe8f2652cbd38..cb42fd7a319e9b2223e93b8ff6fb2b104ced3af4 100644 --- a/src/component.rs +++ b/src/component.rs @@ -5,8 +5,8 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::helpers::PlainText; -use sha1::Sha1; use digest::Digest; +use sha1::Sha1; generate_element!( /// The main authentication mechanism for components. @@ -25,9 +25,7 @@ generate_element!( impl Handshake { /// Creates a successful reply from a server. pub fn new() -> Handshake { - Handshake { - data: None, - } + Handshake { data: None } } /// Creates an authentication request from the component. @@ -44,8 +42,8 @@ impl Handshake { #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; use minidom::Element; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -61,11 +59,15 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let handshake = Handshake::try_from(elem).unwrap(); assert_eq!(handshake.data, None); - let elem: Element = "Coucou".parse().unwrap(); + let elem: Element = "Coucou" + .parse() + .unwrap(); let handshake = Handshake::try_from(elem).unwrap(); assert_eq!(handshake.data, Some(String::from("Coucou"))); } @@ -76,6 +78,9 @@ mod tests { assert_eq!(handshake.data, None); let handshake = Handshake::from_password_and_stream_id("123456", "sid"); - assert_eq!(handshake.data, Some(String::from("9accec263ab84a43c6037ccf7cd48cb1d3f6df8e"))); + assert_eq!( + handshake.data, + Some(String::from("9accec263ab84a43c6037ccf7cd48cb1d3f6df8e")) + ); } } diff --git a/src/data_forms.rs b/src/data_forms.rs index 622820d329d446b38db7b57032a652bdd75d69f4..9d7608dfc3cceb0fa504a77375568f9cda421542 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -4,14 +4,11 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - -use minidom::Element; - use crate::error::Error; -use crate::ns; - use crate::media_element::MediaElement; +use crate::ns; +use minidom::Element; +use try_from::TryFrom; generate_element!( /// Represents one of the possible values for a list- field. @@ -98,8 +95,7 @@ pub struct Field { impl Field { fn is_list(&self) -> bool { - self.type_ == FieldType::ListSingle || - self.type_ == FieldType::ListMulti + self.type_ == FieldType::ListSingle || self.type_ == FieldType::ListMulti } } @@ -114,9 +110,9 @@ impl TryFrom for Field { type_: get_attr!(elem, "type", default), label: get_attr!(elem, "label", optional), required: false, - options: vec!(), - values: vec!(), - media: vec!(), + options: vec![], + values: vec![], + media: vec![], }; for element in elem.children() { if element.is("value", ns::DATA_FORMS) { @@ -140,7 +136,9 @@ impl TryFrom for Field { let media_element = MediaElement::try_from(element.clone())?; field.media.push(media_element); } else { - return Err(Error::ParseError("Field child isn’t a value, option or media element.")); + return Err(Error::ParseError( + "Field child isn’t a value, option or media element.", + )); } } Ok(field) @@ -150,17 +148,30 @@ impl TryFrom for Field { impl From for Element { fn from(field: Field) -> Element { Element::builder("field") - .ns(ns::DATA_FORMS) - .attr("var", field.var) - .attr("type", field.type_) - .attr("label", field.label) - .append(if field.required { Some(Element::builder("required").ns(ns::DATA_FORMS).build()) } else { None }) - .append(field.options) - .append(field.values.into_iter().map(|value| { - Element::builder("value").ns(ns::DATA_FORMS).append(value).build() - }).collect::>()) - .append(field.media) - .build() + .ns(ns::DATA_FORMS) + .attr("var", field.var) + .attr("type", field.type_) + .attr("label", field.label) + .append(if field.required { + Some(Element::builder("required").ns(ns::DATA_FORMS).build()) + } else { + None + }) + .append(field.options) + .append( + field + .values + .into_iter() + .map(|value| { + Element::builder("value") + .ns(ns::DATA_FORMS) + .append(value) + .build() + }) + .collect::>(), + ) + .append(field.media) + .build() } } @@ -215,7 +226,7 @@ impl TryFrom for DataForm { form_type: None, title: None, instructions: None, - fields: vec!(), + fields: vec![], }; for child in elem.children() { if child.is("title", ns::DATA_FORMS) { @@ -227,7 +238,9 @@ impl TryFrom for DataForm { form.title = Some(child.text()); } else if child.is("instructions", ns::DATA_FORMS) { if form.instructions.is_some() { - return Err(Error::ParseError("More than one instructions in form element.")); + return Err(Error::ParseError( + "More than one instructions in form element.", + )); } check_no_children!(child, "instructions"); check_no_attributes!(child, "instructions"); @@ -255,12 +268,19 @@ impl TryFrom for DataForm { impl From for Element { fn from(form: DataForm) -> Element { Element::builder("x") - .ns(ns::DATA_FORMS) - .attr("type", form.type_) - .append(form.title.map(|title| Element::builder("title").ns(ns::DATA_FORMS).append(title))) - .append(form.instructions.map(|text| Element::builder("instructions").ns(ns::DATA_FORMS).append(text))) - .append(form.fields) - .build() + .ns(ns::DATA_FORMS) + .attr("type", form.type_) + .append( + form.title + .map(|title| Element::builder("title").ns(ns::DATA_FORMS).append(title)), + ) + .append(form.instructions.map(|text| { + Element::builder("instructions") + .ns(ns::DATA_FORMS) + .append(text) + })) + .append(form.fields) + .build() } } @@ -318,7 +338,9 @@ mod tests { #[test] fn test_wrong_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = DataForm::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -329,12 +351,17 @@ mod tests { #[test] fn option() { - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let option = Option_::try_from(elem).unwrap(); assert_eq!(&option.label.unwrap(), "Coucou !"); assert_eq!(&option.value, "coucou"); - let elem: Element = " -"#.parse().unwrap(); - let expected = vec![103, 97, 109, 101, 115, 58, 98, 111, 97, 114, 100, - 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, - 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 97, - 99, 116, 105, 118, 105, 116, 121, 31, 104, 116, 116, 112, 58, 47, - 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, - 111, 116, 111, 99, 111, 108, 47, 97, 99, 116, 105, 118, 105, 116, - 121, 43, 110, 111, 116, 105, 102, 121, 31, 104, 116, 116, 112, 58, - 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, - 111, 116, 111, 99, 111, 108, 47, 98, 121, 116, 101, 115, 116, 114, - 101, 97, 109, 115, 31, 104, 116, 116, 112, 58,47, 47, 106, 97, 98, - 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, - 111, 108, 47, 99, 104, 97, 116, 115, 116, 97, 116, 101, 115, 31, - 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, - 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 99, 111, - 109, 109, 97, 110, 100, 115, 31,104,116, 116, 112, 58, 47, 47, 106, - 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, - 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105, 110, 102, - 111, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, - 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, - 100, 105, 115, 99, 111, 35, 105, 116, 101, 109, 115, 31, 104, 116, - 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, - 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 101, 118, 105, 108, - 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, - 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 102, - 101, 97, 116, 117, 114, 101, 45, 110, 101, 103, 31, 104, 116, 116, - 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, - 112, 114, 111, 116, 111, 99, 111, 108, 47, 103, 101, 111, 108, 111, - 99, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, - 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99,111, 108, 47, - 103, 101, 111, 108, 111, 99, 43, 110, 111, 116, 105, 102, 121, 31, - 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, - 114, 103,47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 105, 98, - 98, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, - 46, 111, 114, 103, 47, 112, 114, 111,116, 111, 99, 111, 108, 47, - 105, 113, 105, 98, 98, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, - 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116,111, - 99, 111, 108, 47, 109, 111, 111, 100, 31, 104, 116, 116, 112, 58, - 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, - 111, 116, 111, 99, 111,108, 47, 109, 111, 111, 100, 43, 110, 111, - 116, 105, 102, 121, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, - 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, - 99, 111, 108, 47, 114, 111, 115, 116, 101, 114, 120, 31, 104, 116, - 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, - 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 115, 105, 31, 104, - 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, - 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 115, 105, 47, - 112, 114, 111, 102, 105, 108, 101, 47, 102, 105, 108, 101, 45, 116, - 114, 97, 110, 115, 102, 101, 114, 31, 104, 116, 116, 112, 58, 47, - 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, - 111, 116, 111, 99, 111, 108, 47, 116, 117, 110, 101, 31, 104, 116, - 116, 112, 58, 47, 47, 119, 119, 119, 46, 102, 97, 99, 101, 98, 111, - 111, 107, 46, 99, 111, 109, 47, 120, 109, 112, 112, 47, 109, 101, - 115, 115, 97, 103, 101, 115, 31, 104, 116, 116, 112, 58, 47, 47, - 119, 119, 119, 46, 120, 109, 112, 112, 46, 111, 114, 103, 47, 101, - 120, 116, 101, 110, 115, 105, 111, 110, 115, 47, 120, 101, 112, 45, - 48, 48, 56, 52, 46, 104, 116, 109, 108, 35, 110, 115, 45, 109, 101, - 116, 97, 100, 97, 116, 97, 43, 110, 111, 116, 105, 102, 121, 31, - 106, 97, 98, 98, 101, 114,58, 105,113, 58, 97, 118, 97, 116, 97, - 114, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 98, 114, 111, - 119, 115, 101, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, - 100, 116, 99, 112, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, - 102, 105, 108, 101, 120, 102, 101, 114, 31, 106, 97, 98, 98, 101, - 114, 58, 105, 113, 58, 105, 98, 98, 31, 106, 97, 98, 98, 101, 114, - 58, 105, 113, 58, 105, 110, 98, 97, 110, 100, 31, 106, 97, 98, 98, - 101, 114, 58, 105, 113, 58, 106, 105, 100, 108, 105, 110, 107, 31, - 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 108, 97, 115, 116, 31, - 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 111, 111, 98, 31, 106, - 97,98, 98, 101, 114, 58, 105, 113, 58, 112, 114, 105, 118, 97, 99, - 121, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 114, 111, - 115, 116, 101, 114,31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, - 116, 105, 109, 101, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, - 58, 118, 101, 114, 115, 105, 111, 110, 31, 106, 97, 98, 98, 101, - 114, 58, 120, 58, 100, 97, 116, 97, 31, 106, 97, 98, 98, 101, 114, - 58, 120, 58, 101, 118, 101, 110, 116, 31, 106, 97, 98, 98, 101, - 114, 58, 120, 58, 111, 111, 98, 31, 117, 114, 110, 58, 120, 109, - 112, 112, 58, 97, 118, 97, 116, 97, 114, 58, 109, 101, 116, 97, - 100, 97, 116, 97, 43, 110, 111, 116, 105, 102, 121,31, 117, 114, - 110, 58, 120, 109, 112, 112, 58, 112, 105, 110, 103, 31, 117, 114, - 110, 58, 120, 109, 112, 112, 58, 114, 101, 99, 101, 105, 112, 116, - 115, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 116, 105, 109, - 101, 31, 28, 99, 108, 105, 101, 110, 116, 31, 112, 99, 31, 101, - 110, 31, 84, 107, 97, 98, 98, 101, 114,31, 30, 99, 108, 105, 101, - 110, 116, 31, 112, 99, 31, 114, 117, 31, 208, 162, 208, 186, 208, - 176, 208, 177, 208, 177, 208, 181, 209, 128, 31, 30, 28, 70, 79, - 82, 77, 95, 84, 89, 80, 69, 31, 117, 114, 110, 58, 120, 109, 112, - 112, 58, 100, 97, 116, 97, 102, 111, 114, 109, 115, 58, 115, 111, - 102, 116, 119, 97, 114, 101,105, 110, 102, 111, 31, 30, 111, 115, - 31, 87, 105, 110, 100, 111, 119, 115, 31, 30, 111, 115, 95, 118, - 101, 114, 115, 105, 111, 110, 31, 88, 80, 31, 30, 115, 111, 102, - 116, 119, 97, 114, 101, 31, 84, 107, 97, 98, 98, 101, 114, 31, 30, - 115, 111, 102, 116, 119, 97, 114, 101, 95, 118, 101, 114, 115, 105, - 111, 110, 31, 48, 46, 49, 49, 46, 49, 45, 115, 118, 110, 45, 50, - 48, 49, 49, 49, 50, 49, 54, 45, 109, 111, 100, 32, 40, 84, 99, 108, - 47, 84, 107, 32, 56, 46,54, 98, 50, 41, 31, 30, 29, 28]; +"# + .parse() + .unwrap(); + let expected = vec![ + 103, 97, 109, 101, 115, 58, 98, 111, 97, 114, 100, 31, 104, 116, 116, 112, 58, 47, 47, + 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, + 108, 47, 97, 99, 116, 105, 118, 105, 116, 121, 31, 104, 116, 116, 112, 58, 47, 47, 106, + 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, + 97, 99, 116, 105, 118, 105, 116, 121, 43, 110, 111, 116, 105, 102, 121, 31, 104, 116, + 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, + 116, 111, 99, 111, 108, 47, 98, 121, 116, 101, 115, 116, 114, 101, 97, 109, 115, 31, + 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, + 114, 111, 116, 111, 99, 111, 108, 47, 99, 104, 97, 116, 115, 116, 97, 116, 101, 115, + 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, + 112, 114, 111, 116, 111, 99, 111, 108, 47, 99, 111, 109, 109, 97, 110, 100, 115, 31, + 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, + 114, 111, 116, 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105, 110, 102, 111, + 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, + 112, 114, 111, 116, 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105, 116, 101, + 109, 115, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, + 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 101, 118, 105, 108, 31, 104, 116, + 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, + 116, 111, 99, 111, 108, 47, 102, 101, 97, 116, 117, 114, 101, 45, 110, 101, 103, 31, + 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, + 114, 111, 116, 111, 99, 111, 108, 47, 103, 101, 111, 108, 111, 99, 31, 104, 116, 116, + 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, + 111, 99, 111, 108, 47, 103, 101, 111, 108, 111, 99, 43, 110, 111, 116, 105, 102, 121, + 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, + 112, 114, 111, 116, 111, 99, 111, 108, 47, 105, 98, 98, 31, 104, 116, 116, 112, 58, 47, + 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, + 108, 47, 105, 113, 105, 98, 98, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, + 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 109, 111, + 111, 100, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, + 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 109, 111, 111, 100, 43, 110, 111, + 116, 105, 102, 121, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, + 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 114, 111, 115, 116, 101, + 114, 120, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, + 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 115, 105, 31, 104, 116, 116, 112, + 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, + 99, 111, 108, 47, 115, 105, 47, 112, 114, 111, 102, 105, 108, 101, 47, 102, 105, 108, + 101, 45, 116, 114, 97, 110, 115, 102, 101, 114, 31, 104, 116, 116, 112, 58, 47, 47, + 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, + 108, 47, 116, 117, 110, 101, 31, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46, + 102, 97, 99, 101, 98, 111, 111, 107, 46, 99, 111, 109, 47, 120, 109, 112, 112, 47, 109, + 101, 115, 115, 97, 103, 101, 115, 31, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, + 46, 120, 109, 112, 112, 46, 111, 114, 103, 47, 101, 120, 116, 101, 110, 115, 105, 111, + 110, 115, 47, 120, 101, 112, 45, 48, 48, 56, 52, 46, 104, 116, 109, 108, 35, 110, 115, + 45, 109, 101, 116, 97, 100, 97, 116, 97, 43, 110, 111, 116, 105, 102, 121, 31, 106, 97, + 98, 98, 101, 114, 58, 105, 113, 58, 97, 118, 97, 116, 97, 114, 31, 106, 97, 98, 98, + 101, 114, 58, 105, 113, 58, 98, 114, 111, 119, 115, 101, 31, 106, 97, 98, 98, 101, 114, + 58, 105, 113, 58, 100, 116, 99, 112, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, + 102, 105, 108, 101, 120, 102, 101, 114, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, + 58, 105, 98, 98, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 105, 110, 98, 97, + 110, 100, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 106, 105, 100, 108, 105, + 110, 107, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 108, 97, 115, 116, 31, 106, + 97, 98, 98, 101, 114, 58, 105, 113, 58, 111, 111, 98, 31, 106, 97, 98, 98, 101, 114, + 58, 105, 113, 58, 112, 114, 105, 118, 97, 99, 121, 31, 106, 97, 98, 98, 101, 114, 58, + 105, 113, 58, 114, 111, 115, 116, 101, 114, 31, 106, 97, 98, 98, 101, 114, 58, 105, + 113, 58, 116, 105, 109, 101, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 118, 101, + 114, 115, 105, 111, 110, 31, 106, 97, 98, 98, 101, 114, 58, 120, 58, 100, 97, 116, 97, + 31, 106, 97, 98, 98, 101, 114, 58, 120, 58, 101, 118, 101, 110, 116, 31, 106, 97, 98, + 98, 101, 114, 58, 120, 58, 111, 111, 98, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, + 97, 118, 97, 116, 97, 114, 58, 109, 101, 116, 97, 100, 97, 116, 97, 43, 110, 111, 116, + 105, 102, 121, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 112, 105, 110, 103, 31, + 117, 114, 110, 58, 120, 109, 112, 112, 58, 114, 101, 99, 101, 105, 112, 116, 115, 31, + 117, 114, 110, 58, 120, 109, 112, 112, 58, 116, 105, 109, 101, 31, 28, 99, 108, 105, + 101, 110, 116, 31, 112, 99, 31, 101, 110, 31, 84, 107, 97, 98, 98, 101, 114, 31, 30, + 99, 108, 105, 101, 110, 116, 31, 112, 99, 31, 114, 117, 31, 208, 162, 208, 186, 208, + 176, 208, 177, 208, 177, 208, 181, 209, 128, 31, 30, 28, 70, 79, 82, 77, 95, 84, 89, + 80, 69, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 100, 97, 116, 97, 102, 111, 114, + 109, 115, 58, 115, 111, 102, 116, 119, 97, 114, 101, 105, 110, 102, 111, 31, 30, 111, + 115, 31, 87, 105, 110, 100, 111, 119, 115, 31, 30, 111, 115, 95, 118, 101, 114, 115, + 105, 111, 110, 31, 88, 80, 31, 30, 115, 111, 102, 116, 119, 97, 114, 101, 31, 84, 107, + 97, 98, 98, 101, 114, 31, 30, 115, 111, 102, 116, 119, 97, 114, 101, 95, 118, 101, 114, + 115, 105, 111, 110, 31, 48, 46, 49, 49, 46, 49, 45, 115, 118, 110, 45, 50, 48, 49, 49, + 49, 50, 49, 54, 45, 109, 111, 100, 32, 40, 84, 99, 108, 47, 84, 107, 32, 56, 46, 54, + 98, 50, 41, 31, 30, 29, 28, + ]; let disco = DiscoInfoResult::try_from(elem).unwrap(); let ecaps2 = compute_disco(&disco); assert_eq!(ecaps2.len(), 0x543); assert_eq!(ecaps2, expected); let sha_256 = hash_ecaps2(&ecaps2, Algo::Sha_256).unwrap(); - assert_eq!(sha_256.hash, base64::decode("u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY=").unwrap()); + assert_eq!( + sha_256.hash, + base64::decode("u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY=").unwrap() + ); let sha3_256 = hash_ecaps2(&ecaps2, Algo::Sha3_256).unwrap(); - assert_eq!(sha3_256.hash, base64::decode("XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg=").unwrap()); + assert_eq!( + sha3_256.hash, + base64::decode("XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg=").unwrap() + ); } #[test] fn test_blake2b_512() { let hash = hash_ecaps2("abc".as_bytes(), Algo::Blake2b_512).unwrap(); - let known_hash: Vec = vec!( - 0xBA, 0x80, 0xA5, 0x3F, 0x98, 0x1C, 0x4D, 0x0D, 0x6A, 0x27, 0x97, 0xB6, 0x9F, 0x12, 0xF6, 0xE9, - 0x4C, 0x21, 0x2F, 0x14, 0x68, 0x5A, 0xC4, 0xB7, 0x4B, 0x12, 0xBB, 0x6F, 0xDB, 0xFF, 0xA2, 0xD1, - 0x7D, 0x87, 0xC5, 0x39, 0x2A, 0xAB, 0x79, 0x2D, 0xC2, 0x52, 0xD5, 0xDE, 0x45, 0x33, 0xCC, 0x95, - 0x18, 0xD3, 0x8A, 0xA8, 0xDB, 0xF1, 0x92, 0x5A, 0xB9, 0x23, 0x86, 0xED, 0xD4, 0x00, 0x99, 0x23, - ); + let known_hash: Vec = vec![ + 0xBA, 0x80, 0xA5, 0x3F, 0x98, 0x1C, 0x4D, 0x0D, 0x6A, 0x27, 0x97, 0xB6, 0x9F, 0x12, + 0xF6, 0xE9, 0x4C, 0x21, 0x2F, 0x14, 0x68, 0x5A, 0xC4, 0xB7, 0x4B, 0x12, 0xBB, 0x6F, + 0xDB, 0xFF, 0xA2, 0xD1, 0x7D, 0x87, 0xC5, 0x39, 0x2A, 0xAB, 0x79, 0x2D, 0xC2, 0x52, + 0xD5, 0xDE, 0x45, 0x33, 0xCC, 0x95, 0x18, 0xD3, 0x8A, 0xA8, 0xDB, 0xF1, 0x92, 0x5A, + 0xB9, 0x23, 0x86, 0xED, 0xD4, 0x00, 0x99, 0x23, + ]; assert_eq!(hash.hash, known_hash); } } diff --git a/src/eme.rs b/src/eme.rs index 5c13f5107ae0b955c49ef3d14fbf203cd3105ce3..4b8ffbe6bcb2a6ee0e990b9a8b73ad9094e05e2a 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -24,9 +24,9 @@ impl MessagePayload for ExplicitMessageEncryption {} #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; use crate::error::Error; + use minidom::Element; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -42,7 +42,9 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let encryption = ExplicitMessageEncryption::try_from(elem).unwrap(); assert_eq!(encryption.namespace, "urn:xmpp:otr:0"); assert_eq!(encryption.name, None); @@ -55,7 +57,9 @@ mod tests { #[test] fn test_unknown() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = ExplicitMessageEncryption::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -66,7 +70,9 @@ mod tests { #[test] fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = ExplicitMessageEncryption::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -77,8 +83,13 @@ mod tests { #[test] fn test_serialise() { - let elem: Element = "".parse().unwrap(); - let eme = ExplicitMessageEncryption { namespace: String::from("coucou"), name: None }; + let elem: Element = "" + .parse() + .unwrap(); + let eme = ExplicitMessageEncryption { + namespace: String::from("coucou"), + name: None, + }; let elem2 = eme.into(); assert_eq!(elem, elem2); } diff --git a/src/error.rs b/src/error.rs index 36bd03471a344f68c719295992974f573c8aa1de..f2378a19b4d99314abc43445a86335a960d10085 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,15 +4,14 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use base64; +use chrono; +use jid; use std::convert::From; -use std::num; -use std::string; use std::fmt; use std::net; - -use base64; -use jid; -use chrono; +use std::num; +use std::string; /// Contains one of the potential errors triggered while parsing an /// [Element](../struct.Element.html) into a specialised struct. diff --git a/src/forwarding.rs b/src/forwarding.rs index 9f725132d40e832d9b8aff96024e951f950ba556..e36ebf8eed09979f79e984e5a2d0ffe937e01f6b 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -26,9 +26,9 @@ generate_element!( #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; use crate::error::Error; + use minidom::Element; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -50,7 +50,9 @@ mod tests { #[test] fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Forwarded::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -62,7 +64,10 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let forwarded = Forwarded { delay: None, stanza: None }; + let forwarded = Forwarded { + delay: None, + stanza: None, + }; let elem2 = forwarded.into(); assert_eq!(elem, elem2); } diff --git a/src/hashes.rs b/src/hashes.rs index 93a17291f127b3c78b2b725556dfff1ab261bbc0..a7773ee1cda05bc2b509270586cb361cbd59cd38 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -4,14 +4,11 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::str::FromStr; - -use minidom::IntoAttributeValue; - use crate::error::Error; - use crate::helpers::Base64; use base64; +use minidom::IntoAttributeValue; +use std::str::FromStr; /// List of the algorithms we support, or Unknown. #[allow(non_camel_case_types)] @@ -114,10 +111,7 @@ generate_element!( impl Hash { /// Creates a [Hash] element with the given algo and data. pub fn new(algo: Algo, hash: Vec) -> Hash { - Hash { - algo, - hash, - } + Hash { algo, hash } } /// Like [new](#method.new) but takes base64-encoded data before decoding @@ -130,8 +124,8 @@ impl Hash { #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; use minidom::Element; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -152,12 +146,17 @@ mod tests { let elem: Element = "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=".parse().unwrap(); let hash = Hash::try_from(elem).unwrap(); assert_eq!(hash.algo, Algo::Sha_256); - assert_eq!(hash.hash, base64::decode("2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=").unwrap()); + assert_eq!( + hash.hash, + base64::decode("2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=").unwrap() + ); } #[test] fn test_unknown() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Hash::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -168,7 +167,9 @@ mod tests { #[test] fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Hash::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, diff --git a/src/helpers.rs b/src/helpers.rs index 9d4f3ecb405771063854168e88414c4977aabf7f..e4d2324475a6eb3d13bb27eab7db787c4638d49e 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -4,8 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use base64; use crate::error::Error; +use base64; /// Codec for plain text content. pub struct PlainText; @@ -19,9 +19,7 @@ impl PlainText { } pub fn encode(string: &Option) -> Option { - string.as_ref().map(|text| { - text.to_owned() - }) + string.as_ref().map(|text| text.to_owned()) } } diff --git a/src/ibb.rs b/src/ibb.rs index 19af588174dce9c0d1c488a519cd625cb48dc072..f778ddf1aa3aebdb55c3e778ec4946dcc3419947 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -8,8 +8,9 @@ use crate::helpers::Base64; use crate::iq::IqSetPayload; generate_id!( -/// An identifier matching a stream. -StreamId); + /// An identifier matching a stream. + StreamId +); generate_attribute!( /// Which stanza type to use to exchange data. @@ -71,10 +72,10 @@ impl IqSetPayload for Close {} #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; use crate::error::Error; + use minidom::Element; use std::error::Error as StdError; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -98,26 +99,36 @@ mod tests { fn test_simple() { let sid = StreamId(String::from("coucou")); - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let open = Open::try_from(elem).unwrap(); assert_eq!(open.block_size, 3); assert_eq!(open.sid, sid); assert_eq!(open.stanza, Stanza::Iq); - let elem: Element = "AAAA".parse().unwrap(); + let elem: Element = + "AAAA" + .parse() + .unwrap(); let data = Data::try_from(elem).unwrap(); assert_eq!(data.seq, 0); assert_eq!(data.sid, sid); assert_eq!(data.data, vec!(0, 0, 0)); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let close = Close::try_from(elem).unwrap(); assert_eq!(close.sid, sid); } #[test] fn test_invalid() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Open::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -125,7 +136,9 @@ mod tests { }; assert_eq!(message, "Required attribute 'block-size' missing."); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Open::try_from(elem).unwrap_err(); let message = match error { Error::ParseIntError(error) => error, @@ -133,7 +146,9 @@ mod tests { }; assert_eq!(message.description(), "invalid digit found in string"); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Open::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(error) => error, diff --git a/src/ibr.rs b/src/ibr.rs index 4afe719e38ee4fa2c1eaa4700a29ecbd8f3920fc..39aa229d68dbfabcc353a2fdd7f9c64d1b587758 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -4,17 +4,13 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::collections::HashMap; -use try_from::TryFrom; - -use minidom::Element; - -use crate::error::Error; - -use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload}; use crate::data_forms::DataForm; - +use crate::error::Error; +use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; +use minidom::Element; +use std::collections::HashMap; +use try_from::TryFrom; /// Query for registering against a service. #[derive(Debug, Clone)] @@ -31,7 +27,6 @@ pub struct Query { /// A data form the user must fill before being allowed to register. pub form: Option, - // Not yet implemented. //pub oob: Option, } @@ -55,9 +50,26 @@ impl TryFrom for Query { let namespace = child.ns().unwrap(); if namespace == ns::REGISTER { let name = child.name(); - let fields = vec!["address", "city", "date", "email", "first", "instructions", - "key", "last", "misc", "name", "nick", "password", "phone", - "state", "text", "url", "username", "zip"]; + let fields = vec![ + "address", + "city", + "date", + "email", + "first", + "instructions", + "key", + "last", + "misc", + "name", + "nick", + "password", + "phone", + "state", + "text", + "url", + "username", + "zip", + ]; if fields.binary_search(&name).is_ok() { query.fields.insert(name.to_owned(), child.text()); } else if name == "registered" { @@ -80,14 +92,26 @@ impl TryFrom for Query { impl From for Element { fn from(query: Query) -> Element { Element::builder("query") - .ns(ns::REGISTER) - .append(if query.registered { Some(Element::builder("registered").ns(ns::REGISTER)) } else { None }) - .append(query.fields.into_iter().map(|(name, value)| { - Element::builder(name).ns(ns::REGISTER).append(value) - }).collect::>()) - .append(if query.remove { Some(Element::builder("remove").ns(ns::REGISTER)) } else { None }) - .append(query.form) - .build() + .ns(ns::REGISTER) + .append(if query.registered { + Some(Element::builder("registered").ns(ns::REGISTER)) + } else { + None + }) + .append( + query + .fields + .into_iter() + .map(|(name, value)| Element::builder(name).ns(ns::REGISTER).append(value)) + .collect::>(), + ) + .append(if query.remove { + Some(Element::builder("remove").ns(ns::REGISTER)) + } else { + None + }) + .append(query.form) + .build() } } @@ -126,7 +150,9 @@ mod tests { -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let query = Query::try_from(elem).unwrap(); assert_eq!(query.registered, false); assert_eq!(query.fields["instructions"], "\n Choose a username and password for use with this service.\n Please also provide your email address.\n "); @@ -172,16 +198,27 @@ mod tests { -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let elem1 = elem.clone(); let query = Query::try_from(elem).unwrap(); assert_eq!(query.registered, false); assert!(!query.fields["instructions"].is_empty()); let form = query.form.clone().unwrap(); assert!(!form.instructions.unwrap().is_empty()); - assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("first"))).is_ok()); - assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("x-gender"))).is_ok()); - assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("coucou"))).is_err()); + assert!(form + .fields + .binary_search_by(|field| field.var.cmp(&String::from("first"))) + .is_ok()); + assert!(form + .fields + .binary_search_by(|field| field.var.cmp(&String::from("x-gender"))) + .is_ok()); + assert!(form + .fields + .binary_search_by(|field| field.var.cmp(&String::from("coucou"))) + .is_err()); let elem2 = query.into(); assert!(elem1.compare_to(&elem2)); } @@ -208,7 +245,9 @@ mod tests { -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let elem1 = elem.clone(); let query = Query::try_from(elem).unwrap(); assert_eq!(query.registered, false); diff --git a/src/idle.rs b/src/idle.rs index 53ededdad8797cb9a29ba0c65c5700c19bafb82e..df2d005d67b6a6361b3e02f51d961ee534f528a5 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -4,8 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::presence::PresencePayload; use crate::date::DateTime; +use crate::presence::PresencePayload; generate_element!( /// Represents the last time the user interacted with their system. @@ -21,11 +21,11 @@ impl PresencePayload for Idle {} #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; use crate::error::Error; - use std::str::FromStr; + use minidom::Element; use std::error::Error as StdError; + use std::str::FromStr; + use try_from::TryFrom; #[test] fn test_size() { @@ -34,13 +34,17 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); Idle::try_from(elem).unwrap(); } #[test] fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Idle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -63,7 +67,9 @@ mod tests { #[test] fn test_invalid_date() { // There is no thirteenth month. - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Idle::try_from(elem).unwrap_err(); let message = match error { Error::ChronoParseError(string) => string, @@ -72,7 +78,9 @@ mod tests { assert_eq!(message.description(), "input is out of range"); // Timezone ≥24:00 aren’t allowed. - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Idle::try_from(elem).unwrap_err(); let message = match error { Error::ChronoParseError(string) => string, @@ -81,7 +89,9 @@ mod tests { assert_eq!(message.description(), "input is out of range"); // Timezone without the : separator aren’t allowed. - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Idle::try_from(elem).unwrap_err(); let message = match error { Error::ChronoParseError(string) => string, @@ -90,7 +100,9 @@ mod tests { assert_eq!(message.description(), "input contains invalid characters"); // No seconds, error message could be improved. - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Idle::try_from(elem).unwrap_err(); let message = match error { Error::ChronoParseError(string) => string, @@ -99,7 +111,9 @@ mod tests { assert_eq!(message.description(), "input contains invalid characters"); // TODO: maybe we’ll want to support this one, as per XEP-0082 §4. - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Idle::try_from(elem).unwrap_err(); let message = match error { Error::ChronoParseError(string) => string, @@ -108,7 +122,9 @@ mod tests { assert_eq!(message.description(), "input contains invalid characters"); // No timezone. - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Idle::try_from(elem).unwrap_err(); let message = match error { Error::ChronoParseError(string) => string, @@ -119,8 +135,12 @@ mod tests { #[test] fn test_serialise() { - let elem: Element = "".parse().unwrap(); - let idle = Idle { since: DateTime::from_str("2017-05-21T20:19:55+01:00").unwrap() }; + let elem: Element = "" + .parse() + .unwrap(); + let idle = Idle { + since: DateTime::from_str("2017-05-21T20:19:55+01:00").unwrap(), + }; let elem2 = idle.into(); assert_eq!(elem, elem2); } diff --git a/src/iq.rs b/src/iq.rs index 1657fcb650d3c89e83f6644f37622c954b819350..d4ee8b4197fc5d696017fdabff473adc0e21798b 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -5,18 +5,13 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - -use minidom::Element; -use minidom::IntoAttributeValue; - -use jid::Jid; - use crate::error::Error; - use crate::ns; - use crate::stanza_error::StanzaError; +use jid::Jid; +use minidom::Element; +use minidom::IntoAttributeValue; +use try_from::TryFrom; /// Should be implemented on every known payload of an ``. pub trait IqGetPayload: TryFrom + Into {} @@ -45,12 +40,15 @@ pub enum IqType { impl<'a> IntoAttributeValue for &'a IqType { fn into_attribute_value(self) -> Option { - Some(match *self { - IqType::Get(_) => "get", - IqType::Set(_) => "set", - IqType::Result(_) => "result", - IqType::Error(_) => "error", - }.to_owned()) + Some( + match *self { + IqType::Get(_) => "get", + IqType::Set(_) => "set", + IqType::Result(_) => "result", + IqType::Error(_) => "error", + } + .to_owned(), + ) } } @@ -201,16 +199,14 @@ impl TryFrom for Iq { impl From for Element { fn from(iq: Iq) -> Element { let mut stanza = Element::builder("iq") - .ns(ns::DEFAULT_NS) - .attr("from", iq.from) - .attr("to", iq.to) - .attr("id", iq.id) - .attr("type", &iq.payload) - .build(); + .ns(ns::DEFAULT_NS) + .attr("from", iq.from) + .attr("to", iq.to) + .attr("id", iq.id) + .attr("type", &iq.payload) + .build(); let elem = match iq.payload { - IqType::Get(elem) - | IqType::Set(elem) - | IqType::Result(Some(elem)) => elem, + IqType::Get(elem) | IqType::Set(elem) | IqType::Result(Some(elem)) => elem, IqType::Error(error) => error.into(), IqType::Result(None) => return stanza, }; @@ -222,9 +218,9 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use crate::stanza_error::{ErrorType, DefinedCondition}; use crate::compare_elements::NamespaceAwareCompare; use crate::disco::DiscoInfoQuery; + use crate::stanza_error::{DefinedCondition, ErrorType}; #[cfg(target_pointer_width = "32")] #[test] @@ -259,11 +255,15 @@ mod tests { #[cfg(not(feature = "component"))] let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); #[cfg(feature = "component")] let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let iq = Iq::try_from(elem).unwrap(); let query: Element = "".parse().unwrap(); assert_eq!(iq.from, None); @@ -271,7 +271,7 @@ mod tests { assert_eq!(iq.id, None); assert!(match iq.payload { IqType::Get(element) => element.compare_to(&query), - _ => false + _ => false, }); } @@ -280,11 +280,15 @@ mod tests { #[cfg(not(feature = "component"))] let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); #[cfg(feature = "component")] let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let iq = Iq::try_from(elem).unwrap(); let vcard: Element = "".parse().unwrap(); assert_eq!(iq.from, None); @@ -292,7 +296,7 @@ mod tests { assert_eq!(iq.id, None); assert!(match iq.payload { IqType::Set(element) => element.compare_to(&vcard), - _ => false + _ => false, }); } @@ -301,7 +305,9 @@ mod tests { #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); #[cfg(feature = "component")] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let iq = Iq::try_from(elem).unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); @@ -317,13 +323,19 @@ mod tests { #[cfg(not(feature = "component"))] let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); #[cfg(feature = "component")] let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let iq = Iq::try_from(elem).unwrap(); - let query: Element = "".parse().unwrap(); + let query: Element = "" + .parse() + .unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); assert_eq!(iq.id, None); @@ -341,14 +353,18 @@ mod tests { - ".parse().unwrap(); + " + .parse() + .unwrap(); #[cfg(feature = "component")] let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let iq = Iq::try_from(elem).unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); @@ -357,10 +373,13 @@ mod tests { IqType::Error(error) => { assert_eq!(error.type_, ErrorType::Cancel); assert_eq!(error.by, None); - assert_eq!(error.defined_condition, DefinedCondition::ServiceUnavailable); + assert_eq!( + error.defined_condition, + DefinedCondition::ServiceUnavailable + ); assert_eq!(error.texts.len(), 0); assert_eq!(error.other, None); - }, + } _ => panic!(), } } @@ -368,9 +387,13 @@ mod tests { #[test] fn test_children_invalid() { #[cfg(not(feature = "component"))] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); #[cfg(feature = "component")] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Iq::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -384,7 +407,9 @@ mod tests { #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); #[cfg(feature = "component")] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let iq2 = Iq { from: None, to: None, diff --git a/src/jingle.rs b/src/jingle.rs index 720eae864dda1c5f708ce17c6e3288e4aa27df55..2a3c09669e2eb1ec0a73901024ee99a2afd12a81 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -4,15 +4,13 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; -use std::str::FromStr; - -use minidom::Element; -use jid::Jid; - use crate::error::Error; -use crate::ns; use crate::iq::IqSetPayload; +use crate::ns; +use jid::Jid; +use minidom::Element; +use std::str::FromStr; +use try_from::TryFrom; generate_attribute!( /// The action attribute. @@ -351,7 +349,8 @@ impl From for Element { Reason::Timeout => "timeout", Reason::UnsupportedApplications => "unsupported-applications", Reason::UnsupportedTransports => "unsupported-transports", - }).build() + }) + .build() } } @@ -379,19 +378,25 @@ impl TryFrom for ReasonElement { match child.name() { "text" => { if text.is_some() { - return Err(Error::ParseError("Reason must not have more than one text.")); + return Err(Error::ParseError( + "Reason must not have more than one text.", + )); } text = Some(child.text()); - }, + } name => { if reason.is_some() { - return Err(Error::ParseError("Reason must not have more than one reason.")); + return Err(Error::ParseError( + "Reason must not have more than one reason.", + )); } reason = Some(name.parse()?); - }, + } } } - let reason = reason.ok_or(Error::ParseError("Reason doesn’t contain a valid reason."))?; + let reason = reason.ok_or(Error::ParseError( + "Reason doesn’t contain a valid reason.", + ))?; Ok(ReasonElement { reason: reason, text: text, @@ -402,9 +407,9 @@ impl TryFrom for ReasonElement { impl From for Element { fn from(reason: ReasonElement) -> Element { Element::builder("reason") - .append(Element::from(reason.reason)) - .append(reason.text) - .build() + .append(Element::from(reason.reason)) + .append(reason.text) + .build() } } @@ -491,9 +496,9 @@ impl TryFrom for Jingle { initiator: get_attr!(root, "initiator", optional), responder: get_attr!(root, "responder", optional), sid: get_attr!(root, "sid", required), - contents: vec!(), + contents: vec![], reason: None, - other: vec!(), + other: vec![], }; for child in root.children().cloned() { @@ -502,7 +507,9 @@ impl TryFrom for Jingle { jingle.contents.push(content); } else if child.is("reason", ns::JINGLE) { if jingle.reason.is_some() { - return Err(Error::ParseError("Jingle must not have more than one reason.")); + return Err(Error::ParseError( + "Jingle must not have more than one reason.", + )); } let reason = ReasonElement::try_from(child)?; jingle.reason = Some(reason); @@ -518,14 +525,14 @@ impl TryFrom for Jingle { impl From for Element { fn from(jingle: Jingle) -> Element { Element::builder("jingle") - .ns(ns::JINGLE) - .attr("action", jingle.action) - .attr("initiator", jingle.initiator) - .attr("responder", jingle.responder) - .attr("sid", jingle.sid) - .append(jingle.contents) - .append(jingle.reason) - .build() + .ns(ns::JINGLE) + .attr("action", jingle.action) + .attr("initiator", jingle.initiator) + .attr("responder", jingle.responder) + .attr("sid", jingle.sid) + .append(jingle.contents) + .append(jingle.reason) + .build() } } @@ -565,7 +572,10 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let jingle = Jingle::try_from(elem).unwrap(); assert_eq!(jingle.action, Action::SessionInitiate); assert_eq!(jingle.sid, SessionId(String::from("coucou"))); @@ -581,7 +591,9 @@ mod tests { }; assert_eq!(message, "Required attribute 'action' missing."); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -589,7 +601,9 @@ mod tests { }; assert_eq!(message, "Required attribute 'sid' missing."); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 15bb4f3285ce6981e66b3215de0235311b6ffcb5..381eb21fa288b1c37ac00ef139d4f033750b454c 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -4,19 +4,15 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; -use std::str::FromStr; - -use std::collections::BTreeMap; - -use crate::hashes::Hash; -use crate::jingle::{Creator, ContentId}; use crate::date::DateTime; - -use minidom::Element; - use crate::error::Error; +use crate::hashes::Hash; +use crate::jingle::{ContentId, Creator}; use crate::ns; +use minidom::Element; +use std::collections::BTreeMap; +use std::str::FromStr; +use try_from::TryFrom; generate_element!( /// Represents a range in a file. @@ -153,7 +149,7 @@ impl TryFrom for File { descs: BTreeMap::new(), size: None, range: None, - hashes: vec!(), + hashes: vec![], }; for child in elem.children() { @@ -164,7 +160,9 @@ impl TryFrom for File { file.date = Some(child.text().parse()?); } else if child.is("media-type", ns::JINGLE_FT) { if file.media_type.is_some() { - return Err(Error::ParseError("File must not have more than one media-type.")); + return Err(Error::ParseError( + "File must not have more than one media-type.", + )); } file.media_type = Some(child.text()); } else if child.is("name", ns::JINGLE_FT) { @@ -176,7 +174,9 @@ impl TryFrom for File { let lang = get_attr!(child, "xml:lang", default); let desc = Desc(child.text()); if file.descs.insert(lang, desc).is_some() { - return Err(Error::ParseError("Desc element present twice for the same xml:lang.")); + return Err(Error::ParseError( + "Desc element present twice for the same xml:lang.", + )); } } else if child.is("size", ns::JINGLE_FT) { if file.size.is_some() { @@ -201,38 +201,47 @@ impl TryFrom for File { impl From for Element { fn from(file: File) -> Element { - let mut root = Element::builder("file") - .ns(ns::JINGLE_FT); + let mut root = Element::builder("file").ns(ns::JINGLE_FT); if let Some(date) = file.date { - root = root.append(Element::builder("date") - .ns(ns::JINGLE_FT) - .append(date) - .build()); + root = root.append( + Element::builder("date") + .ns(ns::JINGLE_FT) + .append(date) + .build(), + ); } if let Some(media_type) = file.media_type { - root = root.append(Element::builder("media-type") - .ns(ns::JINGLE_FT) - .append(media_type) - .build()); + root = root.append( + Element::builder("media-type") + .ns(ns::JINGLE_FT) + .append(media_type) + .build(), + ); } if let Some(name) = file.name { - root = root.append(Element::builder("name") - .ns(ns::JINGLE_FT) - .append(name) - .build()); + root = root.append( + Element::builder("name") + .ns(ns::JINGLE_FT) + .append(name) + .build(), + ); } for (lang, desc) in file.descs.into_iter() { - root = root.append(Element::builder("desc") - .ns(ns::JINGLE_FT) - .attr("xml:lang", lang) - .append(desc.0) - .build()); + root = root.append( + Element::builder("desc") + .ns(ns::JINGLE_FT) + .attr("xml:lang", lang) + .append(desc.0) + .build(), + ); } if let Some(size) = file.size { - root = root.append(Element::builder("size") - .ns(ns::JINGLE_FT) - .append(format!("{}", size)) - .build()); + root = root.append( + Element::builder("size") + .ns(ns::JINGLE_FT) + .append(format!("{}", size)) + .build(), + ); } if let Some(range) = file.range { root = root.append(range); @@ -260,12 +269,16 @@ impl TryFrom for Description { let mut file = None; for child in elem.children() { if file.is_some() { - return Err(Error::ParseError("JingleFT description element must have exactly one child.")); + return Err(Error::ParseError( + "JingleFT description element must have exactly one child.", + )); } file = Some(File::try_from(child.clone())?); } if file.is_none() { - return Err(Error::ParseError("JingleFT description element must have exactly one child.")); + return Err(Error::ParseError( + "JingleFT description element must have exactly one child.", + )); } Ok(Description { file: file.unwrap(), @@ -276,9 +289,9 @@ impl TryFrom for Description { impl From for Element { fn from(description: Description) -> Element { Element::builder("description") - .ns(ns::JINGLE_FT) - .append(description.file) - .build() + .ns(ns::JINGLE_FT) + .append(description.file) + .build() } } @@ -304,12 +317,16 @@ impl TryFrom for Checksum { let mut file = None; for child in elem.children() { if file.is_some() { - return Err(Error::ParseError("JingleFT checksum element must have exactly one child.")); + return Err(Error::ParseError( + "JingleFT checksum element must have exactly one child.", + )); } file = Some(File::try_from(child.clone())?); } if file.is_none() { - return Err(Error::ParseError("JingleFT checksum element must have exactly one child.")); + return Err(Error::ParseError( + "JingleFT checksum element must have exactly one child.", + )); } Ok(Checksum { name: get_attr!(elem, "name", required), @@ -322,11 +339,11 @@ impl TryFrom for Checksum { impl From for Element { fn from(checksum: Checksum) -> Element { Element::builder("checksum") - .ns(ns::JINGLE_FT) - .attr("name", checksum.name) - .attr("creator", checksum.creator) - .append(checksum.file) - .build() + .ns(ns::JINGLE_FT) + .attr("name", checksum.name) + .attr("creator", checksum.creator) + .append(checksum.file) + .build() } } @@ -381,16 +398,24 @@ mod tests { algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48= -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let desc = Description::try_from(elem).unwrap(); assert_eq!(desc.file.media_type, Some(String::from("text/plain"))); assert_eq!(desc.file.name, Some(String::from("test.txt"))); assert_eq!(desc.file.descs, BTreeMap::new()); - assert_eq!(desc.file.date, DateTime::from_str("2015-07-26T21:46:00+01:00").ok()); + assert_eq!( + desc.file.date, + DateTime::from_str("2015-07-26T21:46:00+01:00").ok() + ); assert_eq!(desc.file.size, Some(6144u64)); assert_eq!(desc.file.range, None); assert_eq!(desc.file.hashes[0].algo, Algo::Sha_1); - assert_eq!(desc.file.hashes[0].hash, base64::decode("w0mcJylzCn+AfvuGdqkty2+KP48=").unwrap()); + assert_eq!( + desc.file.hashes[0].hash, + base64::decode("w0mcJylzCn+AfvuGdqkty2+KP48=").unwrap() + ); } #[test] @@ -402,7 +427,9 @@ mod tests { algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48= -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let desc = Description::try_from(elem).unwrap(); assert_eq!(desc.file.media_type, None); assert_eq!(desc.file.name, None); @@ -411,7 +438,10 @@ mod tests { assert_eq!(desc.file.size, None); assert_eq!(desc.file.range, None); assert_eq!(desc.file.hashes[0].algo, Algo::Sha_1); - assert_eq!(desc.file.hashes[0].hash, base64::decode("w0mcJylzCn+AfvuGdqkty2+KP48=").unwrap()); + assert_eq!( + desc.file.hashes[0].hash, + base64::decode("w0mcJylzCn+AfvuGdqkty2+KP48=").unwrap() + ); } #[test] @@ -426,11 +456,19 @@ mod tests { algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48= -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let desc = Description::try_from(elem).unwrap(); - assert_eq!(desc.file.descs.keys().cloned().collect::>(), ["en", "fr"]); + assert_eq!( + desc.file.descs.keys().cloned().collect::>(), + ["en", "fr"] + ); assert_eq!(desc.file.descs["en"], Desc(String::from("Secret file!"))); - assert_eq!(desc.file.descs["fr"], Desc(String::from("Fichier secret !"))); + assert_eq!( + desc.file.descs["fr"], + Desc(String::from("Fichier secret !")) + ); let elem: Element = r#" @@ -442,7 +480,9 @@ mod tests { algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48= -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let error = Description::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -478,7 +518,10 @@ mod tests { }; assert_eq!(message, "Unknown attribute in received element."); - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let error = Received::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -498,16 +541,31 @@ mod tests { #[test] fn test_checksum() { let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); - let hash = vec!(195, 73, 156, 39, 41, 115, 10, 127, 128, 126, 251, 134, 118, 169, 45, 203, 111, 138, 63, 143); + let hash = vec![ + 195, 73, 156, 39, 41, 115, 10, 127, 128, 126, 251, 134, 118, 169, 45, 203, 111, 138, + 63, 143, + ]; let checksum = Checksum::try_from(elem).unwrap(); assert_eq!(checksum.name, ContentId(String::from("coucou"))); assert_eq!(checksum.creator, Creator::Initiator); - assert_eq!(checksum.file.hashes, vec!(Hash { algo: Algo::Sha_1, hash: hash.clone() })); + assert_eq!( + checksum.file.hashes, + vec!(Hash { + algo: Algo::Sha_1, + hash: hash.clone() + }) + ); let elem2 = Element::from(checksum); let checksum2 = Checksum::try_from(elem2).unwrap(); assert_eq!(checksum2.name, ContentId(String::from("coucou"))); assert_eq!(checksum2.creator, Creator::Initiator); - assert_eq!(checksum2.file.hashes, vec!(Hash { algo: Algo::Sha_1, hash: hash.clone() })); + assert_eq!( + checksum2.file.hashes, + vec!(Hash { + algo: Algo::Sha_1, + hash: hash.clone() + }) + ); let elem: Element = "".parse().unwrap(); let error = Checksum::try_from(elem).unwrap_err(); @@ -544,14 +602,22 @@ mod tests { #[test] fn test_range() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let range = Range::try_from(elem).unwrap(); assert_eq!(range.offset, 0); assert_eq!(range.length, None); assert_eq!(range.hashes, vec!()); let elem: Element = "kHp5RSzW/h7Gm1etSf90Mr5PC/k=".parse().unwrap(); - let hashes = vec!(Hash { algo: Algo::Sha_1, hash: vec!(144, 122, 121, 69, 44, 214, 254, 30, 198, 155, 87, 173, 73, 255, 116, 50, 190, 79, 11, 249) }); + let hashes = vec![Hash { + algo: Algo::Sha_1, + hash: vec![ + 144, 122, 121, 69, 44, 214, 254, 30, 198, 155, 87, 173, 73, 255, 116, 50, 190, 79, + 11, 249, + ], + }]; let range = Range::try_from(elem).unwrap(); assert_eq!(range.offset, 2048); assert_eq!(range.length, Some(1024)); @@ -562,7 +628,9 @@ mod tests { assert_eq!(range2.length, Some(1024)); assert_eq!(range2.hashes, hashes); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Range::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 129f7ad40d0efc3078bfbcd27a5dc90fd7eb5c41..1addc1ee4f9c5d41a87494cab0f3af5e9df17ced 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -24,10 +24,10 @@ attributes: [ #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; use crate::error::Error; + use minidom::Element; use std::error::Error as StdError; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -43,7 +43,10 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let transport = Transport::try_from(elem).unwrap(); assert_eq!(transport.block_size, 3); assert_eq!(transport.sid, StreamId(String::from("coucou"))); @@ -52,7 +55,9 @@ mod tests { #[test] fn test_invalid() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Transport::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -60,15 +65,23 @@ mod tests { }; assert_eq!(message, "Required attribute 'block-size' missing."); - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let error = Transport::try_from(elem).unwrap_err(); let message = match error { Error::ParseIntError(error) => error, _ => panic!(), }; - assert_eq!(message.description(), "number too large to fit in target type"); + assert_eq!( + message.description(), + "number too large to fit in target type" + ); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Transport::try_from(elem).unwrap_err(); let message = match error { Error::ParseIntError(error) => error, @@ -76,7 +89,10 @@ mod tests { }; assert_eq!(message.description(), "invalid digit found in string"); - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let error = Transport::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, diff --git a/src/jingle_message.rs b/src/jingle_message.rs index 2f90d2a5f9da75427b3cb393a087658a9f26a547..02c1e9924fb3e3449fce3c132ca26e7ee509aa7d 100644 --- a/src/jingle_message.rs +++ b/src/jingle_message.rs @@ -4,15 +4,11 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - -use minidom::Element; - use crate::error::Error; - use crate::jingle::SessionId; - use crate::ns; +use minidom::Element; +use try_from::TryFrom; /// Defines a protocol for broadcasting Jingle requests to all of the clients /// of a user. @@ -72,9 +68,11 @@ impl TryFrom for JingleMI { } JingleMI::Propose { sid: get_sid(elem)?, - description: description.ok_or(Error::ParseError("Propose element doesn’t contain a description."))?, + description: description.ok_or(Error::ParseError( + "Propose element doesn’t contain a description.", + ))?, } - }, + } "retract" => JingleMI::Retract(check_empty_and_get_sid(elem)?), "accept" => JingleMI::Accept(check_empty_and_get_sid(elem)?), "proceed" => JingleMI::Proceed(check_empty_and_get_sid(elem)?), @@ -87,33 +85,24 @@ impl TryFrom for JingleMI { impl From for Element { fn from(jingle_mi: JingleMI) -> Element { match jingle_mi { - JingleMI::Propose { sid, description } => { - Element::builder("propose") - .ns(ns::JINGLE_MESSAGE) - .attr("id", sid) - .append(description) - }, - JingleMI::Retract(sid) => { - Element::builder("retract") - .ns(ns::JINGLE_MESSAGE) - .attr("id", sid) - } - JingleMI::Accept(sid) => { - Element::builder("accept") - .ns(ns::JINGLE_MESSAGE) - .attr("id", sid) - } - JingleMI::Proceed(sid) => { - Element::builder("proceed") - .ns(ns::JINGLE_MESSAGE) - .attr("id", sid) - } - JingleMI::Reject(sid) => { - Element::builder("reject") - .ns(ns::JINGLE_MESSAGE) - .attr("id", sid) - } - }.build() + JingleMI::Propose { sid, description } => Element::builder("propose") + .ns(ns::JINGLE_MESSAGE) + .attr("id", sid) + .append(description), + JingleMI::Retract(sid) => Element::builder("retract") + .ns(ns::JINGLE_MESSAGE) + .attr("id", sid), + JingleMI::Accept(sid) => Element::builder("accept") + .ns(ns::JINGLE_MESSAGE) + .attr("id", sid), + JingleMI::Proceed(sid) => Element::builder("proceed") + .ns(ns::JINGLE_MESSAGE) + .attr("id", sid), + JingleMI::Reject(sid) => Element::builder("reject") + .ns(ns::JINGLE_MESSAGE) + .attr("id", sid), + } + .build() } } @@ -135,13 +124,18 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); JingleMI::try_from(elem).unwrap(); } #[test] fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let error = JingleMI::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 4d660d281530e345f0fd0410cf54f61bf28f336d..e85390caa2b89b1b62a3cd2dcbbd226feac00db0 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -4,15 +4,12 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; -use std::net::IpAddr; - -use minidom::Element; -use jid::Jid; - use crate::error::Error; - use crate::ns; +use jid::Jid; +use minidom::Element; +use std::net::IpAddr; +use try_from::TryFrom; generate_attribute!( /// The type of the connection being proposed by this candidate. @@ -187,37 +184,50 @@ impl TryFrom for Transport { let mut payload = None; for child in elem.children() { payload = Some(if child.is("candidate", ns::JINGLE_S5B) { - let mut candidates = match payload { - Some(TransportPayload::Candidates(candidates)) => candidates, - Some(_) => return Err(Error::ParseError("Non-candidate child already present in JingleS5B transport element.")), - None => vec!(), - }; + let mut candidates = + match payload { + Some(TransportPayload::Candidates(candidates)) => candidates, + Some(_) => return Err(Error::ParseError( + "Non-candidate child already present in JingleS5B transport element.", + )), + None => vec![], + }; candidates.push(Candidate::try_from(child.clone())?); TransportPayload::Candidates(candidates) } else if child.is("activated", ns::JINGLE_S5B) { if payload.is_some() { - return Err(Error::ParseError("Non-activated child already present in JingleS5B transport element.")); + return Err(Error::ParseError( + "Non-activated child already present in JingleS5B transport element.", + )); } let cid = get_attr!(child, "cid", required); TransportPayload::Activated(cid) } else if child.is("candidate-error", ns::JINGLE_S5B) { if payload.is_some() { - return Err(Error::ParseError("Non-candidate-error child already present in JingleS5B transport element.")); + return Err(Error::ParseError( + "Non-candidate-error child already present in JingleS5B transport element.", + )); } TransportPayload::CandidateError } else if child.is("candidate-used", ns::JINGLE_S5B) { if payload.is_some() { - return Err(Error::ParseError("Non-candidate-used child already present in JingleS5B transport element.")); + return Err(Error::ParseError( + "Non-candidate-used child already present in JingleS5B transport element.", + )); } let cid = get_attr!(child, "cid", required); TransportPayload::CandidateUsed(cid) } else if child.is("proxy-error", ns::JINGLE_S5B) { if payload.is_some() { - return Err(Error::ParseError("Non-proxy-error child already present in JingleS5B transport element.")); + return Err(Error::ParseError( + "Non-proxy-error child already present in JingleS5B transport element.", + )); } TransportPayload::ProxyError } else { - return Err(Error::ParseError("Unknown child in JingleS5B transport element.")); + return Err(Error::ParseError( + "Unknown child in JingleS5B transport element.", + )); }); } let payload = payload.unwrap_or(TransportPayload::None); @@ -233,49 +243,40 @@ impl TryFrom for Transport { impl From for Element { fn from(transport: Transport) -> Element { Element::builder("transport") - .ns(ns::JINGLE_S5B) - .attr("sid", transport.sid) - .attr("dstaddr", transport.dstaddr) - .attr("mode", transport.mode) - .append(match transport.payload { - TransportPayload::Candidates(candidates) => { - candidates.into_iter() - .map(Element::from) - .collect::>() - }, - TransportPayload::Activated(cid) => { - vec!(Element::builder("activated") - .ns(ns::JINGLE_S5B) - .attr("cid", cid) - .build()) - }, - TransportPayload::CandidateError => { - vec!(Element::builder("candidate-error") - .ns(ns::JINGLE_S5B) - .build()) - }, - TransportPayload::CandidateUsed(cid) => { - vec!(Element::builder("candidate-used") - .ns(ns::JINGLE_S5B) - .attr("cid", cid) - .build()) - }, - TransportPayload::ProxyError => { - vec!(Element::builder("proxy-error") - .ns(ns::JINGLE_S5B) - .build()) - }, - TransportPayload::None => vec!(), - }) - .build() + .ns(ns::JINGLE_S5B) + .attr("sid", transport.sid) + .attr("dstaddr", transport.dstaddr) + .attr("mode", transport.mode) + .append(match transport.payload { + TransportPayload::Candidates(candidates) => candidates + .into_iter() + .map(Element::from) + .collect::>(), + TransportPayload::Activated(cid) => vec![Element::builder("activated") + .ns(ns::JINGLE_S5B) + .attr("cid", cid) + .build()], + TransportPayload::CandidateError => vec![Element::builder("candidate-error") + .ns(ns::JINGLE_S5B) + .build()], + TransportPayload::CandidateUsed(cid) => vec![Element::builder("candidate-used") + .ns(ns::JINGLE_S5B) + .attr("cid", cid) + .build()], + TransportPayload::ProxyError => { + vec![Element::builder("proxy-error").ns(ns::JINGLE_S5B).build()] + } + TransportPayload::None => vec![], + }) + .build() } } #[cfg(test)] mod tests { use super::*; - use std::str::FromStr; use crate::compare_elements::NamespaceAwareCompare; + use std::str::FromStr; #[cfg(target_pointer_width = "32")] #[test] @@ -303,7 +304,9 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let transport = Transport::try_from(elem).unwrap(); assert_eq!(transport.sid, StreamId(String::from("coucou"))); assert_eq!(transport.dstaddr, None); @@ -334,14 +337,14 @@ mod tests { sid: StreamId(String::from("coucou")), dstaddr: None, mode: Mode::Tcp, - payload: TransportPayload::Candidates(vec!(Candidate { + payload: TransportPayload::Candidates(vec![Candidate { cid: CandidateId(String::from("coucou")), host: IpAddr::from_str("127.0.0.1").unwrap(), jid: Jid::from_str("coucou@coucou").unwrap(), port: None, priority: 0u32, type_: Type::Direct, - })), + }]), }; let elem2: Element = transport.into(); assert!(elem.compare_to(&elem2)); diff --git a/src/lib.rs b/src/lib.rs index 54994e19ecadd6053b16bd3f7075362246809cfa..f35d5b956c691bd6375dc9f1e44155bc952f77b9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,15 +24,15 @@ #![deny(missing_docs)] -extern crate minidom; -extern crate jid; extern crate base64; +extern crate blake2; +extern crate chrono; extern crate digest; +extern crate jid; +extern crate minidom; extern crate sha1; extern crate sha2; extern crate sha3; -extern crate blake2; -extern crate chrono; extern crate try_from; pub use minidom::Element; @@ -52,20 +52,20 @@ mod macros; /// Namespace-aware comparison for tests mod compare_elements; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core +pub mod bind; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core +pub mod iq; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod message; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod presence; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core -pub mod iq; +pub mod sasl; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod stanza_error; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod stream; -/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core -pub mod sasl; -/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core -pub mod bind; /// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence pub mod roster; diff --git a/src/macros.rs b/src/macros.rs index da96b67a61b227820390b8df7dbfec6fef79b689..ac77ed557d59cc60dcfa734fc4792f95b99cd20e 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -5,34 +5,40 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. macro_rules! get_attr { - ($elem:ident, $attr:tt, $type:tt) => ( + ($elem:ident, $attr:tt, $type:tt) => { get_attr!($elem, $attr, $type, value, value.parse()?) - ); - ($elem:ident, $attr:tt, optional_empty, $value:ident, $func:expr) => ( + }; + ($elem:ident, $attr:tt, optional_empty, $value:ident, $func:expr) => { match $elem.attr($attr) { Some("") => None, Some($value) => Some($func), None => None, } - ); - ($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => ( + }; + ($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => { match $elem.attr($attr) { Some($value) => Some($func), None => None, } - ); - ($elem:ident, $attr:tt, required, $value:ident, $func:expr) => ( + }; + ($elem:ident, $attr:tt, required, $value:ident, $func:expr) => { match $elem.attr($attr) { Some($value) => $func, - None => return Err(crate::error::Error::ParseError(concat!("Required attribute '", $attr, "' missing."))), + None => { + return Err(crate::error::Error::ParseError(concat!( + "Required attribute '", + $attr, + "' missing." + ))) + } } - ); - ($elem:ident, $attr:tt, default, $value:ident, $func:expr) => ( + }; + ($elem:ident, $attr:tt, default, $value:ident, $func:expr) => { match $elem.attr($attr) { Some($value) => $func, None => ::std::default::Default::default(), } - ); + }; } macro_rules! generate_attribute { @@ -211,38 +217,54 @@ macro_rules! generate_attribute_enum { } macro_rules! check_self { - ($elem:ident, $name:tt, $ns:ident) => ( + ($elem:ident, $name:tt, $ns:ident) => { check_self!($elem, $name, $ns, $name); - ); - ($elem:ident, $name:tt, $ns:ident, $pretty_name:tt) => ( + }; + ($elem:ident, $name:tt, $ns:ident, $pretty_name:tt) => { if !$elem.is($name, crate::ns::$ns) { - return Err(crate::error::Error::ParseError(concat!("This is not a ", $pretty_name, " element."))); + return Err(crate::error::Error::ParseError(concat!( + "This is not a ", + $pretty_name, + " element." + ))); } - ); + }; } macro_rules! check_ns_only { - ($elem:ident, $name:tt, $ns:ident) => ( + ($elem:ident, $name:tt, $ns:ident) => { if !$elem.has_ns(crate::ns::$ns) { - return Err(crate::error::Error::ParseError(concat!("This is not a ", $name, " element."))); + return Err(crate::error::Error::ParseError(concat!( + "This is not a ", + $name, + " element." + ))); } - ); + }; } macro_rules! check_no_children { - ($elem:ident, $name:tt) => ( + ($elem:ident, $name:tt) => { for _ in $elem.children() { - return Err(crate::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); + return Err(crate::error::Error::ParseError(concat!( + "Unknown child in ", + $name, + " element." + ))); } - ); + }; } macro_rules! check_no_attributes { - ($elem:ident, $name:tt) => ( + ($elem:ident, $name:tt) => { for _ in $elem.attrs() { - return Err(crate::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); + return Err(crate::error::Error::ParseError(concat!( + "Unknown attribute in ", + $name, + " element." + ))); } - ); + }; } macro_rules! check_no_unknown_attributes { @@ -351,76 +373,95 @@ macro_rules! start_decl { } macro_rules! start_parse_elem { - ($temp:ident: Vec) => ( + ($temp:ident: Vec) => { let mut $temp = Vec::new(); - ); - ($temp:ident: Option) => ( + }; + ($temp:ident: Option) => { let mut $temp = None; - ); - ($temp:ident: Required) => ( + }; + ($temp:ident: Required) => { let mut $temp = None; - ); + }; } macro_rules! do_parse { - ($elem:ident, Element) => ( + ($elem:ident, Element) => { $elem.clone() - ); - ($elem:ident, String) => ( + }; + ($elem:ident, String) => { $elem.text() - ); - ($elem:ident, $constructor:ident) => ( + }; + ($elem:ident, $constructor:ident) => { $constructor::try_from($elem.clone())? - ); + }; } macro_rules! do_parse_elem { - ($temp:ident: Vec = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => ( + ($temp:ident: Vec = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => { $temp.push(do_parse!($elem, $constructor)); - ); - ($temp:ident: Option = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => ( + }; + ($temp:ident: Option = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => { if $temp.is_some() { - return Err(crate::error::Error::ParseError(concat!("Element ", $parent_name, " must not have more than one ", $name, " child."))); + return Err(crate::error::Error::ParseError(concat!( + "Element ", + $parent_name, + " must not have more than one ", + $name, + " child." + ))); } $temp = Some(do_parse!($elem, $constructor)); - ); - ($temp:ident: Required = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => ( + }; + ($temp:ident: Required = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => { if $temp.is_some() { - return Err(crate::error::Error::ParseError(concat!("Element ", $parent_name, " must not have more than one ", $name, " child."))); + return Err(crate::error::Error::ParseError(concat!( + "Element ", + $parent_name, + " must not have more than one ", + $name, + " child." + ))); } $temp = Some(do_parse!($elem, $constructor)); - ); + }; } macro_rules! finish_parse_elem { - ($temp:ident: Vec = $name:tt, $parent_name:tt) => ( + ($temp:ident: Vec = $name:tt, $parent_name:tt) => { $temp - ); - ($temp:ident: Option = $name:tt, $parent_name:tt) => ( + }; + ($temp:ident: Option = $name:tt, $parent_name:tt) => { $temp - ); - ($temp:ident: Required = $name:tt, $parent_name:tt) => ( - $temp.ok_or(crate::error::Error::ParseError(concat!("Missing child ", $name, " in ", $parent_name, " element.")))? - ); + }; + ($temp:ident: Required = $name:tt, $parent_name:tt) => { + $temp.ok_or(crate::error::Error::ParseError(concat!( + "Missing child ", + $name, + " in ", + $parent_name, + " element." + )))? + }; } macro_rules! generate_serialiser { - ($parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => ( + ($parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => { ::minidom::Element::builder($name) .ns(crate::ns::$ns) .append($parent.$elem) .build() - ); - ($parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => ( - $parent.$elem.map(|elem| + }; + ($parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => { + $parent.$elem.map(|elem| { ::minidom::Element::builder($name) .ns(crate::ns::$ns) .append(elem) - .build()) - ); - ($parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => ( + .build() + }) + }; + ($parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => { $parent.$elem - ); + }; } macro_rules! generate_element { diff --git a/src/mam.rs b/src/mam.rs index ec88ead6fb654e6c0022cbe59975d38959ed1c80..8124be9feb760ef3132e9bf78b5d4bc81f45571f 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -4,21 +4,17 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - -use minidom::Element; -use jid::Jid; - -use crate::error::Error; - -use crate::message::MessagePayload; -use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload}; use crate::data_forms::DataForm; -use crate::rsm::{SetQuery, SetResult}; +use crate::error::Error; use crate::forwarding::Forwarded; -use crate::pubsub::NodeName; - +use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; +use crate::message::MessagePayload; use crate::ns; +use crate::pubsub::NodeName; +use crate::rsm::{SetQuery, SetResult}; +use jid::Jid; +use minidom::Element; +use try_from::TryFrom; generate_id!( /// An identifier matching a result message to the query requesting it. @@ -70,7 +66,9 @@ impl MessagePayload for Result_ {} generate_attribute!( /// True when the end of a MAM query has been reached. - Complete, "complete", bool + Complete, + "complete", + bool ); generate_element!( @@ -133,8 +131,8 @@ impl TryFrom for Prefs { fn try_from(elem: Element) -> Result { check_self!(elem, "prefs", MAM); check_no_unknown_attributes!(elem, "prefs", ["default"]); - let mut always = vec!(); - let mut never = vec!(); + let mut always = vec![]; + let mut never = vec![]; for child in elem.children() { if child.is("always", ns::MAM) { for jid_elem in child.children() { @@ -155,7 +153,11 @@ impl TryFrom for Prefs { } } let default_ = get_attr!(elem, "default", required); - Ok(Prefs { default_, always, never }) + Ok(Prefs { + default_, + always, + never, + }) } } @@ -163,26 +165,27 @@ fn serialise_jid_list(name: &str, jids: Vec) -> Option { if jids.is_empty() { None } else { - Some(Element::builder(name) - .ns(ns::MAM) - .append(jids.into_iter() - .map(|jid| Element::builder("jid") - .ns(ns::MAM) - .append(jid) - .build()) - .collect::>()) - .build()) + Some( + Element::builder(name) + .ns(ns::MAM) + .append( + jids.into_iter() + .map(|jid| Element::builder("jid").ns(ns::MAM).append(jid).build()) + .collect::>(), + ) + .build(), + ) } } impl From for Element { fn from(prefs: Prefs) -> Element { Element::builder("prefs") - .ns(ns::MAM) - .attr("default", prefs.default_) - .append(serialise_jid_list("always", prefs.always)) - .append(serialise_jid_list("never", prefs.never)) - .build() + .ns(ns::MAM) + .attr("default", prefs.default_) + .append(serialise_jid_list("always", prefs.always)) + .append(serialise_jid_list("never", prefs.never)) + .build() } } @@ -233,7 +236,9 @@ mod tests { -"#.parse().unwrap(); +"# + .parse() + .unwrap(); #[cfg(feature = "component")] let elem: Element = r#" @@ -257,7 +262,9 @@ mod tests { 09af3-cc343-b409f -"#.parse().unwrap(); +"# + .parse() + .unwrap(); Fin::try_from(elem).unwrap(); } @@ -274,7 +281,9 @@ mod tests { -"#.parse().unwrap(); +"# + .parse() + .unwrap(); Query::try_from(elem).unwrap(); } @@ -294,13 +303,17 @@ mod tests { 10 -"#.parse().unwrap(); +"# + .parse() + .unwrap(); Query::try_from(elem).unwrap(); } #[test] fn test_prefs_get() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let prefs = Prefs::try_from(elem).unwrap(); assert_eq!(prefs.always, vec!()); assert_eq!(prefs.never, vec!()); @@ -310,7 +323,9 @@ mod tests { -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let prefs = Prefs::try_from(elem).unwrap(); assert_eq!(prefs.always, vec!()); assert_eq!(prefs.never, vec!()); @@ -327,10 +342,18 @@ mod tests { montague@montague.lit -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let prefs = Prefs::try_from(elem).unwrap(); - assert_eq!(prefs.always, vec!(Jid::from_str("romeo@montague.lit").unwrap())); - assert_eq!(prefs.never, vec!(Jid::from_str("montague@montague.lit").unwrap())); + assert_eq!( + prefs.always, + vec!(Jid::from_str("romeo@montague.lit").unwrap()) + ); + assert_eq!( + prefs.never, + vec!(Jid::from_str("montague@montague.lit").unwrap()) + ); let elem2 = Element::from(prefs.clone()); println!("{:?}", elem2); @@ -342,7 +365,9 @@ mod tests { #[test] fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Query::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -354,7 +379,12 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let replace = Query { queryid: None, node: None, form: None, set: None }; + let replace = Query { + queryid: None, + node: None, + form: None, + set: None, + }; let elem2 = replace.into(); assert_eq!(elem, elem2); } diff --git a/src/media_element.rs b/src/media_element.rs index 2bc512d8753ee61814a59d65f32197768b6f3188..f3a623870c06c53da518519365e23857d804e378 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -45,11 +45,11 @@ generate_element!( #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; - use crate::error::Error; use crate::data_forms::DataForm; + use crate::error::Error; + use minidom::Element; use std::error::Error as StdError; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -76,7 +76,9 @@ mod tests { #[test] fn test_width_height() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let media = MediaElement::try_from(elem).unwrap(); assert_eq!(media.width.unwrap(), 32); assert_eq!(media.height.unwrap(), 32); @@ -93,15 +95,22 @@ mod tests { #[test] fn test_invalid_width_height() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = MediaElement::try_from(elem).unwrap_err(); let error = match error { Error::ParseIntError(error) => error, _ => panic!(), }; - assert_eq!(error.description(), "cannot parse integer from empty string"); + assert_eq!( + error.description(), + "cannot parse integer from empty string" + ); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = MediaElement::try_from(elem).unwrap_err(); let error = match error { Error::ParseIntError(error) => error, @@ -109,15 +118,22 @@ mod tests { }; assert_eq!(error.description(), "invalid digit found in string"); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = MediaElement::try_from(elem).unwrap_err(); let error = match error { Error::ParseIntError(error) => error, _ => panic!(), }; - assert_eq!(error.description(), "cannot parse integer from empty string"); + assert_eq!( + error.description(), + "cannot parse integer from empty string" + ); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = MediaElement::try_from(elem).unwrap_err(); let error = match error { Error::ParseIntError(error) => error, @@ -128,7 +144,9 @@ mod tests { #[test] fn test_unknown_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = MediaElement::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -139,7 +157,10 @@ mod tests { #[test] fn test_bad_uri() { - let elem: Element = "https://example.org/".parse().unwrap(); + let elem: Element = + "https://example.org/" + .parse() + .unwrap(); let error = MediaElement::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -147,7 +168,9 @@ mod tests { }; assert_eq!(message, "Required attribute 'type' missing."); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = MediaElement::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -169,17 +192,28 @@ mod tests { http://victim.example.com/challenges/speech.mp3?F3A6292C -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let media = MediaElement::try_from(elem).unwrap(); assert!(media.width.is_none()); assert!(media.height.is_none()); assert_eq!(media.uris.len(), 3); assert_eq!(media.uris[0].type_, "audio/x-wav"); - assert_eq!(media.uris[0].uri, "http://victim.example.com/challenges/speech.wav?F3A6292C"); + assert_eq!( + media.uris[0].uri, + "http://victim.example.com/challenges/speech.wav?F3A6292C" + ); assert_eq!(media.uris[1].type_, "audio/ogg; codecs=speex"); - assert_eq!(media.uris[1].uri, "cid:sha1+a15a505e360702b79c75a5f67773072ed392f52a@bob.xmpp.org"); + assert_eq!( + media.uris[1].uri, + "cid:sha1+a15a505e360702b79c75a5f67773072ed392f52a@bob.xmpp.org" + ); assert_eq!(media.uris[2].type_, "audio/mpeg"); - assert_eq!(media.uris[2].uri, "http://victim.example.com/challenges/speech.mp3?F3A6292C"); + assert_eq!( + media.uris[2].uri, + "http://victim.example.com/challenges/speech.mp3?F3A6292C" + ); } #[test] @@ -200,15 +234,23 @@ mod tests { [ ... ] -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let form = DataForm::try_from(elem).unwrap(); assert_eq!(form.fields.len(), 1); assert_eq!(form.fields[0].var, "ocr"); assert_eq!(form.fields[0].media[0].width, Some(290)); assert_eq!(form.fields[0].media[0].height, Some(80)); assert_eq!(form.fields[0].media[0].uris[0].type_, "image/jpeg"); - assert_eq!(form.fields[0].media[0].uris[0].uri, "http://www.victim.com/challenges/ocr.jpeg?F3A6292C"); + assert_eq!( + form.fields[0].media[0].uris[0].uri, + "http://www.victim.com/challenges/ocr.jpeg?F3A6292C" + ); assert_eq!(form.fields[0].media[0].uris[1].type_, "image/jpeg"); - assert_eq!(form.fields[0].media[0].uris[1].uri, "cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org"); + assert_eq!( + form.fields[0].media[0].uris[1].uri, + "cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org" + ); } } diff --git a/src/message.rs b/src/message.rs index caae40ab7c81107ea153bd8243b062df9fc1c3ae..159bcd2d8fe76492cb2a92236e24226ddf837239 100644 --- a/src/message.rs +++ b/src/message.rs @@ -4,16 +4,12 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; -use std::collections::BTreeMap; - -use minidom::Element; - -use jid::Jid; - use crate::error::Error; - use crate::ns; +use jid::Jid; +use minidom::Element; +use std::collections::BTreeMap; +use try_from::TryFrom; /// Should be implemented on every known payload of a ``. pub trait MessagePayload: TryFrom + Into {} @@ -44,18 +40,24 @@ type Lang = String; generate_elem_id!( /// Represents one `` element, that is the free form text content of /// a message. - Body, "body", DEFAULT_NS + Body, + "body", + DEFAULT_NS ); generate_elem_id!( /// Defines the subject of a room, or of an email-like normal message. - Subject, "subject", DEFAULT_NS + Subject, + "subject", + DEFAULT_NS ); generate_elem_id!( /// A thread identifier, so that other people can specify to which message /// they are replying. - Thread, "thread", DEFAULT_NS + Thread, + "thread", + DEFAULT_NS ); /// The main structure representing the `` stanza. @@ -102,11 +104,14 @@ impl Message { bodies: BTreeMap::new(), subjects: BTreeMap::new(), thread: None, - payloads: vec!(), + payloads: vec![], } } - fn get_best<'a, T>(map: &'a BTreeMap, preferred_langs: Vec<&str>) -> Option<(Lang, &'a T)> { + fn get_best<'a, T>( + map: &'a BTreeMap, + preferred_langs: Vec<&str>, + ) -> Option<(Lang, &'a T)> { if map.is_empty() { return None; } @@ -156,21 +161,25 @@ impl TryFrom for Message { let mut bodies = BTreeMap::new(); let mut subjects = BTreeMap::new(); let mut thread = None; - let mut payloads = vec!(); + let mut payloads = vec![]; for elem in root.children() { if elem.is("body", ns::DEFAULT_NS) { check_no_children!(elem, "body"); let lang = get_attr!(elem, "xml:lang", default); let body = Body(elem.text()); if bodies.insert(lang, body).is_some() { - return Err(Error::ParseError("Body element present twice for the same xml:lang.")); + return Err(Error::ParseError( + "Body element present twice for the same xml:lang.", + )); } } else if elem.is("subject", ns::DEFAULT_NS) { check_no_children!(elem, "subject"); let lang = get_attr!(elem, "xml:lang", default); let subject = Subject(elem.text()); if subjects.insert(lang, subject).is_some() { - return Err(Error::ParseError("Subject element present twice for the same xml:lang.")); + return Err(Error::ParseError( + "Subject element present twice for the same xml:lang.", + )); } } else if elem.is("thread", ns::DEFAULT_NS) { if thread.is_some() { @@ -198,41 +207,55 @@ impl TryFrom for Message { impl From for Element { fn from(message: Message) -> Element { Element::builder("message") - .ns(ns::DEFAULT_NS) - .attr("from", message.from) - .attr("to", message.to) - .attr("id", message.id) - .attr("type", message.type_) - .append(message.subjects.into_iter() - .map(|(lang, subject)| { - let mut subject = Element::from(subject); - subject.set_attr("xml:lang", match lang.as_ref() { - "" => None, - lang => Some(lang), - }); - subject - }) - .collect::>()) - .append(message.bodies.into_iter() - .map(|(lang, body)| { - let mut body = Element::from(body); - body.set_attr("xml:lang", match lang.as_ref() { - "" => None, - lang => Some(lang), - }); - body - }) - .collect::>()) - .append(message.payloads) - .build() + .ns(ns::DEFAULT_NS) + .attr("from", message.from) + .attr("to", message.to) + .attr("id", message.id) + .attr("type", message.type_) + .append( + message + .subjects + .into_iter() + .map(|(lang, subject)| { + let mut subject = Element::from(subject); + subject.set_attr( + "xml:lang", + match lang.as_ref() { + "" => None, + lang => Some(lang), + }, + ); + subject + }) + .collect::>(), + ) + .append( + message + .bodies + .into_iter() + .map(|(lang, body)| { + let mut body = Element::from(body); + body.set_attr( + "xml:lang", + match lang.as_ref() { + "" => None, + lang => Some(lang), + }, + ); + body + }) + .collect::>(), + ) + .append(message.payloads) + .build() } } #[cfg(test)] mod tests { use super::*; - use std::str::FromStr; use crate::compare_elements::NamespaceAwareCompare; + use std::str::FromStr; #[cfg(target_pointer_width = "32")] #[test] @@ -259,7 +282,9 @@ mod tests { #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); #[cfg(feature = "component")] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let message = Message::try_from(elem).unwrap(); assert_eq!(message.from, None); assert_eq!(message.to, None); @@ -273,7 +298,9 @@ mod tests { #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); #[cfg(feature = "component")] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let mut message = Message::new(None); message.type_ = MessageType::Normal; let elem2 = message.into(); @@ -291,7 +318,7 @@ mod tests { assert_eq!(message.bodies[""], Body::from_str("Hello world!").unwrap()); { - let (lang, body) = message.get_best_body(vec!("en")).unwrap(); + let (lang, body) = message.get_best_body(vec!["en"]).unwrap(); assert_eq!(lang, ""); assert_eq!(body, &Body::from_str("Hello world!").unwrap()); } @@ -307,7 +334,9 @@ mod tests { #[cfg(feature = "component")] let elem: Element = "Hello world!".parse().unwrap(); let mut message = Message::new(Some(Jid::from_str("coucou@example.org").unwrap())); - message.bodies.insert(String::from(""), Body::from_str("Hello world!").unwrap()); + message + .bodies + .insert(String::from(""), Body::from_str("Hello world!").unwrap()); let elem2 = message.into(); assert!(elem.compare_to(&elem2)); } @@ -320,10 +349,13 @@ mod tests { let elem: Element = "Hello world!".parse().unwrap(); let elem1 = elem.clone(); let message = Message::try_from(elem).unwrap(); - assert_eq!(message.subjects[""], Subject::from_str("Hello world!").unwrap()); + assert_eq!( + message.subjects[""], + Subject::from_str("Hello world!").unwrap() + ); { - let (lang, subject) = message.get_best_subject(vec!("en")).unwrap(); + let (lang, subject) = message.get_best_subject(vec!["en"]).unwrap(); assert_eq!(lang, ""); assert_eq!(subject, &Subject::from_str("Hello world!").unwrap()); } @@ -342,28 +374,28 @@ mod tests { // Tests basic feature. { - let (lang, body) = message.get_best_body(vec!("fr")).unwrap(); + let (lang, body) = message.get_best_body(vec!["fr"]).unwrap(); assert_eq!(lang, "fr"); assert_eq!(body, &Body::from_str("Salut le monde !").unwrap()); } // Tests order. { - let (lang, body) = message.get_best_body(vec!("en", "de")).unwrap(); + let (lang, body) = message.get_best_body(vec!["en", "de"]).unwrap(); assert_eq!(lang, "de"); assert_eq!(body, &Body::from_str("Hallo Welt!").unwrap()); } // Tests fallback. { - let (lang, body) = message.get_best_body(vec!()).unwrap(); + let (lang, body) = message.get_best_body(vec![]).unwrap(); assert_eq!(lang, ""); assert_eq!(body, &Body::from_str("Hello world!").unwrap()); } // Tests fallback. { - let (lang, body) = message.get_best_body(vec!("ja")).unwrap(); + let (lang, body) = message.get_best_body(vec!["ja"]).unwrap(); assert_eq!(lang, ""); assert_eq!(body, &Body::from_str("Hello world!").unwrap()); } diff --git a/src/message_correct.rs b/src/message_correct.rs index 24b56919132099984fa576f9bd4939c08ff066c7..9e7a08f726c1e789b2794949a3888ec4d8524eb8 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -21,9 +21,9 @@ impl MessagePayload for Replace {} #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; use crate::error::Error; + use minidom::Element; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -39,13 +39,17 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); Replace::try_from(elem).unwrap(); } #[test] fn test_invalid_attribute() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Replace::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -56,7 +60,9 @@ mod tests { #[test] fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Replace::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -67,7 +73,9 @@ mod tests { #[test] fn test_invalid_id() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Replace::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -78,8 +86,12 @@ mod tests { #[test] fn test_serialise() { - let elem: Element = "".parse().unwrap(); - let replace = Replace { id: String::from("coucou") }; + let elem: Element = "" + .parse() + .unwrap(); + let replace = Replace { + id: String::from("coucou"), + }; let elem2 = replace.into(); assert_eq!(elem, elem2); } diff --git a/src/mood.rs b/src/mood.rs index 59204322be515e7f3ea7ff4bbba16f280e45e5d3..da07bf50a296d6a5190c56b6bc24cab306eb11ab 100644 --- a/src/mood.rs +++ b/src/mood.rs @@ -263,14 +263,16 @@ generate_element_enum!( generate_elem_id!( /// Free-form text description of the mood. - Text, "text", MOOD + Text, + "text", + MOOD ); #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; use minidom::Element; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -288,14 +290,18 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let mood = MoodEnum::try_from(elem).unwrap(); assert_eq!(mood, MoodEnum::Happy); } #[test] fn test_text() { - let elem: Element = "Yay!".parse().unwrap(); + let elem: Element = "Yay!" + .parse() + .unwrap(); let text = Text::try_from(elem).unwrap(); assert_eq!(text.0, String::from("Yay!")); } diff --git a/src/muc/muc.rs b/src/muc/muc.rs index a49c1bb85656d8fc11f222e8b515cd18d4a8ede2..32df468d7e46f8548656f6a9503daef514dc610f 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -5,8 +5,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::presence::PresencePayload; use crate::date::DateTime; +use crate::presence::PresencePayload; generate_element!( /// Represents the query for messages before our join. @@ -102,21 +102,25 @@ impl Muc { #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; + use crate::compare_elements::NamespaceAwareCompare; use crate::error::Error; + use minidom::Element; use std::str::FromStr; - use crate::compare_elements::NamespaceAwareCompare; + use try_from::TryFrom; #[test] fn test_muc_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); Muc::try_from(elem).unwrap(); } #[test] fn test_muc_invalid_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Muc::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -127,7 +131,9 @@ mod tests { #[test] fn test_muc_serialise() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let muc = Muc { password: None, history: None, @@ -138,7 +144,9 @@ mod tests { #[test] fn test_muc_invalid_attribute() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Muc::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -153,7 +161,8 @@ mod tests { coucou " - .parse().unwrap(); + .parse() + .unwrap(); let elem1 = elem.clone(); let muc = Muc::try_from(elem).unwrap(); assert_eq!(muc.password, Some("coucou".to_owned())); @@ -168,7 +177,8 @@ mod tests { " - .parse().unwrap(); + .parse() + .unwrap(); let muc = Muc::try_from(elem).unwrap(); let muc2 = Muc::new().with_history(History::new().with_maxstanzas(0)); assert_eq!(muc, muc2); @@ -183,8 +193,12 @@ mod tests { " - .parse().unwrap(); + .parse() + .unwrap(); let muc = Muc::try_from(elem).unwrap(); - assert_eq!(muc.history.unwrap().since.unwrap(), DateTime::from_str("1970-01-01T00:00:00+00:00").unwrap()); + assert_eq!( + muc.history.unwrap().since.unwrap(), + DateTime::from_str("1970-01-01T00:00:00+00:00").unwrap() + ); } } diff --git a/src/muc/user.rs b/src/muc/user.rs index 7d114abd63c82b2d33ccf82c4ed9e8a40c3fab6c..3f058a1740803f879dcec7a873de4cbaccd9ff0c 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -5,15 +5,11 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - -use minidom::Element; - -use jid::Jid; - use crate::error::Error; - use crate::ns; +use jid::Jid; +use minidom::Element; +use try_from::TryFrom; generate_attribute_enum!( /// Lists all of the possible status codes used in MUC presences. @@ -103,9 +99,11 @@ impl TryFrom for Actor { let nick = get_attr!(elem, "nick", optional); match (jid, nick) { - (Some(_), Some(_)) - | (None, None) => - return Err(Error::ParseError("Either 'jid' or 'nick' attribute is required.")), + (Some(_), Some(_)) | (None, None) => { + return Err(Error::ParseError( + "Either 'jid' or 'nick' attribute is required.", + )) + } (Some(jid), _) => Ok(Actor::Jid(jid)), (_, Some(nick)) => Ok(Actor::Nick(nick)), } @@ -119,7 +117,8 @@ impl From for Element { (match actor { Actor::Jid(jid) => elem.attr("jid", jid), Actor::Nick(nick) => elem.attr("nick", nick), - }).build() + }) + .build() } } @@ -135,7 +134,9 @@ generate_element!( generate_elem_id!( /// A reason for inviting, declining, etc. a request. - Reason, "reason", MUC_USER + Reason, + "reason", + MUC_USER ); generate_attribute!( @@ -237,14 +238,16 @@ generate_element!( #[cfg(test)] mod tests { use super::*; - use std::error::Error as StdError; use crate::compare_elements::NamespaceAwareCompare; + use std::error::Error as StdError; #[test] fn test_simple() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); MucUser::try_from(elem).unwrap(); } @@ -256,7 +259,9 @@ mod tests { - ".parse().unwrap(); + " + .parse() + .unwrap(); let muc_user = MucUser::try_from(elem).unwrap(); assert_eq!(muc_user.status.len(), 2); assert_eq!(muc_user.status[0], Status::AffiliationChange); @@ -272,7 +277,9 @@ mod tests { - ".parse().unwrap(); + " + .parse() + .unwrap(); let error = MucUser::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -285,8 +292,13 @@ mod tests { fn test_serialise() { let elem: Element = " - ".parse().unwrap(); - let muc = MucUser { status: vec!(), items: vec!() }; + " + .parse() + .unwrap(); + let muc = MucUser { + status: vec![], + items: vec![], + }; let elem2 = muc.into(); assert!(elem.compare_to(&elem2)); } @@ -295,7 +307,9 @@ mod tests { fn test_invalid_attribute() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let error = MucUser::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -308,7 +322,9 @@ mod tests { fn test_status_simple() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); Status::try_from(elem).unwrap(); } @@ -316,7 +332,9 @@ mod tests { fn test_status_invalid() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let error = Status::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -331,7 +349,9 @@ mod tests { - ".parse().unwrap(); + " + .parse() + .unwrap(); let error = Status::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -344,7 +364,9 @@ mod tests { fn test_status_simple_code() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let status = Status::try_from(elem).unwrap(); assert_eq!(status, Status::Kicked); } @@ -353,7 +375,9 @@ mod tests { fn test_status_invalid_code() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let error = Status::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -366,7 +390,9 @@ mod tests { fn test_status_invalid_code2() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let error = Status::try_from(elem).unwrap_err(); let error = match error { Error::ParseIntError(error) => error, @@ -379,7 +405,9 @@ mod tests { fn test_actor_required_attributes() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let error = Actor::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -394,7 +422,9 @@ mod tests { - ".parse().unwrap(); + " + .parse() + .unwrap(); let error = Actor::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -408,7 +438,9 @@ mod tests { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let actor = Actor::try_from(elem).unwrap(); let jid = match actor { Actor::Jid(jid) => jid, @@ -421,7 +453,9 @@ mod tests { fn test_actor_nick() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let actor = Actor::try_from(elem).unwrap(); let nick = match actor { Actor::Nick(nick) => nick, @@ -434,7 +468,9 @@ mod tests { fn test_continue_simple() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); Continue::try_from(elem).unwrap(); } @@ -443,7 +479,9 @@ mod tests { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let continue_ = Continue::try_from(elem).unwrap(); assert_eq!(continue_.thread, Some("foo".to_owned())); } @@ -454,7 +492,9 @@ mod tests { - ".parse().unwrap(); + " + .parse() + .unwrap(); let continue_ = Continue::try_from(elem).unwrap_err(); let message = match continue_ { Error::ParseError(string) => string, @@ -467,7 +507,8 @@ mod tests { fn test_reason_simple() { let elem: Element = " Reason" - .parse().unwrap(); + .parse() + .unwrap(); let reason = Reason::try_from(elem).unwrap(); assert_eq!(reason.0, "Reason".to_owned()); } @@ -476,7 +517,9 @@ mod tests { fn test_reason_invalid_attribute() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let error = Reason::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -491,7 +534,9 @@ mod tests { - ".parse().unwrap(); + " + .parse() + .unwrap(); let error = Reason::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -501,11 +546,13 @@ mod tests { } #[test] - fn test_item_invalid_attr(){ + fn test_item_invalid_attr() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let error = Item::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -515,21 +562,25 @@ mod tests { } #[test] - fn test_item_affiliation_role_attr(){ + fn test_item_affiliation_role_attr() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); Item::try_from(elem).unwrap(); } #[test] - fn test_item_affiliation_role_invalid_attr(){ + fn test_item_affiliation_role_invalid_attr() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let error = Item::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -539,13 +590,15 @@ mod tests { } #[test] - fn test_item_nick_attr(){ + fn test_item_nick_attr() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let item = Item::try_from(elem).unwrap(); match item { Item { nick, .. } => assert_eq!(nick, Some("foobar".to_owned())), @@ -553,65 +606,79 @@ mod tests { } #[test] - fn test_item_affiliation_role_invalid_attr2(){ + fn test_item_affiliation_role_invalid_attr2() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let error = Item::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Required attribute 'affiliation' missing.".to_owned()); + assert_eq!( + message, + "Required attribute 'affiliation' missing.".to_owned() + ); } #[test] - fn test_item_role_actor_child(){ + fn test_item_role_actor_child() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let item = Item::try_from(elem).unwrap(); match item { - Item { actor, .. } => - assert_eq!(actor, Some(Actor::Nick("foobar".to_owned()))), + Item { actor, .. } => assert_eq!(actor, Some(Actor::Nick("foobar".to_owned()))), } } #[test] - fn test_item_role_continue_child(){ + fn test_item_role_continue_child() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let item = Item::try_from(elem).unwrap(); - let continue_1 = Continue { thread: Some("foobar".to_owned()) }; + let continue_1 = Continue { + thread: Some("foobar".to_owned()), + }; match item { - Item { continue_: Some(continue_2), .. } => assert_eq!(continue_2.thread, continue_1.thread), + Item { + continue_: Some(continue_2), + .. + } => assert_eq!(continue_2.thread, continue_1.thread), _ => panic!(), } } #[test] - fn test_item_role_reason_child(){ + fn test_item_role_reason_child() { let elem: Element = " foobar - ".parse().unwrap(); + " + .parse() + .unwrap(); let item = Item::try_from(elem).unwrap(); match item { - Item { reason, .. } => - assert_eq!(reason, Some(Reason("foobar".to_owned()))), + Item { reason, .. } => assert_eq!(reason, Some(Reason("foobar".to_owned()))), } } } diff --git a/src/nick.rs b/src/nick.rs index 360348bd8761074ce8ff9fa1bad2b51dfaf88982..edf8dcacce17b23e54ac39fcbea32b87aad8d5a2 100644 --- a/src/nick.rs +++ b/src/nick.rs @@ -6,15 +6,17 @@ generate_elem_id!( /// Represents a global, memorable, friendly or informal name chosen by a user. - Nick, "nick", NICK + Nick, + "nick", + NICK ); #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; use crate::error::Error; + use minidom::Element; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -30,7 +32,9 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "Link Mauve".parse().unwrap(); + let elem: Element = "Link Mauve" + .parse() + .unwrap(); let nick = Nick::try_from(elem).unwrap(); assert_eq!(&nick.0, "Link Mauve"); } @@ -38,13 +42,17 @@ mod tests { #[test] fn test_serialise() { let elem1 = Element::from(Nick(String::from("Link Mauve"))); - let elem2: Element = "Link Mauve".parse().unwrap(); + let elem2: Element = "Link Mauve" + .parse() + .unwrap(); assert_eq!(elem1, elem2); } #[test] fn test_invalid() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Nick::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -55,7 +63,9 @@ mod tests { #[test] fn test_invalid_attribute() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Nick::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, diff --git a/src/ping.rs b/src/ping.rs index ddb636d3f3fa91d6b7c7883202acd83e14f8bc24..4cb25f649c453db84f9879f5871c743df187898f 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -10,7 +10,9 @@ use crate::iq::IqGetPayload; generate_empty_element!( /// Represents a ping to the recipient, which must be answered with an /// empty `` or with an error. - Ping, "ping", PING + Ping, + "ping", + PING ); impl IqGetPayload for Ping {} @@ -18,9 +20,9 @@ impl IqGetPayload for Ping {} #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; use crate::error::Error; + use minidom::Element; + use try_from::TryFrom; #[test] fn test_size() { @@ -42,7 +44,9 @@ mod tests { #[test] fn test_invalid() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Ping::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, diff --git a/src/presence.rs b/src/presence.rs index aece0cff7c25c83db0b216c5f5a2e779d20d422e..8f3d2cf109a806ab68600cb12caa159412255553 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -5,11 +5,11 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; -use std::str::FromStr; use std::collections::BTreeMap; +use std::str::FromStr; +use try_from::TryFrom; -use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; +use minidom::{Element, ElementEmitter, IntoAttributeValue, IntoElements}; use jid::Jid; @@ -68,14 +68,15 @@ impl IntoElements for Show { } emitter.append_child( Element::builder("show") - .append(match self { - Show::None => unreachable!(), - Show::Away => Some("away"), - Show::Chat => Some("chat"), - Show::Dnd => Some("dnd"), - Show::Xa => Some("xa"), - }) - .build()) + .append(match self { + Show::None => unreachable!(), + Show::Away => Some("away"), + Show::Chat => Some("chat"), + Show::Dnd => Some("dnd"), + Show::Xa => Some("xa"), + }) + .build(), + ) } } @@ -84,7 +85,7 @@ type Status = String; type Priority = i8; -/// +/// #[derive(Debug, Clone, PartialEq)] pub enum Type { /// This value is not an acceptable 'type' attribute, it is only used @@ -136,24 +137,31 @@ impl FromStr for Type { "unsubscribe" => Type::Unsubscribe, "unsubscribed" => Type::Unsubscribed, - _ => return Err(Error::ParseError("Invalid 'type' attribute on presence element.")), + _ => { + return Err(Error::ParseError( + "Invalid 'type' attribute on presence element.", + )) + } }) } } impl IntoAttributeValue for Type { fn into_attribute_value(self) -> Option { - Some(match self { - Type::None => return None, - - Type::Error => "error", - Type::Probe => "probe", - Type::Subscribe => "subscribe", - Type::Subscribed => "subscribed", - Type::Unavailable => "unavailable", - Type::Unsubscribe => "unsubscribe", - Type::Unsubscribed => "unsubscribed", - }.to_owned()) + Some( + match self { + Type::None => return None, + + Type::Error => "error", + Type::Probe => "probe", + Type::Subscribe => "subscribe", + Type::Subscribed => "subscribed", + Type::Unavailable => "unavailable", + Type::Unsubscribe => "unsubscribe", + Type::Unsubscribed => "unsubscribed", + } + .to_owned(), + ) } } @@ -197,7 +205,7 @@ impl Presence { show: Show::None, statuses: BTreeMap::new(), priority: 0i8, - payloads: vec!(), + payloads: vec![], } } @@ -266,12 +274,14 @@ impl TryFrom for Presence { show: Show::None, statuses: BTreeMap::new(), priority: 0i8, - payloads: vec!(), + payloads: vec![], }; for elem in root.children() { if elem.is("show", ns::DEFAULT_NS) { if show.is_some() { - return Err(Error::ParseError("More than one show element in a presence.")); + return Err(Error::ParseError( + "More than one show element in a presence.", + )); } check_no_attributes!(elem, "show"); check_no_children!(elem, "show"); @@ -281,11 +291,15 @@ impl TryFrom for Presence { check_no_children!(elem, "status"); let lang = get_attr!(elem, "xml:lang", default); if presence.statuses.insert(lang, elem.text()).is_some() { - return Err(Error::ParseError("Status element present twice for the same xml:lang.")); + return Err(Error::ParseError( + "Status element present twice for the same xml:lang.", + )); } } else if elem.is("priority", ns::DEFAULT_NS) { if priority.is_some() { - return Err(Error::ParseError("More than one priority element in a presence.")); + return Err(Error::ParseError( + "More than one priority element in a presence.", + )); } check_no_attributes!(elem, "priority"); check_no_children!(elem, "priority"); @@ -307,24 +321,37 @@ impl TryFrom for Presence { impl From for Element { fn from(presence: Presence) -> Element { Element::builder("presence") - .ns(ns::DEFAULT_NS) - .attr("from", presence.from) - .attr("to", presence.to) - .attr("id", presence.id) - .attr("type", presence.type_) - .append(presence.show) - .append(presence.statuses.into_iter().map(|(lang, status)| { - Element::builder("status") - .attr("xml:lang", match lang.as_ref() { - "" => None, - lang => Some(lang), - }) - .append(status) - .build() - }).collect::>()) - .append(if presence.priority == 0 { None } else { Some(format!("{}", presence.priority)) }) - .append(presence.payloads) - .build() + .ns(ns::DEFAULT_NS) + .attr("from", presence.from) + .attr("to", presence.to) + .attr("id", presence.id) + .attr("type", presence.type_) + .append(presence.show) + .append( + presence + .statuses + .into_iter() + .map(|(lang, status)| { + Element::builder("status") + .attr( + "xml:lang", + match lang.as_ref() { + "" => None, + lang => Some(lang), + }, + ) + .append(status) + .build() + }) + .collect::>(), + ) + .append(if presence.priority == 0 { + None + } else { + Some(format!("{}", presence.priority)) + }) + .append(presence.payloads) + .build() } } @@ -354,7 +381,9 @@ mod tests { #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); #[cfg(feature = "component")] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.from, None); assert_eq!(presence.to, None); @@ -366,9 +395,13 @@ mod tests { #[test] fn test_serialise() { #[cfg(not(feature = "component"))] - let elem: Element = "/>".parse().unwrap(); + let elem: Element = "/>" + .parse() + .unwrap(); #[cfg(feature = "component")] - let elem: Element = "/>".parse().unwrap(); + let elem: Element = "/>" + .parse() + .unwrap(); let presence = Presence::new(Type::Unavailable); let elem2 = presence.into(); assert!(elem.compare_to(&elem2)); @@ -377,9 +410,14 @@ mod tests { #[test] fn test_show() { #[cfg(not(feature = "component"))] - let elem: Element = "chat".parse().unwrap(); + let elem: Element = "chat" + .parse() + .unwrap(); #[cfg(feature = "component")] - let elem: Element = "chat".parse().unwrap(); + let elem: Element = + "chat" + .parse() + .unwrap(); let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.show, Show::Chat); @@ -388,9 +426,13 @@ mod tests { #[test] fn test_missing_show_value() { #[cfg(not(feature = "component"))] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); #[cfg(feature = "component")] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Presence::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -403,9 +445,14 @@ mod tests { fn test_invalid_show() { // "online" used to be a pretty common mistake. #[cfg(not(feature = "component"))] - let elem: Element = "online".parse().unwrap(); + let elem: Element = "online" + .parse() + .unwrap(); #[cfg(feature = "component")] - let elem: Element = "online".parse().unwrap(); + let elem: Element = + "online" + .parse() + .unwrap(); let error = Presence::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -417,9 +464,13 @@ mod tests { #[test] fn test_empty_status() { #[cfg(not(feature = "component"))] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); #[cfg(feature = "component")] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.statuses.len(), 1); @@ -429,9 +480,14 @@ mod tests { #[test] fn test_status() { #[cfg(not(feature = "component"))] - let elem: Element = "Here!".parse().unwrap(); + let elem: Element = "Here!" + .parse() + .unwrap(); #[cfg(feature = "component")] - let elem: Element = "Here!".parse().unwrap(); + let elem: Element = + "Here!" + .parse() + .unwrap(); let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.statuses.len(), 1); @@ -462,15 +518,23 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Status element present twice for the same xml:lang."); + assert_eq!( + message, + "Status element present twice for the same xml:lang." + ); } #[test] fn test_priority() { #[cfg(not(feature = "component"))] - let elem: Element = "-1".parse().unwrap(); + let elem: Element = "-1" + .parse() + .unwrap(); #[cfg(feature = "component")] - let elem: Element = "-1".parse().unwrap(); + let elem: Element = + "-1" + .parse() + .unwrap(); let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.priority, -1i8); @@ -479,9 +543,14 @@ mod tests { #[test] fn test_invalid_priority() { #[cfg(not(feature = "component"))] - let elem: Element = "128".parse().unwrap(); + let elem: Element = "128" + .parse() + .unwrap(); #[cfg(feature = "component")] - let elem: Element = "128".parse().unwrap(); + let elem: Element = + "128" + .parse() + .unwrap(); let error = Presence::try_from(elem).unwrap_err(); match error { Error::ParseIntError(_) => (), @@ -492,9 +561,14 @@ mod tests { #[test] fn test_unknown_child() { #[cfg(not(feature = "component"))] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); #[cfg(feature = "component")] - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let presence = Presence::try_from(elem).unwrap(); let payload = &presence.payloads[0]; assert!(payload.is("test", "invalid")); @@ -503,9 +577,14 @@ mod tests { #[test] fn test_invalid_status_child() { #[cfg(not(feature = "component"))] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); #[cfg(feature = "component")] - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let error = Presence::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -517,9 +596,14 @@ mod tests { #[test] fn test_invalid_attribute() { #[cfg(not(feature = "component"))] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); #[cfg(feature = "component")] - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let error = Presence::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index c1ccace3983e20bbf0da4fcc4a4898ef90c42c43..b8f2bc0066a17dc74c93e345b3a1f035f22d1ad5 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -4,19 +4,14 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - -use minidom::Element; -use jid::Jid; +use crate::data_forms::DataForm; use crate::date::DateTime; - use crate::error::Error; - use crate::ns; - -use crate::data_forms::DataForm; - -use crate::pubsub::{NodeName, ItemId, Subscription, SubscriptionId}; +use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId}; +use jid::Jid; +use minidom::Element; +use try_from::TryFrom; /// One PubSub item from a node. #[derive(Debug, Clone)] @@ -40,7 +35,9 @@ impl TryFrom for Item { let mut payloads = elem.children().cloned().collect::>(); let payload = payloads.pop(); if !payloads.is_empty() { - return Err(Error::ParseError("More than a single payload in item element.")); + return Err(Error::ParseError( + "More than a single payload in item element.", + )); } Ok(Item { payload, @@ -53,11 +50,11 @@ impl TryFrom for Item { impl From for Element { fn from(item: Item) -> Element { Element::builder("item") - .ns(ns::PUBSUB_EVENT) - .attr("id", item.id) - .attr("publisher", item.publisher) - .append(item.payload) - .build() + .ns(ns::PUBSUB_EVENT) + .attr("id", item.id) + .attr("publisher", item.publisher) + .append(item.payload) + .build() } } @@ -131,21 +128,29 @@ pub enum PubSubEvent { fn parse_items(elem: Element, node: NodeName) -> Result { let mut is_retract = None; - let mut items = vec!(); - let mut retracts = vec!(); + let mut items = vec![]; + let mut retracts = vec![]; for child in elem.children() { if child.is("item", ns::PUBSUB_EVENT) { match is_retract { None => is_retract = Some(false), Some(false) => (), - Some(true) => return Err(Error::ParseError("Mix of item and retract in items element.")), + Some(true) => { + return Err(Error::ParseError( + "Mix of item and retract in items element.", + )) + } } items.push(Item::try_from(child.clone())?); } else if child.is("retract", ns::PUBSUB_EVENT) { match is_retract { None => is_retract = Some(true), Some(true) => (), - Some(false) => return Err(Error::ParseError("Mix of item and retract in items element.")), + Some(false) => { + return Err(Error::ParseError( + "Mix of item and retract in items element.", + )) + } } check_no_children!(child, "retract"); check_no_unknown_attributes!(child, "retract", ["id"]); @@ -157,7 +162,10 @@ fn parse_items(elem: Element, node: NodeName) -> Result { } Ok(match is_retract { Some(false) => PubSubEvent::PublishedItems { node, items }, - Some(true) => PubSubEvent::RetractedItems { node, items: retracts }, + Some(true) => PubSubEvent::RetractedItems { + node, + items: retracts, + }, None => return Err(Error::ParseError("Missing children in items element.")), }) } @@ -176,7 +184,9 @@ impl TryFrom for PubSubEvent { let mut payloads = child.children().cloned().collect::>(); let item = payloads.pop(); if !payloads.is_empty() { - return Err(Error::ParseError("More than a single payload in configuration element.")); + return Err(Error::ParseError( + "More than a single payload in configuration element.", + )); } let form = match item { None => None, @@ -188,7 +198,9 @@ impl TryFrom for PubSubEvent { for item in child.children() { if item.is("redirect", ns::PUBSUB_EVENT) { if redirect.is_some() { - return Err(Error::ParseError("More than one redirect in delete element.")); + return Err(Error::ParseError( + "More than one redirect in delete element.", + )); } let uri = get_attr!(item, "uri", required); redirect = Some(uri); @@ -222,77 +234,79 @@ impl TryFrom for PubSubEvent { impl From for Element { fn from(event: PubSubEvent) -> Element { let payload = match event { - PubSubEvent::Configuration { node, form } => { - Element::builder("configuration") - .ns(ns::PUBSUB_EVENT) - .attr("node", node) - .append(form) - .build() - }, - PubSubEvent::Delete { node, redirect } => { - Element::builder("purge") - .ns(ns::PUBSUB_EVENT) - .attr("node", node) - .append(redirect.map(|redirect| { - Element::builder("redirect") - .ns(ns::PUBSUB_EVENT) - .attr("uri", redirect) - .build() - })) - .build() - }, - PubSubEvent::PublishedItems { node, items } => { - Element::builder("items") - .ns(ns::PUBSUB_EVENT) - .attr("node", node) - .append(items) - .build() - }, - PubSubEvent::RetractedItems { node, items } => { - Element::builder("items") - .ns(ns::PUBSUB_EVENT) - .attr("node", node) - .append(items.into_iter().map(|id| { - Element::builder("retract") - .ns(ns::PUBSUB_EVENT) - .attr("id", id) - .build() - }).collect::>()) - .build() - }, - PubSubEvent::Purge { node } => { - Element::builder("purge") - .ns(ns::PUBSUB_EVENT) - .attr("node", node) - .build() - }, - PubSubEvent::Subscription { node, expiry, jid, subid, subscription } => { - Element::builder("subscription") + PubSubEvent::Configuration { node, form } => Element::builder("configuration") + .ns(ns::PUBSUB_EVENT) + .attr("node", node) + .append(form) + .build(), + PubSubEvent::Delete { node, redirect } => Element::builder("purge") + .ns(ns::PUBSUB_EVENT) + .attr("node", node) + .append(redirect.map(|redirect| { + Element::builder("redirect") .ns(ns::PUBSUB_EVENT) - .attr("node", node) - .attr("expiry", expiry) - .attr("jid", jid) - .attr("subid", subid) - .attr("subscription", subscription) + .attr("uri", redirect) .build() - }, + })) + .build(), + PubSubEvent::PublishedItems { node, items } => Element::builder("items") + .ns(ns::PUBSUB_EVENT) + .attr("node", node) + .append(items) + .build(), + PubSubEvent::RetractedItems { node, items } => Element::builder("items") + .ns(ns::PUBSUB_EVENT) + .attr("node", node) + .append( + items + .into_iter() + .map(|id| { + Element::builder("retract") + .ns(ns::PUBSUB_EVENT) + .attr("id", id) + .build() + }) + .collect::>(), + ) + .build(), + PubSubEvent::Purge { node } => Element::builder("purge") + .ns(ns::PUBSUB_EVENT) + .attr("node", node) + .build(), + PubSubEvent::Subscription { + node, + expiry, + jid, + subid, + subscription, + } => Element::builder("subscription") + .ns(ns::PUBSUB_EVENT) + .attr("node", node) + .attr("expiry", expiry) + .attr("jid", jid) + .attr("subid", subid) + .attr("subscription", subscription) + .build(), }; Element::builder("event") - .ns(ns::PUBSUB_EVENT) - .append(payload) - .build() + .ns(ns::PUBSUB_EVENT) + .append(payload) + .build() } } #[cfg(test)] mod tests { use super::*; - use std::str::FromStr; use crate::compare_elements::NamespaceAwareCompare; + use std::str::FromStr; #[test] fn missing_items() { - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let error = PubSubEvent::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -309,9 +323,12 @@ mod tests { PubSubEvent::PublishedItems { node, items } => { assert_eq!(node, NodeName(String::from("coucou"))); assert_eq!(items[0].id, Some(ItemId(String::from("test")))); - assert_eq!(items[0].publisher, Some(Jid::from_str("test@coucou").unwrap())); + assert_eq!( + items[0].publisher, + Some(Jid::from_str("test@coucou").unwrap()) + ); assert_eq!(items[0].payload, None); - }, + } _ => panic!(), } } @@ -329,7 +346,7 @@ mod tests { Some(ref elem) => assert!(elem.is("foreign", "example:namespace")), _ => panic!(), } - }, + } _ => panic!(), } } @@ -343,7 +360,7 @@ mod tests { assert_eq!(node, NodeName(String::from("something"))); assert_eq!(items[0], ItemId(String::from("coucou"))); assert_eq!(items[1], ItemId(String::from("test"))); - }, + } _ => panic!(), } } @@ -356,19 +373,22 @@ mod tests { PubSubEvent::Delete { node, redirect } => { assert_eq!(node, NodeName(String::from("coucou"))); assert_eq!(redirect, Some(String::from("hello"))); - }, + } _ => panic!(), } } #[test] fn test_simple_purge() { - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let event = PubSubEvent::try_from(elem).unwrap(); match event { PubSubEvent::Purge { node } => { assert_eq!(node, NodeName(String::from("coucou"))); - }, + } _ => panic!(), } } @@ -381,14 +401,17 @@ mod tests { PubSubEvent::Configuration { node, form: _ } => { assert_eq!(node, NodeName(String::from("coucou"))); //assert_eq!(form.type_, Result_); - }, + } _ => panic!(), } } #[test] fn test_invalid() { - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let error = PubSubEvent::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -399,7 +422,9 @@ mod tests { #[test] fn test_invalid_attribute() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = PubSubEvent::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -419,16 +444,29 @@ mod tests { subid='ba49252aaa4f5d320c24d3766f0bdcade78c78d3' subscription='subscribed'/> -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let event = PubSubEvent::try_from(elem.clone()).unwrap(); match event.clone() { - PubSubEvent::Subscription { node, expiry, jid, subid, subscription } => { + PubSubEvent::Subscription { + node, + expiry, + jid, + subid, + subscription, + } => { assert_eq!(node, NodeName(String::from("princely_musings"))); - assert_eq!(subid, Some(SubscriptionId(String::from("ba49252aaa4f5d320c24d3766f0bdcade78c78d3")))); + assert_eq!( + subid, + Some(SubscriptionId(String::from( + "ba49252aaa4f5d320c24d3766f0bdcade78c78d3" + ))) + ); assert_eq!(subscription, Some(Subscription::Subscribed)); assert_eq!(jid, Some(Jid::from_str("francisco@denmark.lit").unwrap())); assert_eq!(expiry, Some("2006-02-28T23:59:59Z".parse().unwrap())); - }, + } _ => panic!(), } diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index cf3d980396cf1efc200985de342927e84b2c7c42..e406d867a0eb126ea0c4c901e2c48ef53fc883b3 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -4,19 +4,14 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - -use minidom::Element; -use jid::Jid; - +use crate::data_forms::DataForm; use crate::error::Error; - +use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; - -use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload}; -use crate::data_forms::DataForm; - -use crate::pubsub::{NodeName, ItemId, Subscription, SubscriptionId}; +use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId}; +use jid::Jid; +use minidom::Element; +use try_from::TryFrom; // TODO: a better solution would be to split this into a query and a result elements, like for // XEP-0030. @@ -137,7 +132,9 @@ impl TryFrom for Item { let mut payloads = elem.children().cloned().collect::>(); let payload = payloads.pop(); if !payloads.is_empty() { - return Err(Error::ParseError("More than a single payload in item element.")); + return Err(Error::ParseError( + "More than a single payload in item element.", + )); } Ok(Item { payload, @@ -149,10 +146,10 @@ impl TryFrom for Item { impl From for Element { fn from(item: Item) -> Element { Element::builder("item") - .ns(ns::PUBSUB) - .attr("id", item.id) - .append(item.payload) - .build() + .ns(ns::PUBSUB) + .attr("id", item.id) + .append(item.payload) + .build() } } @@ -199,7 +196,9 @@ generate_element!( generate_attribute!( /// Whether a retract request should notify subscribers or not. - Notify, "notify", bool + Notify, + "notify", + bool ); generate_element!( @@ -235,11 +234,15 @@ impl TryFrom for SubscribeOptions { for child in elem.children() { if child.is("required", ns::PUBSUB) { if required { - return Err(Error::ParseError("More than one required element in subscribe-options.")); + return Err(Error::ParseError( + "More than one required element in subscribe-options.", + )); } required = true; } else { - return Err(Error::ParseError("Unknown child in subscribe-options element.")); + return Err(Error::ParseError( + "Unknown child in subscribe-options element.", + )); } } Ok(SubscribeOptions { required }) @@ -251,12 +254,10 @@ impl From for Element { Element::builder("subscribe-options") .ns(ns::PUBSUB) .append(if subscribe_options.required { - vec!(Element::builder("required") - .ns(ns::PUBSUB) - .build()) - } else { - vec!() - }) + vec![Element::builder("required").ns(ns::PUBSUB).build()] + } else { + vec![] + }) .build() } } @@ -334,7 +335,7 @@ pub enum PubSub { create: Create, /// The configure request for the new node. - configure: Option + configure: Option, }, /// Request to publish items to a node, with optional options. @@ -343,7 +344,7 @@ pub enum PubSub { publish: Publish, /// The options related to this publish request. - publish_options: Option + publish_options: Option, }, /// A list of affiliations you have on a service, or on a node. @@ -383,75 +384,114 @@ impl TryFrom for PubSub { for child in elem.children() { if child.is("create", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError("Payload is already defined in pubsub element.")); + return Err(Error::ParseError( + "Payload is already defined in pubsub element.", + )); } let create = Create::try_from(child.clone())?; - payload = Some(PubSub::Create { create, configure: None }); + payload = Some(PubSub::Create { + create, + configure: None, + }); } else if child.is("configure", ns::PUBSUB) { if let Some(PubSub::Create { create, configure }) = payload { if configure.is_some() { - return Err(Error::ParseError("Configure is already defined in pubsub element.")); + return Err(Error::ParseError( + "Configure is already defined in pubsub element.", + )); } let configure = Some(Configure::try_from(child.clone())?); payload = Some(PubSub::Create { create, configure }); } else { - return Err(Error::ParseError("Payload is already defined in pubsub element.")); + return Err(Error::ParseError( + "Payload is already defined in pubsub element.", + )); } } else if child.is("publish", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError("Payload is already defined in pubsub element.")); + return Err(Error::ParseError( + "Payload is already defined in pubsub element.", + )); } let publish = Publish::try_from(child.clone())?; - payload = Some(PubSub::Publish { publish, publish_options: None }); + payload = Some(PubSub::Publish { + publish, + publish_options: None, + }); } else if child.is("publish-options", ns::PUBSUB) { - if let Some(PubSub::Publish { publish, publish_options }) = payload { + if let Some(PubSub::Publish { + publish, + publish_options, + }) = payload + { if publish_options.is_some() { - return Err(Error::ParseError("Publish-options are already defined in pubsub element.")); + return Err(Error::ParseError( + "Publish-options are already defined in pubsub element.", + )); } let publish_options = Some(PublishOptions::try_from(child.clone())?); - payload = Some(PubSub::Publish { publish, publish_options }); + payload = Some(PubSub::Publish { + publish, + publish_options, + }); } else { - return Err(Error::ParseError("Payload is already defined in pubsub element.")); + return Err(Error::ParseError( + "Payload is already defined in pubsub element.", + )); } } else if child.is("affiliations", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError("Payload is already defined in pubsub element.")); + return Err(Error::ParseError( + "Payload is already defined in pubsub element.", + )); } let affiliations = Affiliations::try_from(child.clone())?; payload = Some(PubSub::Affiliations(affiliations)); } else if child.is("default", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError("Payload is already defined in pubsub element.")); + return Err(Error::ParseError( + "Payload is already defined in pubsub element.", + )); } let default = Default::try_from(child.clone())?; payload = Some(PubSub::Default(default)); } else if child.is("items", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError("Payload is already defined in pubsub element.")); + return Err(Error::ParseError( + "Payload is already defined in pubsub element.", + )); } let items = Items::try_from(child.clone())?; payload = Some(PubSub::Items(items)); } else if child.is("retract", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError("Payload is already defined in pubsub element.")); + return Err(Error::ParseError( + "Payload is already defined in pubsub element.", + )); } let retract = Retract::try_from(child.clone())?; payload = Some(PubSub::Retract(retract)); } else if child.is("subscription", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError("Payload is already defined in pubsub element.")); + return Err(Error::ParseError( + "Payload is already defined in pubsub element.", + )); } let subscription = SubscriptionElem::try_from(child.clone())?; payload = Some(PubSub::Subscription(subscription)); } else if child.is("subscriptions", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError("Payload is already defined in pubsub element.")); + return Err(Error::ParseError( + "Payload is already defined in pubsub element.", + )); } let subscriptions = Subscriptions::try_from(child.clone())?; payload = Some(PubSub::Subscriptions(subscriptions)); } else if child.is("unsubscribe", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError("Payload is already defined in pubsub element.")); + return Err(Error::ParseError( + "Payload is already defined in pubsub element.", + )); } let unsubscribe = Unsubscribe::try_from(child.clone())?; payload = Some(PubSub::Unsubscribe(unsubscribe)); @@ -468,28 +508,31 @@ impl From for Element { Element::builder("pubsub") .ns(ns::PUBSUB) .append(match pubsub { - PubSub::Create { create, configure } => { - let mut elems = vec!(Element::from(create)); - if let Some(configure) = configure { - elems.push(Element::from(configure)); - } - elems - }, - PubSub::Publish { publish, publish_options } => { - let mut elems = vec!(Element::from(publish)); - if let Some(publish_options) = publish_options { - elems.push(Element::from(publish_options)); - } - elems - }, - PubSub::Affiliations(affiliations) => vec!(Element::from(affiliations)), - PubSub::Default(default) => vec!(Element::from(default)), - PubSub::Items(items) => vec!(Element::from(items)), - PubSub::Retract(retract) => vec!(Element::from(retract)), - PubSub::Subscription(subscription) => vec!(Element::from(subscription)), - PubSub::Subscriptions(subscriptions) => vec!(Element::from(subscriptions)), - PubSub::Unsubscribe(unsubscribe) => vec!(Element::from(unsubscribe)), - }) + PubSub::Create { create, configure } => { + let mut elems = vec![Element::from(create)]; + if let Some(configure) = configure { + elems.push(Element::from(configure)); + } + elems + } + PubSub::Publish { + publish, + publish_options, + } => { + let mut elems = vec![Element::from(publish)]; + if let Some(publish_options) = publish_options { + elems.push(Element::from(publish_options)); + } + elems + } + PubSub::Affiliations(affiliations) => vec![Element::from(affiliations)], + PubSub::Default(default) => vec![Element::from(default)], + PubSub::Items(items) => vec![Element::from(items)], + PubSub::Retract(retract) => vec![Element::from(retract)], + PubSub::Subscription(subscription) => vec![Element::from(subscription)], + PubSub::Subscriptions(subscriptions) => vec![Element::from(subscriptions)], + PubSub::Unsubscribe(unsubscribe) => vec![Element::from(unsubscribe)], + }) .build() } } @@ -501,7 +544,9 @@ mod tests { #[test] fn create() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let elem1 = elem.clone(); let pubsub = PubSub::try_from(elem).unwrap(); match pubsub.clone() { @@ -515,7 +560,10 @@ mod tests { let elem2 = Element::from(pubsub); assert!(elem1.compare_to(&elem2)); - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let elem1 = elem.clone(); let pubsub = PubSub::try_from(elem).unwrap(); match pubsub.clone() { @@ -532,7 +580,10 @@ mod tests { #[test] fn create_and_configure() { - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let elem1 = elem.clone(); let pubsub = PubSub::try_from(elem).unwrap(); match pubsub.clone() { @@ -549,11 +600,17 @@ mod tests { #[test] fn publish() { - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let elem1 = elem.clone(); let pubsub = PubSub::try_from(elem).unwrap(); match pubsub.clone() { - PubSub::Publish { publish, publish_options } => { + PubSub::Publish { + publish, + publish_options, + } => { assert_eq!(&publish.node.0, "coucou"); assert!(publish_options.is_none()); } @@ -570,7 +627,10 @@ mod tests { let elem1 = elem.clone(); let pubsub = PubSub::try_from(elem).unwrap(); match pubsub.clone() { - PubSub::Publish { publish, publish_options } => { + PubSub::Publish { + publish, + publish_options, + } => { assert_eq!(&publish.node.0, "coucou"); assert!(publish_options.unwrap().form.is_none()); } @@ -583,7 +643,9 @@ mod tests { #[test] fn invalid_empty_pubsub() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = PubSub::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -596,12 +658,17 @@ mod tests { fn publish_option() { let elem: Element = "http://jabber.org/protocol/pubsub#publish-options".parse().unwrap(); let publish_options = PublishOptions::try_from(elem).unwrap(); - assert_eq!(&publish_options.form.unwrap().form_type.unwrap(), "http://jabber.org/protocol/pubsub#publish-options"); + assert_eq!( + &publish_options.form.unwrap().form_type.unwrap(), + "http://jabber.org/protocol/pubsub#publish-options" + ); } #[test] fn subscribe_options() { - let elem1: Element = "".parse().unwrap(); + let elem1: Element = "" + .parse() + .unwrap(); let subscribe_options1 = SubscribeOptions::try_from(elem1).unwrap(); assert_eq!(subscribe_options1.required, false); diff --git a/src/receipts.rs b/src/receipts.rs index 61877f576d2ebbb2d11aef84f53de0b39c20dec9..c97d3e118358630758fa253d5e0153fbea3cf8cc 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -9,7 +9,9 @@ use crate::message::MessagePayload; generate_empty_element!( /// Requests that this message is acked by the final recipient once /// received. - Request, "request", RECEIPTS + Request, + "request", + RECEIPTS ); impl MessagePayload for Request {} @@ -29,9 +31,9 @@ impl MessagePayload for Received {} #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; use crate::ns; + use minidom::Element; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -55,7 +57,9 @@ mod tests { let elem: Element = "".parse().unwrap(); Received::try_from(elem).unwrap(); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); Received::try_from(elem).unwrap(); } diff --git a/src/roster.rs b/src/roster.rs index 86d023098767cb2a530bb2f16cc84d4a8276eaca..40cc7a25dc7f8a7f935306df4507e3cff8d4862e 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -4,12 +4,14 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use jid::Jid; -use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload}; generate_elem_id!( /// Represents a group a contact is part of. - Group, "group", ROSTER + Group, + "group", + ROSTER ); generate_attribute!( @@ -79,11 +81,11 @@ impl IqResultPayload for Roster {} #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; + use crate::compare_elements::NamespaceAwareCompare; use crate::error::Error; + use minidom::Element; use std::str::FromStr; - use crate::compare_elements::NamespaceAwareCompare; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -122,7 +124,9 @@ mod tests { let roster2 = Roster::try_from(elem2).unwrap(); assert_eq!(roster.items, roster2.items); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let roster = Roster::try_from(elem).unwrap(); assert_eq!(roster.ver, Some(String::from("ver9"))); assert!(roster.items.is_empty()); @@ -141,14 +145,22 @@ mod tests { name='Benvolio' subscription='both'/> -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let roster = Roster::try_from(elem).unwrap(); assert_eq!(roster.ver, Some(String::from("ver11"))); assert_eq!(roster.items.len(), 3); - assert_eq!(roster.items[0].jid, Jid::from_str("romeo@example.net").unwrap()); + assert_eq!( + roster.items[0].jid, + Jid::from_str("romeo@example.net").unwrap() + ); assert_eq!(roster.items[0].name, Some(String::from("Romeo"))); assert_eq!(roster.items[0].subscription, Subscription::Both); - assert_eq!(roster.items[0].groups, vec!(Group::from_str("Friends").unwrap())); + assert_eq!( + roster.items[0].groups, + vec!(Group::from_str("Friends").unwrap()) + ); } #[test] @@ -160,12 +172,17 @@ mod tests { B -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let elem1 = elem.clone(); let roster = Roster::try_from(elem).unwrap(); assert!(roster.ver.is_none()); assert_eq!(roster.items.len(), 1); - assert_eq!(roster.items[0].jid, Jid::from_str("test@example.org").unwrap()); + assert_eq!( + roster.items[0].jid, + Jid::from_str("test@example.org").unwrap() + ); assert_eq!(roster.items[0].name, None); assert_eq!(roster.items[0].groups.len(), 2); assert_eq!(roster.items[0].groups[0], Group::from_str("A").unwrap()); @@ -176,7 +193,10 @@ mod tests { #[test] fn test_set() { - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let roster = Roster::try_from(elem).unwrap(); assert!(roster.ver.is_none()); assert_eq!(roster.items.len(), 1); @@ -188,25 +208,38 @@ mod tests { Servants -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let roster = Roster::try_from(elem).unwrap(); assert!(roster.ver.is_none()); assert_eq!(roster.items.len(), 1); - assert_eq!(roster.items[0].jid, Jid::from_str("nurse@example.com").unwrap()); + assert_eq!( + roster.items[0].jid, + Jid::from_str("nurse@example.com").unwrap() + ); assert_eq!(roster.items[0].name, Some(String::from("Nurse"))); assert_eq!(roster.items[0].groups.len(), 1); - assert_eq!(roster.items[0].groups[0], Group::from_str("Servants").unwrap()); + assert_eq!( + roster.items[0].groups[0], + Group::from_str("Servants").unwrap() + ); let elem: Element = r#" -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let roster = Roster::try_from(elem).unwrap(); assert!(roster.ver.is_none()); assert_eq!(roster.items.len(), 1); - assert_eq!(roster.items[0].jid, Jid::from_str("nurse@example.com").unwrap()); + assert_eq!( + roster.items[0].jid, + Jid::from_str("nurse@example.com").unwrap() + ); assert!(roster.items[0].name.is_none()); assert!(roster.items[0].groups.is_empty()); assert_eq!(roster.items[0].subscription, Subscription::Remove); @@ -214,7 +247,9 @@ mod tests { #[test] fn test_invalid() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Roster::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -222,7 +257,9 @@ mod tests { }; assert_eq!(message, "Unknown child in query element."); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Roster::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -233,7 +270,9 @@ mod tests { #[test] fn test_invalid_item() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Roster::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -251,7 +290,10 @@ mod tests { assert_eq!(error.description(), "Invalid JID, I guess?"); */ - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let error = Roster::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, diff --git a/src/rsm.rs b/src/rsm.rs index 6bb02ba736f23d2be3ae3986907d50fb411b51de..afa08be66f6361eaee0aa543116c791092bc3c76 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -74,12 +74,30 @@ impl TryFrom for SetQuery { impl From for Element { fn from(set: SetQuery) -> Element { Element::builder("set") - .ns(ns::RSM) - .append(set.max.map(|max| Element::builder("max").ns(ns::RSM).append(format!("{}", max)).build())) - .append(set.after.map(|after| Element::builder("after").ns(ns::RSM).append(after).build())) - .append(set.before.map(|before| Element::builder("before").ns(ns::RSM).append(before).build())) - .append(set.index.map(|index| Element::builder("index").ns(ns::RSM).append(format!("{}", index)).build())) - .build() + .ns(ns::RSM) + .append(set.max.map(|max| { + Element::builder("max") + .ns(ns::RSM) + .append(format!("{}", max)) + .build() + })) + .append( + set.after + .map(|after| Element::builder("after").ns(ns::RSM).append(after).build()), + ) + .append(set.before.map(|before| { + Element::builder("before") + .ns(ns::RSM) + .append(before) + .build() + })) + .append(set.index.map(|index| { + Element::builder("index") + .ns(ns::RSM) + .append(format!("{}", index)) + .build() + })) + .build() } } @@ -138,18 +156,27 @@ impl TryFrom for SetResult { impl From for Element { fn from(set: SetResult) -> Element { - let first = set.first.clone() - .map(|first| Element::builder("first") - .ns(ns::RSM) - .attr("index", set.first_index) - .append(first) - .build()); - Element::builder("set") + let first = set.first.clone().map(|first| { + Element::builder("first") .ns(ns::RSM) + .attr("index", set.first_index) .append(first) - .append(set.last.map(|last| Element::builder("last").ns(ns::RSM).append(last).build())) - .append(set.count.map(|count| Element::builder("count").ns(ns::RSM).append(format!("{}", count)).build())) .build() + }); + Element::builder("set") + .ns(ns::RSM) + .append(first) + .append( + set.last + .map(|last| Element::builder("last").ns(ns::RSM).append(last).build()), + ) + .append(set.count.map(|count| { + Element::builder("count") + .ns(ns::RSM) + .append(format!("{}", count)) + .build() + })) + .build() } } @@ -174,14 +201,18 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let set = SetQuery::try_from(elem).unwrap(); assert_eq!(set.max, None); assert_eq!(set.after, None); assert_eq!(set.before, None); assert_eq!(set.index, None); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let set = SetResult::try_from(elem).unwrap(); match set.first { Some(_) => panic!(), @@ -193,7 +224,9 @@ mod tests { #[test] fn test_unknown() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = SetQuery::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -201,7 +234,9 @@ mod tests { }; assert_eq!(message, "This is not a RSM set element."); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = SetResult::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -212,7 +247,9 @@ mod tests { #[test] fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = SetQuery::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -220,7 +257,9 @@ mod tests { }; assert_eq!(message, "Unknown child in set element."); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = SetResult::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -231,7 +270,9 @@ mod tests { #[test] fn test_serialise() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let rsm = SetQuery { max: None, after: None, @@ -241,7 +282,9 @@ mod tests { let elem2 = rsm.into(); assert_eq!(elem, elem2); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let rsm = SetResult { first: None, first_index: None, @@ -254,7 +297,10 @@ mod tests { #[test] fn test_first_index() { - let elem: Element = "coucou".parse().unwrap(); + let elem: Element = + "coucou" + .parse() + .unwrap(); let elem1 = elem.clone(); let set = SetResult::try_from(elem).unwrap(); assert_eq!(set.first, Some(String::from("coucou"))); diff --git a/src/sasl.rs b/src/sasl.rs index 30fa653a6baca3df08534c76befbd3ad905a8de7..3ae0c7adfd8a076d5d370a63f33c78de3884d8fd 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -6,10 +6,10 @@ use std::collections::BTreeMap; -use try_from::TryFrom; -use minidom::Element; use crate::error::Error; use crate::ns; +use minidom::Element; +use try_from::TryFrom; use crate::helpers::Base64; @@ -84,7 +84,9 @@ generate_element!( generate_empty_element!( /// Sent by the client at any point after [auth](struct.Auth.html) if it /// wants to cancel the current authentication process. - Abort, "abort", SASL + Abort, + "abort", + SASL ); generate_element!( @@ -166,11 +168,15 @@ impl TryFrom for Failure { check_no_children!(child, "text"); let lang = get_attr!(child, "xml:lang", default); if texts.insert(lang, child.text()).is_some() { - return Err(Error::ParseError("Text element present twice for the same xml:lang in failure element.")); + return Err(Error::ParseError( + "Text element present twice for the same xml:lang in failure element.", + )); } } else if child.has_ns(ns::SASL) { if defined_condition.is_some() { - return Err(Error::ParseError("Failure must not have more than one defined-condition.")); + return Err(Error::ParseError( + "Failure must not have more than one defined-condition.", + )); } check_no_attributes!(child, "defined-condition"); check_no_children!(child, "defined-condition"); @@ -184,7 +190,8 @@ impl TryFrom for Failure { return Err(Error::ParseError("Unknown element in Failure.")); } } - let defined_condition = defined_condition.ok_or(Error::ParseError("Failure must have a defined-condition."))?; + let defined_condition = + defined_condition.ok_or(Error::ParseError("Failure must have a defined-condition."))?; Ok(Failure { defined_condition: defined_condition, @@ -196,24 +203,30 @@ impl TryFrom for Failure { impl From for Element { fn from(failure: Failure) -> Element { Element::builder("failure") - .ns(ns::SASL) - .append(failure.defined_condition) - .append(failure.texts.into_iter().map(|(lang, text)| { - Element::builder("text") - .ns(ns::SASL) - .attr("xml:lang", lang) - .append(text) - .build() - }).collect::>()) - .build() + .ns(ns::SASL) + .append(failure.defined_condition) + .append( + failure + .texts + .into_iter() + .map(|(lang, text)| { + Element::builder("text") + .ns(ns::SASL) + .attr("xml:lang", lang) + .append(text) + .build() + }) + .collect::>(), + ) + .build() } } #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; use minidom::Element; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -243,7 +256,9 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let auth = Auth::try_from(elem).unwrap(); assert_eq!(auth.mechanism, Mechanism::Plain); assert!(auth.data.is_empty()); @@ -251,7 +266,10 @@ mod tests { #[test] fn section_6_5_1() { - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let failure = Failure::try_from(elem).unwrap(); assert_eq!(failure.defined_condition, DefinedCondition::Aborted); assert!(failure.texts.is_empty()); @@ -262,9 +280,14 @@ mod tests { let elem: Element = " Call 212-555-1212 for assistance. - ".parse().unwrap(); + " + .parse() + .unwrap(); let failure = Failure::try_from(elem).unwrap(); assert_eq!(failure.defined_condition, DefinedCondition::AccountDisabled); - assert_eq!(failure.texts["en"], String::from("Call 212-555-1212 for assistance.")); + assert_eq!( + failure.texts["en"], + String::from("Call 212-555-1212 for assistance.") + ); } } diff --git a/src/sm.rs b/src/sm.rs index b0d08d3e62cca65162dab61c306206b3a8715c73..fc8e7377609a0db349799f2cc9bf033fafd19fe8 100644 --- a/src/sm.rs +++ b/src/sm.rs @@ -24,7 +24,9 @@ impl A { generate_attribute!( /// Whether to allow resumption of a previous stream. - ResumeAttr, "resume", bool + ResumeAttr, + "resume", + bool ); generate_element!( @@ -103,7 +105,9 @@ generate_element!( generate_empty_element!( /// Requests the currently received stanzas by the other party. - R, "r", SM + R, + "r", + SM ); generate_element!( @@ -135,14 +139,16 @@ generate_element!( // TODO: add support for optional and required. generate_empty_element!( /// Represents availability of Stream Management in ``. - StreamManagement, "sm", SM + StreamManagement, + "sm", + SM ); #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; use minidom::Element; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -189,12 +195,16 @@ mod tests { #[test] fn resume() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let enable = Enable::try_from(elem).unwrap(); assert_eq!(enable.max, None); assert_eq!(enable.resume, ResumeAttr::True); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let enabled = Enabled::try_from(elem).unwrap(); let previd = enabled.id.unwrap(); assert_eq!(enabled.resume, ResumeAttr::True); @@ -202,12 +212,16 @@ mod tests { assert_eq!(enabled.max, Some(600)); assert_eq!(enabled.location, None); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let resume = Resume::try_from(elem).unwrap(); assert_eq!(resume.h, 5); assert_eq!(resume.previd, previd); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let resumed = Resumed::try_from(elem).unwrap(); assert_eq!(resumed.h, 5); assert_eq!(resumed.previd, previd); diff --git a/src/stanza_error.rs b/src/stanza_error.rs index ec31ecb1d5d23d7562b580af934eef458c6bd11c..a0848eec439f6cc8e143a3c0ca8d171b5b99a1bf 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -4,16 +4,16 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; use std::collections::BTreeMap; +use try_from::TryFrom; use minidom::Element; +use crate::error::Error; use crate::message::MessagePayload; +use crate::ns; use crate::presence::PresencePayload; -use crate::error::Error; use jid::Jid; -use crate::ns; generate_attribute!( /// The type of the error. @@ -234,23 +234,30 @@ impl TryFrom for StanzaError { check_no_children!(child, "text"); let lang = get_attr!(elem, "xml:lang", default); if texts.insert(lang, child.text()).is_some() { - return Err(Error::ParseError("Text element present twice for the same xml:lang.")); + return Err(Error::ParseError( + "Text element present twice for the same xml:lang.", + )); } } else if child.has_ns(ns::XMPP_STANZAS) { if defined_condition.is_some() { - return Err(Error::ParseError("Error must not have more than one defined-condition.")); + return Err(Error::ParseError( + "Error must not have more than one defined-condition.", + )); } check_no_children!(child, "defined-condition"); let condition = DefinedCondition::try_from(child.clone())?; defined_condition = Some(condition); } else { if other.is_some() { - return Err(Error::ParseError("Error must not have more than one other element.")); + return Err(Error::ParseError( + "Error must not have more than one other element.", + )); } other = Some(child.clone()); } } - let defined_condition = defined_condition.ok_or(Error::ParseError("Error must have a defined-condition."))?; + let defined_condition = + defined_condition.ok_or(Error::ParseError("Error must have a defined-condition."))?; Ok(StanzaError { type_: type_, @@ -265,17 +272,17 @@ impl TryFrom for StanzaError { impl From for Element { fn from(err: StanzaError) -> Element { let mut root = Element::builder("error") - .ns(ns::DEFAULT_NS) - .attr("type", err.type_) - .attr("by", err.by) - .append(err.defined_condition) - .build(); + .ns(ns::DEFAULT_NS) + .attr("type", err.type_) + .attr("by", err.by) + .append(err.defined_condition) + .build(); for (lang, text) in err.texts { let elem = Element::builder("text") - .ns(ns::XMPP_STANZAS) - .attr("xml:lang", lang) - .append(text) - .build(); + .ns(ns::XMPP_STANZAS) + .attr("xml:lang", lang) + .append(text) + .build(); root.append_child(elem); } if let Some(other) = err.other { @@ -313,7 +320,10 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = StanzaError::try_from(elem).unwrap(); assert_eq!(error.type_, ErrorType::Cancel); - assert_eq!(error.defined_condition, DefinedCondition::UndefinedCondition); + assert_eq!( + error.defined_condition, + DefinedCondition::UndefinedCondition + ); } #[test] @@ -330,9 +340,13 @@ mod tests { assert_eq!(message, "Required attribute 'type' missing."); #[cfg(not(feature = "component"))] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); #[cfg(feature = "component")] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = StanzaError::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -344,9 +358,13 @@ mod tests { #[test] fn test_invalid_condition() { #[cfg(not(feature = "component"))] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); #[cfg(feature = "component")] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = StanzaError::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 87595d4324bc9a26ba8b13f6a76ff3f3ebfc0087..2bac2de3e1d7be592ebc053bf1a84d30edb6187a 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -37,10 +37,10 @@ impl MessagePayload for OriginId {} #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; use crate::error::Error; + use minidom::Element; use std::str::FromStr; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -58,19 +58,25 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let stanza_id = StanzaId::try_from(elem).unwrap(); assert_eq!(stanza_id.id, String::from("coucou")); assert_eq!(stanza_id.by, Jid::from_str("coucou@coucou").unwrap()); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let origin_id = OriginId::try_from(elem).unwrap(); assert_eq!(origin_id.id, String::from("coucou")); } #[test] fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = StanzaId::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -92,7 +98,9 @@ mod tests { #[test] fn test_invalid_by() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = StanzaId::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -103,8 +111,13 @@ mod tests { #[test] fn test_serialise() { - let elem: Element = "".parse().unwrap(); - let stanza_id = StanzaId { id: String::from("coucou"), by: Jid::from_str("coucou@coucou").unwrap() }; + let elem: Element = "" + .parse() + .unwrap(); + let stanza_id = StanzaId { + id: String::from("coucou"), + by: Jid::from_str("coucou@coucou").unwrap(), + }; let elem2 = stanza_id.into(); assert_eq!(elem, elem2); } diff --git a/src/stream.rs b/src/stream.rs index ad8ec4f1cf8371565b31f9ac72a4dfe7c572104d..fa416fd80dd7e361169cd909eb175e3a720de6f4 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -73,8 +73,8 @@ impl Stream { #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; use minidom::Element; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/version.rs b/src/version.rs index 4e969f4a2004dfa6457b543de1ffcfc593b4acea..c9e08c32199060179e7be0ab3b3229c3ce9a51d0 100644 --- a/src/version.rs +++ b/src/version.rs @@ -11,7 +11,9 @@ generate_empty_element!( /// /// It should only be used in an ``, as it can only /// represent the request, and not a result. - VersionQuery, "query", VERSION + VersionQuery, + "query", + VERSION ); impl IqGetPayload for VersionQuery {} @@ -39,9 +41,9 @@ impl IqResultPayload for VersionResult {} #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; use crate::compare_elements::NamespaceAwareCompare; + use minidom::Element; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -59,7 +61,10 @@ mod tests { #[test] fn simple() { - let elem: Element = "xmpp-rs0.3.0".parse().unwrap(); + let elem: Element = + "xmpp-rs0.3.0" + .parse() + .unwrap(); let version = VersionResult::try_from(elem).unwrap(); assert_eq!(version.name, String::from("xmpp-rs")); assert_eq!(version.version, String::from("0.3.0")); @@ -74,7 +79,10 @@ mod tests { os: None, }; let elem1 = Element::from(version); - let elem2: Element = "xmpp-rs0.3.0".parse().unwrap(); + let elem2: Element = + "xmpp-rs0.3.0" + .parse() + .unwrap(); println!("{:?}", elem1); assert!(elem1.compare_to(&elem2)); } diff --git a/src/websocket.rs b/src/websocket.rs index f2d28ebfe2892ff84787a67375b53de73134822c..10693ca7d4bee4f9c6278e65c5a8815791a0123b 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -72,8 +72,8 @@ impl Open { #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; use minidom::Element; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -89,7 +89,9 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let open = Open::try_from(elem).unwrap(); assert_eq!(open.from, None); assert_eq!(open.to, None); From 83f7c67870867c0cfb20f59f125b693c56cf6999 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 18 Dec 2018 15:39:37 +0100 Subject: [PATCH 540/698] Remove now-useless "extern crate"s. --- src/lib.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f35d5b956c691bd6375dc9f1e44155bc952f77b9..36eb788250d34834804e416b93a55a1c839e45fc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,17 +24,6 @@ #![deny(missing_docs)] -extern crate base64; -extern crate blake2; -extern crate chrono; -extern crate digest; -extern crate jid; -extern crate minidom; -extern crate sha1; -extern crate sha2; -extern crate sha3; -extern crate try_from; - pub use minidom::Element; /// Error type returned by every parser on failure. From 5e64dee0edb4a1cee0ed8d46a97f634adafbc10e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 18 Dec 2018 15:43:49 +0100 Subject: [PATCH 541/698] Only define assert_size!() macro when testing. --- src/macros.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/macros.rs b/src/macros.rs index ac77ed557d59cc60dcfa734fc4792f95b99cd20e..d1a407406be2923e1d432eed1ebc6afada805206 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -552,6 +552,7 @@ macro_rules! generate_element { ); } +#[cfg(test)] macro_rules! assert_size ( ($t:ty, $sz:expr) => ( assert_eq!(::std::mem::size_of::<$t>(), $sz); From 7a204cd18267d0022b0a20bb0b162ec1cc2ac397 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 18 Dec 2018 15:44:07 +0100 Subject: [PATCH 542/698] Also test the size of IBB StreamId. --- src/ibb.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ibb.rs b/src/ibb.rs index f778ddf1aa3aebdb55c3e778ec4946dcc3419947..f2aff83efe950cbd8475382dc054464d09393330 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -80,6 +80,7 @@ mod tests { #[cfg(target_pointer_width = "32")] #[test] fn test_size() { + assert_size!(StreamId, 12); assert_size!(Stanza, 1); assert_size!(Open, 16); assert_size!(Data, 28); @@ -89,6 +90,7 @@ mod tests { #[cfg(target_pointer_width = "64")] #[test] fn test_size() { + assert_size!(StreamId, 24); assert_size!(Stanza, 1); assert_size!(Open, 32); assert_size!(Data, 56); From 090a16953b2b09e1b815ee0b640d501151be4ee5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 18 Dec 2018 16:00:25 +0100 Subject: [PATCH 543/698] bind: Add forgotten test for attributes. --- src/bind.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/bind.rs b/src/bind.rs index ddda0392b67b2105977923cf0f47e62a4dbf8e75..fe9472b2cc3c2192c959cd7c076dc87d9adc2786 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -55,9 +55,11 @@ impl TryFrom for Bind { return Err(Error::ParseError("Bind can only have one child.")); } if child.is("resource", ns::BIND) { + check_no_attributes!(child, "resource"); check_no_children!(child, "resource"); bind = Bind::Resource(child.text()); } else if child.is("jid", ns::BIND) { + check_no_attributes!(child, "jid"); check_no_children!(child, "jid"); bind = Bind::Jid(Jid::from_str(&child.text())?); } else { @@ -109,4 +111,27 @@ mod tests { let bind = Bind::try_from(elem).unwrap(); assert_eq!(bind, Bind::None); } + + #[test] + fn test_invalid_resource() { + let elem: Element = "resource" + .parse() + .unwrap(); + let error = Bind::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in resource element."); + + let elem: Element = "resource" + .parse() + .unwrap(); + let error = Bind::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in resource element."); + } } From 376ccc1c063937c60d841c5ed037db2d30b0988b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 18 Dec 2018 16:07:46 +0100 Subject: [PATCH 544/698] Run `cargo fmt` on some more files. --- src/presence.rs | 12 ++++-------- src/rsm.rs | 7 ++----- src/sasl.rs | 6 ++---- src/stanza_error.rs | 8 +++----- 4 files changed, 11 insertions(+), 22 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index 8f3d2cf109a806ab68600cb12caa159412255553..765e680d87d7ef36aab84f85dd178252aa3467ff 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -5,18 +5,14 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use crate::error::Error; +use crate::ns; +use jid::Jid; +use minidom::{Element, ElementEmitter, IntoAttributeValue, IntoElements}; use std::collections::BTreeMap; use std::str::FromStr; use try_from::TryFrom; -use minidom::{Element, ElementEmitter, IntoAttributeValue, IntoElements}; - -use jid::Jid; - -use crate::error::Error; - -use crate::ns; - /// Should be implemented on every known payload of a ``. pub trait PresencePayload: TryFrom + Into {} diff --git a/src/rsm.rs b/src/rsm.rs index afa08be66f6361eaee0aa543116c791092bc3c76..0cd048ec854d5c4bb11c16906093356c0498aaff 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -4,13 +4,10 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - -use minidom::Element; - use crate::error::Error; - use crate::ns; +use minidom::Element; +use try_from::TryFrom; /// Requests paging through a potentially big set of items (represented by an /// UID). diff --git a/src/sasl.rs b/src/sasl.rs index 3ae0c7adfd8a076d5d370a63f33c78de3884d8fd..05dd250aba849569c445349720e024dee0e0179a 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -4,15 +4,13 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::collections::BTreeMap; - use crate::error::Error; +use crate::helpers::Base64; use crate::ns; use minidom::Element; +use std::collections::BTreeMap; use try_from::TryFrom; -use crate::helpers::Base64; - generate_attribute!( /// The list of available SASL mechanisms. Mechanism, "mechanism", { diff --git a/src/stanza_error.rs b/src/stanza_error.rs index a0848eec439f6cc8e143a3c0ca8d171b5b99a1bf..c5e62026848b668e5d269d0fdeb8c4f1e3a7eeef 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -4,16 +4,14 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::collections::BTreeMap; -use try_from::TryFrom; - -use minidom::Element; - use crate::error::Error; use crate::message::MessagePayload; use crate::ns; use crate::presence::PresencePayload; use jid::Jid; +use minidom::Element; +use std::collections::BTreeMap; +use try_from::TryFrom; generate_attribute!( /// The type of the error. From cf0cfda6b50ed4a7727223c8ad46a858943f277f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Dec 2018 16:46:36 +0100 Subject: [PATCH 545/698] Link .hgignore to .gitignore. --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 120000 .gitignore diff --git a/.gitignore b/.gitignore new file mode 120000 index 0000000000000000000000000000000000000000..913762c1ff668d96df2c6513e091ff77fbb4277c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.hgignore \ No newline at end of file From f6593aa74b9a0ff05990f0225cf0e7b91013e786 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Dec 2018 16:56:15 +0100 Subject: [PATCH 546/698] Setup CI for GitLab. --- .gitlab-ci.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..0b4e77f06d233074e4efced741958cc6a524498c --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,16 @@ +stages: + - build + +rust-latest: + stage: build + image: rust:latest + script: + - cargo build --verbose + - cargo test --verbose + +rust-nightly: + stage: build + image: rustlang/rust:nightly + script: + - cargo build --verbose + - cargo test --verbose From 96c8b056779122e4be173b202317f16f6902495a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Dec 2018 18:29:11 +0100 Subject: [PATCH 547/698] Do some manual formatting in macros. --- src/macros.rs | 83 +++++++++++++++++++++++---------------------- src/muc/user.rs | 2 +- src/presence.rs | 2 +- src/pubsub/event.rs | 4 +-- 4 files changed, 47 insertions(+), 44 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index d1a407406be2923e1d432eed1ebc6afada805206..62c59736dc5a52435d988e88b30597f22a3f5097 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -29,7 +29,7 @@ macro_rules! get_attr { "Required attribute '", $attr, "' missing." - ))) + ))); } } }; @@ -151,8 +151,8 @@ macro_rules! generate_element_enum { #[derive(Debug, Clone, PartialEq)] pub enum $elem { $( - $(#[$enum_meta])* - $enum + $(#[$enum_meta])* + $enum ),+ } impl ::try_from::TryFrom<::minidom::Element> for $elem { @@ -169,10 +169,13 @@ macro_rules! generate_element_enum { } impl From<$elem> for ::minidom::Element { fn from(elem: $elem) -> ::minidom::Element { - ::minidom::Element::builder(match elem { - $($elem::$enum => $enum_name,)+ - }).ns(crate::ns::$ns) - .build() + ::minidom::Element::builder( + match elem { + $($elem::$enum => $enum_name,)+ + } + ) + .ns(crate::ns::$ns) + .build() } } ); @@ -187,8 +190,8 @@ macro_rules! generate_attribute_enum { #[derive(Debug, Clone, PartialEq)] pub enum $elem { $( - $(#[$enum_meta])* - $enum + $(#[$enum_meta])* + $enum ),+ } impl ::try_from::TryFrom<::minidom::Element> for $elem { @@ -206,11 +209,11 @@ macro_rules! generate_attribute_enum { impl From<$elem> for ::minidom::Element { fn from(elem: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) - .ns(crate::ns::$ns) - .attr($attr, match elem { - $($elem::$enum => $enum_name,)+ - }) - .build() + .ns(crate::ns::$ns) + .attr($attr, match elem { + $($elem::$enum => $enum_name,)+ + }) + .build() } } ); @@ -271,9 +274,9 @@ macro_rules! check_no_unknown_attributes { ($elem:ident, $name:tt, [$($attr:tt),*]) => ( for (_attr, _) in $elem.attrs() { $( - if _attr == $attr { - continue; - } + if _attr == $attr { + continue; + } )* return Err(crate::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); } @@ -300,8 +303,8 @@ macro_rules! generate_empty_element { impl From<$elem> for ::minidom::Element { fn from(_: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) - .ns(crate::ns::$ns) - .build() + .ns(crate::ns::$ns) + .build() } } ); @@ -352,9 +355,9 @@ macro_rules! generate_elem_id { impl From<$elem> for ::minidom::Element { fn from(elem: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) - .ns(crate::ns::$ns) - .append(elem.0) - .build() + .ns(crate::ns::$ns) + .append(elem.0) + .build() } } ); @@ -488,16 +491,16 @@ macro_rules! generate_element { #[derive(Debug, Clone)] pub struct $elem { $( - $(#[$attr_meta])* - pub $attr: $attr_type, + $(#[$attr_meta])* + pub $attr: $attr_type, )* $( - $(#[$child_meta])* - pub $child_ident: start_decl!($coucou, $child_type), + $(#[$child_meta])* + pub $child_ident: start_decl!($coucou, $child_type), )* $( - $(#[$text_meta])* - pub $text_ident: $text_type, + $(#[$text_meta])* + pub $text_ident: $text_type, )* } @@ -508,7 +511,7 @@ macro_rules! generate_element { check_self!(elem, $name, $ns); check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); $( - start_parse_elem!($child_ident: $coucou); + start_parse_elem!($child_ident: $coucou); )* for _child in elem.children() { $( @@ -521,13 +524,13 @@ macro_rules! generate_element { } Ok($elem { $( - $attr: get_attr!(elem, $attr_name, $attr_action), + $attr: get_attr!(elem, $attr_name, $attr_action), )* $( - $child_ident: finish_parse_elem!($child_ident: $coucou = $child_name, $name), + $child_ident: finish_parse_elem!($child_ident: $coucou = $child_name, $name), )* $( - $text_ident: $codec::decode(&elem.text())?, + $text_ident: $codec::decode(&elem.text())?, )* }) } @@ -536,17 +539,17 @@ macro_rules! generate_element { impl From<$elem> for ::minidom::Element { fn from(elem: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) - .ns(crate::ns::$ns) - $( + .ns(crate::ns::$ns) + $( .attr($attr_name, elem.$attr) - )* - $( + )* + $( .append(generate_serialiser!(elem, $child_ident, $coucou, $child_constructor, ($child_name, $child_ns))) - )* - $( + )* + $( .append($codec::encode(&elem.$text_ident)) - )* - .build() + )* + .build() } } ); diff --git a/src/muc/user.rs b/src/muc/user.rs index 3f058a1740803f879dcec7a873de4cbaccd9ff0c..2c1a6926a98b9099f207268bcb683ec9127070a1 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -102,7 +102,7 @@ impl TryFrom for Actor { (Some(_), Some(_)) | (None, None) => { return Err(Error::ParseError( "Either 'jid' or 'nick' attribute is required.", - )) + )); } (Some(jid), _) => Ok(Actor::Jid(jid)), (_, Some(nick)) => Ok(Actor::Nick(nick)), diff --git a/src/presence.rs b/src/presence.rs index 765e680d87d7ef36aab84f85dd178252aa3467ff..1f9846c2b7dc493b5f37d6c135327ebfcd125a68 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -136,7 +136,7 @@ impl FromStr for Type { _ => { return Err(Error::ParseError( "Invalid 'type' attribute on presence element.", - )) + )); } }) } diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index b8f2bc0066a17dc74c93e345b3a1f035f22d1ad5..fac8e2fe26b822297abf1438cfe6ae4332bd065d 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -138,7 +138,7 @@ fn parse_items(elem: Element, node: NodeName) -> Result { Some(true) => { return Err(Error::ParseError( "Mix of item and retract in items element.", - )) + )); } } items.push(Item::try_from(child.clone())?); @@ -149,7 +149,7 @@ fn parse_items(elem: Element, node: NodeName) -> Result { Some(false) => { return Err(Error::ParseError( "Mix of item and retract in items element.", - )) + )); } } check_no_children!(child, "retract"); From 1e85abd10cca63a7426eec56d1ed9d639c96437e Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 20 Dec 2018 20:58:13 +0100 Subject: [PATCH 548/698] sasl: Add test failure_with_non_prefixed_text_lang. --- src/sasl.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/sasl.rs b/src/sasl.rs index 05dd250aba849569c445349720e024dee0e0179a..86e10760b996632ded068544d3e2c05ded68c544 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -288,4 +288,20 @@ mod tests { String::from("Call 212-555-1212 for assistance.") ); } + + #[test] + fn failure_with_non_prefixed_text_lang() { + let elem: Element = " + + Invalid username or password + " + .parse() + .unwrap(); + let failure = Failure::try_from(elem).unwrap(); + assert_eq!(failure.defined_condition, DefinedCondition::NotAuthorized); + assert_eq!( + failure.texts["en"], + String::from("Invalid username or password") + ); + } } From 95f4ade4ba6d8d86ffa84cd49bd4021dad7f2bf3 Mon Sep 17 00:00:00 2001 From: Astro Date: Sun, 30 Dec 2018 00:36:29 +0100 Subject: [PATCH 549/698] compat mode that relaxes some of the check_* macros --- Cargo.toml | 2 ++ src/attention.rs | 3 +++ src/bind.rs | 1 + src/blocking.rs | 18 ++++++++++------ src/caps.rs | 1 + src/chatstates.rs | 2 ++ src/jingle_ft.rs | 49 +++++++++++++++++++++++++----------------- src/macros.rs | 3 +++ src/message_correct.rs | 1 + src/muc/muc.rs | 1 + src/muc/user.rs | 5 +++++ src/nick.rs | 3 +++ src/ping.rs | 3 +++ src/presence.rs | 2 ++ src/pubsub/event.rs | 1 + src/roster.rs | 1 + src/sasl.rs | 5 +---- 17 files changed, 70 insertions(+), 31 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2d3122cb9084fd17e1434003c591f8e2036d1454..65935bab1f028d897229ee24541225749156fd59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,3 +28,5 @@ try_from = "0.3.2" [features] # Build xmpp-parsers to make components instead of clients. component = [] +# Compatibility mode +compat = [] diff --git a/src/attention.rs b/src/attention.rs index be33bb3b6f07d002163bfa2ee46d4f3a7bc6b6c3..c786f3137fce92ed8eb178329fda4ee4481a1da5 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -18,6 +18,7 @@ impl MessagePayload for Attention {} #[cfg(test)] mod tests { use super::*; + #[cfg(not(feature = "compat"))] use crate::error::Error; use minidom::Element; use try_from::TryFrom; @@ -33,6 +34,7 @@ mod tests { Attention::try_from(elem).unwrap(); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid_child() { let elem: Element = "" @@ -46,6 +48,7 @@ mod tests { assert_eq!(message, "Unknown child in attention element."); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid_attribute() { let elem: Element = "" diff --git a/src/bind.rs b/src/bind.rs index fe9472b2cc3c2192c959cd7c076dc87d9adc2786..bd09d4f3b36aa326f51790cc3f0ad0f905f6a2a0 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -112,6 +112,7 @@ mod tests { assert_eq!(bind, Bind::None); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid_resource() { let elem: Element = "resource" diff --git a/src/blocking.rs b/src/blocking.rs index d34fee3d97a1935c5d825e7bfc836408e74885a2..b2fe70df28c9bf49cbd6448a79504baf8f08f0d5 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -155,13 +155,16 @@ mod tests { }, ]; - let request_elem = elem.clone(); - let error = BlocklistRequest::try_from(request_elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown child in blocklist element."); + #[cfg(not(feature = "compat"))] + { + let request_elem = elem.clone(); + let error = BlocklistRequest::try_from(request_elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in blocklist element."); + } let result_elem = elem.clone(); let result = BlocklistResult::try_from(result_elem).unwrap(); @@ -176,6 +179,7 @@ mod tests { assert_eq!(unblock.items, two_items); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid() { let elem: Element = "" diff --git a/src/caps.rs b/src/caps.rs index 72b04263f14bfd58a51276de57be39026c63d21e..3befdc9bc2541de8d5d401c362f4733124eecb03 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -233,6 +233,7 @@ mod tests { ); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid_child() { let elem: Element = "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=".parse().unwrap(); diff --git a/src/chatstates.rs b/src/chatstates.rs index 75d64d91b1549651793e2fd2c2775f811743f64a..9a972aa79a4e97a820c62ac1f76275936240b214 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -63,6 +63,7 @@ mod tests { assert_eq!(message, "This is not a chatstate element."); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid_child() { let elem: Element = "" @@ -76,6 +77,7 @@ mod tests { assert_eq!(message, "Unknown child in chatstate element."); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid_attribute() { let elem: Element = "" diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 381eb21fa288b1c37ac00ef139d4f033750b454c..359cd8b07c17f86094696d201573b5c9169bdc2f 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -510,13 +510,16 @@ mod tests { }; assert_eq!(message, "Unknown child in received element."); - let elem: Element = "".parse().unwrap(); - let error = Received::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown attribute in received element."); + #[cfg(not(feature = "compat"))] + { + let elem: Element = "".parse().unwrap(); + let error = Received::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in received element."); + } let elem: Element = "" @@ -575,13 +578,16 @@ mod tests { }; assert_eq!(message, "This is not a file element."); - let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); - let error = Checksum::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown attribute in checksum element."); + #[cfg(not(feature = "compat"))] + { + let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); + let error = Checksum::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in checksum element."); + } let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); let error = Checksum::try_from(elem).unwrap_err(); @@ -631,11 +637,14 @@ mod tests { let elem: Element = "" .parse() .unwrap(); - let error = Range::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown attribute in range element."); + #[cfg(not(feature = "compat"))] + { + let error = Range::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in range element."); + } } } diff --git a/src/macros.rs b/src/macros.rs index 62c59736dc5a52435d988e88b30597f22a3f5097..daccec5c676346e14468723f34b9151e044972a2 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -248,6 +248,7 @@ macro_rules! check_ns_only { macro_rules! check_no_children { ($elem:ident, $name:tt) => { + #[cfg(not(feature = "compat"))] for _ in $elem.children() { return Err(crate::error::Error::ParseError(concat!( "Unknown child in ", @@ -260,6 +261,7 @@ macro_rules! check_no_children { macro_rules! check_no_attributes { ($elem:ident, $name:tt) => { + #[cfg(not(feature = "compat"))] for _ in $elem.attrs() { return Err(crate::error::Error::ParseError(concat!( "Unknown attribute in ", @@ -272,6 +274,7 @@ macro_rules! check_no_attributes { macro_rules! check_no_unknown_attributes { ($elem:ident, $name:tt, [$($attr:tt),*]) => ( + #[cfg(not(feature = "compat"))] for (_attr, _) in $elem.attrs() { $( if _attr == $attr { diff --git a/src/message_correct.rs b/src/message_correct.rs index 9e7a08f726c1e789b2794949a3888ec4d8524eb8..e0d22d6257322d3a31bccbb32d20ed6cfb44ce0c 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -45,6 +45,7 @@ mod tests { Replace::try_from(elem).unwrap(); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid_attribute() { let elem: Element = "" diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 32df468d7e46f8548656f6a9503daef514dc610f..40528383da094ad9438d492d6ec92f95cf291540 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -142,6 +142,7 @@ mod tests { assert_eq!(elem, elem2); } + #[cfg(not(feature = "compat"))] #[test] fn test_muc_invalid_attribute() { let elem: Element = "" diff --git a/src/muc/user.rs b/src/muc/user.rs index 2c1a6926a98b9099f207268bcb683ec9127070a1..a104e319f9104715a22655c4901631cdad49d1a9 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -303,6 +303,7 @@ mod tests { assert!(elem.compare_to(&elem2)); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid_attribute() { let elem: Element = " @@ -343,6 +344,7 @@ mod tests { assert_eq!(message, "Required attribute 'code' missing."); } + #[cfg(not(feature = "compat"))] #[test] fn test_status_invalid_child() { let elem: Element = " @@ -513,6 +515,7 @@ mod tests { assert_eq!(reason.0, "Reason".to_owned()); } + #[cfg(not(feature = "compat"))] #[test] fn test_reason_invalid_attribute() { let elem: Element = " @@ -528,6 +531,7 @@ mod tests { assert_eq!(message, "Unknown attribute in reason element.".to_owned()); } + #[cfg(not(feature = "compat"))] #[test] fn test_reason_invalid() { let elem: Element = " @@ -545,6 +549,7 @@ mod tests { assert_eq!(message, "Unknown child in reason element.".to_owned()); } + #[cfg(not(feature = "compat"))] #[test] fn test_item_invalid_attr() { let elem: Element = " diff --git a/src/nick.rs b/src/nick.rs index edf8dcacce17b23e54ac39fcbea32b87aad8d5a2..67a45ff9c5342ea6c2216ec4a4f99865e0aae859 100644 --- a/src/nick.rs +++ b/src/nick.rs @@ -14,6 +14,7 @@ generate_elem_id!( #[cfg(test)] mod tests { use super::*; + #[cfg(not(feature = "compat"))] use crate::error::Error; use minidom::Element; use try_from::TryFrom; @@ -48,6 +49,7 @@ mod tests { assert_eq!(elem1, elem2); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid() { let elem: Element = "" @@ -61,6 +63,7 @@ mod tests { assert_eq!(message, "Unknown child in nick element."); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid_attribute() { let elem: Element = "" diff --git a/src/ping.rs b/src/ping.rs index 4cb25f649c453db84f9879f5871c743df187898f..6f48dd5592941854555995db680452f7a901ccaf 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -20,6 +20,7 @@ impl IqGetPayload for Ping {} #[cfg(test)] mod tests { use super::*; + #[cfg(not(feature = "compat"))] use crate::error::Error; use minidom::Element; use try_from::TryFrom; @@ -42,6 +43,7 @@ mod tests { assert_eq!(elem1, elem2); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid() { let elem: Element = "" @@ -55,6 +57,7 @@ mod tests { assert_eq!(message, "Unknown child in ping element."); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid_attribute() { let elem: Element = "".parse().unwrap(); diff --git a/src/presence.rs b/src/presence.rs index 1f9846c2b7dc493b5f37d6c135327ebfcd125a68..20d63e879e48ac4af6f1a147b30010a785d215cd 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -570,6 +570,7 @@ mod tests { assert!(payload.is("test", "invalid")); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid_status_child() { #[cfg(not(feature = "component"))] @@ -589,6 +590,7 @@ mod tests { assert_eq!(message, "Unknown child in status element."); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid_attribute() { #[cfg(not(feature = "component"))] diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index fac8e2fe26b822297abf1438cfe6ae4332bd065d..0cad1cc0379fb9b7ad357821ad8f5a899057734b 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -420,6 +420,7 @@ mod tests { assert_eq!(message, "Unknown child in event element."); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid_attribute() { let elem: Element = "" diff --git a/src/roster.rs b/src/roster.rs index 40cc7a25dc7f8a7f935306df4507e3cff8d4862e..c0d869d1d0079196f04dfa651fbdce888178d721 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -245,6 +245,7 @@ mod tests { assert_eq!(roster.items[0].subscription, Subscription::Remove); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid() { let elem: Element = "" diff --git a/src/sasl.rs b/src/sasl.rs index 86e10760b996632ded068544d3e2c05ded68c544..21e585722aaf113c0c1a391ae2c79d28aed00ed8 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -289,6 +289,7 @@ mod tests { ); } + #[cfg(feature = "compat")] #[test] fn failure_with_non_prefixed_text_lang() { let elem: Element = " @@ -299,9 +300,5 @@ mod tests { .unwrap(); let failure = Failure::try_from(elem).unwrap(); assert_eq!(failure.defined_condition, DefinedCondition::NotAuthorized); - assert_eq!( - failure.texts["en"], - String::from("Invalid username or password") - ); } } From 047649dbc839b4253df17bc01d357a7f731b48fe Mon Sep 17 00:00:00 2001 From: Astro Date: Sun, 30 Dec 2018 00:49:57 +0100 Subject: [PATCH 550/698] .gitlab-ci.yml: add a compat-rust-latest stage --- .gitlab-ci.yml | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0b4e77f06d233074e4efced741958cc6a524498c..ba1742248ffe71a3ce5d45053f47159030da4b50 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,16 +1,29 @@ stages: - build +variables: + FEATURES: "" + rust-latest: stage: build image: rust:latest script: - - cargo build --verbose - - cargo test --verbose + - cargo build --verbose --no-default-features --features=$FEATURES + - cargo test --verbose --no-default-features --features=$FEATURES rust-nightly: stage: build image: rustlang/rust:nightly script: - - cargo build --verbose - - cargo test --verbose + - cargo build --verbose --no-default-features --features=$FEATURES + - cargo test --verbose --no-default-features --features=$FEATURES + +"rust-latest with features=compat": + extends: rust-latest + variables: + FEATURES: "compat" + +"rust-nightly with features=compat": + extends: rust-nightly + variables: + FEATURES: "compat" From 5ebe92c2605d53234afcb1abaa07f391270630f6 Mon Sep 17 00:00:00 2001 From: O01eg Date: Mon, 7 Jan 2019 16:49:33 +0300 Subject: [PATCH 551/698] Add ask attribute from RFC3921. Fixes #1 --- src/roster.rs | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/roster.rs b/src/roster.rs index 40cc7a25dc7f8a7f935306df4507e3cff8d4862e..a64a47932e14ee94020163bfb06f1c473ccf89de 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -36,6 +36,14 @@ generate_attribute!( }, Default = None ); +generate_attribute!( + /// The sub-state of subscription with a contact. + Ask, "ask", { + /// Pending sub-state of the 'none' subscription state + Subscribe => "subscribe", + } +); + generate_element!( /// Contact from the user’s contact list. #[derive(PartialEq)] @@ -48,7 +56,10 @@ generate_element!( name: Option = "name" => optional_empty, /// Subscription status of this contact. - subscription: Subscription = "subscription" => default + subscription: Subscription = "subscription" => default, + + /// + ask: Option = "ask" => optional_empty ], children: [ @@ -144,23 +155,42 @@ mod tests { + + MyBuddies + "# .parse() .unwrap(); let roster = Roster::try_from(elem).unwrap(); assert_eq!(roster.ver, Some(String::from("ver11"))); - assert_eq!(roster.items.len(), 3); + assert_eq!(roster.items.len(), 4); assert_eq!( roster.items[0].jid, Jid::from_str("romeo@example.net").unwrap() ); assert_eq!(roster.items[0].name, Some(String::from("Romeo"))); assert_eq!(roster.items[0].subscription, Subscription::Both); + assert_eq!(roster.items[0].ask, None); assert_eq!( roster.items[0].groups, vec!(Group::from_str("Friends").unwrap()) ); + + assert_eq!( + roster.items[3].jid, + Jid::from_str("contact@example.org").unwrap() + ); + assert_eq!(roster.items[3].name, Some(String::from("MyContact"))); + assert_eq!(roster.items[3].subscription, Subscription::None); + assert_eq!(roster.items[3].ask, Some(Ask::Subscribe)); + assert_eq!( + roster.items[3].groups, + vec!(Group::from_str("MyBuddies").unwrap()) + ); } #[test] From ee511e653aa17a646391a991f7aef0af281080ba Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 Jan 2019 20:41:12 +0100 Subject: [PATCH 552/698] sasl: Add back the assert, with the correct @xml:lang this time. --- src/sasl.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sasl.rs b/src/sasl.rs index 21e585722aaf113c0c1a391ae2c79d28aed00ed8..6f922901b2475da7cb72ecdcb6c55e47ddb30f17 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -300,5 +300,9 @@ mod tests { .unwrap(); let failure = Failure::try_from(elem).unwrap(); assert_eq!(failure.defined_condition, DefinedCondition::NotAuthorized); + assert_eq!( + failure.texts[""], + String::from("Invalid username or password") + ); } } From 8b15728bb2c9b686506affb0442697660625ee67 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 Jan 2019 20:41:40 +0100 Subject: [PATCH 553/698] blocking, jingle_ft: Split #[cfg] sections into their own tests. --- src/blocking.rs | 23 +++++++++-------- src/jingle_ft.rs | 65 +++++++++++++++++++++++++----------------------- 2 files changed, 46 insertions(+), 42 deletions(-) diff --git a/src/blocking.rs b/src/blocking.rs index b2fe70df28c9bf49cbd6448a79504baf8f08f0d5..e12e7e49ae6757a078a2432892b3388f2551c60a 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -155,17 +155,6 @@ mod tests { }, ]; - #[cfg(not(feature = "compat"))] - { - let request_elem = elem.clone(); - let error = BlocklistRequest::try_from(request_elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown child in blocklist element."); - } - let result_elem = elem.clone(); let result = BlocklistResult::try_from(result_elem).unwrap(); assert_eq!(result.items, two_items); @@ -221,4 +210,16 @@ mod tests { }; assert_eq!(message, "Unknown attribute in unblock element."); } + + #[cfg(not(feature = "compat"))] + #[test] + fn test_non_empty_blocklist_request() { + let elem: Element = "".parse().unwrap(); + let error = BlocklistRequest::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in blocklist element."); + } } diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 359cd8b07c17f86094696d201573b5c9169bdc2f..d9a73b29b5fdd281c2091bb0f2bb5fa063003166 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -510,17 +510,6 @@ mod tests { }; assert_eq!(message, "Unknown child in received element."); - #[cfg(not(feature = "compat"))] - { - let elem: Element = "".parse().unwrap(); - let error = Received::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown attribute in received element."); - } - let elem: Element = "" .parse() @@ -541,6 +530,18 @@ mod tests { assert_eq!(message, "Unknown value for 'creator' attribute."); } + #[cfg(not(feature = "compat"))] + #[test] + fn test_invalid_received() { + let elem: Element = "".parse().unwrap(); + let error = Received::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in received element."); + } + #[test] fn test_checksum() { let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); @@ -578,17 +579,6 @@ mod tests { }; assert_eq!(message, "This is not a file element."); - #[cfg(not(feature = "compat"))] - { - let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); - let error = Checksum::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown attribute in checksum element."); - } - let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); let error = Checksum::try_from(elem).unwrap_err(); let message = match error { @@ -606,6 +596,18 @@ mod tests { assert_eq!(message, "Unknown value for 'creator' attribute."); } + #[cfg(not(feature = "compat"))] + #[test] + fn test_invalid_checksum() { + let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); + let error = Checksum::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in checksum element."); + } + #[test] fn test_range() { let elem: Element = "" @@ -633,18 +635,19 @@ mod tests { assert_eq!(range2.offset, 2048); assert_eq!(range2.length, Some(1024)); assert_eq!(range2.hashes, hashes); + } + #[cfg(not(feature = "compat"))] + #[test] + fn test_invalid_range() { let elem: Element = "" .parse() .unwrap(); - #[cfg(not(feature = "compat"))] - { - let error = Range::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown attribute in range element."); - } + let error = Range::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in range element."); } } From c2b7e193788ca42f1ce07ab4a1bb404a799597ea Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 Jan 2019 22:00:46 +0100 Subject: [PATCH 554/698] Rename "compat" into "disable-validation", to insist on it breaking some guarantees. --- .gitlab-ci.yml | 8 ++++---- Cargo.toml | 4 ++-- src/attention.rs | 6 +++--- src/bind.rs | 2 +- src/blocking.rs | 4 ++-- src/caps.rs | 2 +- src/chatstates.rs | 4 ++-- src/jingle_ft.rs | 6 +++--- src/macros.rs | 6 +++--- src/message_correct.rs | 2 +- src/muc/muc.rs | 2 +- src/muc/user.rs | 10 +++++----- src/nick.rs | 6 +++--- src/ping.rs | 6 +++--- src/presence.rs | 4 ++-- src/pubsub/event.rs | 2 +- src/roster.rs | 2 +- src/sasl.rs | 2 +- 18 files changed, 39 insertions(+), 39 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ba1742248ffe71a3ce5d45053f47159030da4b50..3f233d21b15fa678e2dae3df182edba04d670be8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -18,12 +18,12 @@ rust-nightly: - cargo build --verbose --no-default-features --features=$FEATURES - cargo test --verbose --no-default-features --features=$FEATURES -"rust-latest with features=compat": +"rust-latest with features=disable-validation": extends: rust-latest variables: - FEATURES: "compat" + FEATURES: "disable-validation" -"rust-nightly with features=compat": +"rust-nightly with features=disable-validation": extends: rust-nightly variables: - FEATURES: "compat" + FEATURES: "disable-validation" diff --git a/Cargo.toml b/Cargo.toml index 65935bab1f028d897229ee24541225749156fd59..0e3b9a36dbde483ea64a3f00074fcfd2d1a29199 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,5 +28,5 @@ try_from = "0.3.2" [features] # Build xmpp-parsers to make components instead of clients. component = [] -# Compatibility mode -compat = [] +# Disable validation of unknown attributes. +disable-validation = [] diff --git a/src/attention.rs b/src/attention.rs index c786f3137fce92ed8eb178329fda4ee4481a1da5..bd4c7a15e431cc8f5548cd837b96f97abbc59968 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -18,7 +18,7 @@ impl MessagePayload for Attention {} #[cfg(test)] mod tests { use super::*; - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] use crate::error::Error; use minidom::Element; use try_from::TryFrom; @@ -34,7 +34,7 @@ mod tests { Attention::try_from(elem).unwrap(); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_child() { let elem: Element = "" @@ -48,7 +48,7 @@ mod tests { assert_eq!(message, "Unknown child in attention element."); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_attribute() { let elem: Element = "" diff --git a/src/bind.rs b/src/bind.rs index bd09d4f3b36aa326f51790cc3f0ad0f905f6a2a0..9d3a894f8950c2aee20cda960de6b17acc6a4bac 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -112,7 +112,7 @@ mod tests { assert_eq!(bind, Bind::None); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_resource() { let elem: Element = "resource" diff --git a/src/blocking.rs b/src/blocking.rs index e12e7e49ae6757a078a2432892b3388f2551c60a..53417f7f749ccebc425fceeee2da39d63b03f337 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -168,7 +168,7 @@ mod tests { assert_eq!(unblock.items, two_items); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid() { let elem: Element = "" @@ -211,7 +211,7 @@ mod tests { assert_eq!(message, "Unknown attribute in unblock element."); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_non_empty_blocklist_request() { let elem: Element = "".parse().unwrap(); diff --git a/src/caps.rs b/src/caps.rs index 3befdc9bc2541de8d5d401c362f4733124eecb03..74042be31a368be7a0d5b756a7393ec0ad77a09c 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -233,7 +233,7 @@ mod tests { ); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_child() { let elem: Element = "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=".parse().unwrap(); diff --git a/src/chatstates.rs b/src/chatstates.rs index 9a972aa79a4e97a820c62ac1f76275936240b214..4eb173f11b796d1ef554f7f766638de486efef6f 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -63,7 +63,7 @@ mod tests { assert_eq!(message, "This is not a chatstate element."); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_child() { let elem: Element = "" @@ -77,7 +77,7 @@ mod tests { assert_eq!(message, "Unknown child in chatstate element."); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_attribute() { let elem: Element = "" diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index d9a73b29b5fdd281c2091bb0f2bb5fa063003166..32dd41f6aeb7233bc5a236796ea7bfa6fa80418f 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -530,7 +530,7 @@ mod tests { assert_eq!(message, "Unknown value for 'creator' attribute."); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_received() { let elem: Element = "".parse().unwrap(); @@ -596,7 +596,7 @@ mod tests { assert_eq!(message, "Unknown value for 'creator' attribute."); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_checksum() { let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); @@ -637,7 +637,7 @@ mod tests { assert_eq!(range2.hashes, hashes); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_range() { let elem: Element = "" diff --git a/src/macros.rs b/src/macros.rs index daccec5c676346e14468723f34b9151e044972a2..aa89a45cbe2b6c429ef7c6300c75cc8fcc49621c 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -248,7 +248,7 @@ macro_rules! check_ns_only { macro_rules! check_no_children { ($elem:ident, $name:tt) => { - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] for _ in $elem.children() { return Err(crate::error::Error::ParseError(concat!( "Unknown child in ", @@ -261,7 +261,7 @@ macro_rules! check_no_children { macro_rules! check_no_attributes { ($elem:ident, $name:tt) => { - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] for _ in $elem.attrs() { return Err(crate::error::Error::ParseError(concat!( "Unknown attribute in ", @@ -274,7 +274,7 @@ macro_rules! check_no_attributes { macro_rules! check_no_unknown_attributes { ($elem:ident, $name:tt, [$($attr:tt),*]) => ( - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] for (_attr, _) in $elem.attrs() { $( if _attr == $attr { diff --git a/src/message_correct.rs b/src/message_correct.rs index e0d22d6257322d3a31bccbb32d20ed6cfb44ce0c..3bba10e7a187abf3cb1fcdb2e116a9b014fda2f8 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -45,7 +45,7 @@ mod tests { Replace::try_from(elem).unwrap(); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_attribute() { let elem: Element = "" diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 40528383da094ad9438d492d6ec92f95cf291540..fc596fa9c95adfe4f759cefead11bbabca89974e 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -142,7 +142,7 @@ mod tests { assert_eq!(elem, elem2); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_muc_invalid_attribute() { let elem: Element = "" diff --git a/src/muc/user.rs b/src/muc/user.rs index a104e319f9104715a22655c4901631cdad49d1a9..56c111e8ef18b7cd8a6be65a3544bdff4bb17efb 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -303,7 +303,7 @@ mod tests { assert!(elem.compare_to(&elem2)); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_attribute() { let elem: Element = " @@ -344,7 +344,7 @@ mod tests { assert_eq!(message, "Required attribute 'code' missing."); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_status_invalid_child() { let elem: Element = " @@ -515,7 +515,7 @@ mod tests { assert_eq!(reason.0, "Reason".to_owned()); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_reason_invalid_attribute() { let elem: Element = " @@ -531,7 +531,7 @@ mod tests { assert_eq!(message, "Unknown attribute in reason element.".to_owned()); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_reason_invalid() { let elem: Element = " @@ -549,7 +549,7 @@ mod tests { assert_eq!(message, "Unknown child in reason element.".to_owned()); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_item_invalid_attr() { let elem: Element = " diff --git a/src/nick.rs b/src/nick.rs index 67a45ff9c5342ea6c2216ec4a4f99865e0aae859..03f544aaa9455e9d9b8894972098676b9f898df2 100644 --- a/src/nick.rs +++ b/src/nick.rs @@ -14,7 +14,7 @@ generate_elem_id!( #[cfg(test)] mod tests { use super::*; - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] use crate::error::Error; use minidom::Element; use try_from::TryFrom; @@ -49,7 +49,7 @@ mod tests { assert_eq!(elem1, elem2); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid() { let elem: Element = "" @@ -63,7 +63,7 @@ mod tests { assert_eq!(message, "Unknown child in nick element."); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_attribute() { let elem: Element = "" diff --git a/src/ping.rs b/src/ping.rs index 6f48dd5592941854555995db680452f7a901ccaf..018531205c3d16f9b6b681251fe12091b1e091ab 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -20,7 +20,7 @@ impl IqGetPayload for Ping {} #[cfg(test)] mod tests { use super::*; - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] use crate::error::Error; use minidom::Element; use try_from::TryFrom; @@ -43,7 +43,7 @@ mod tests { assert_eq!(elem1, elem2); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid() { let elem: Element = "" @@ -57,7 +57,7 @@ mod tests { assert_eq!(message, "Unknown child in ping element."); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_attribute() { let elem: Element = "".parse().unwrap(); diff --git a/src/presence.rs b/src/presence.rs index 20d63e879e48ac4af6f1a147b30010a785d215cd..7e3e8aa8f78a6afb68bfd7430467580f96a7e527 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -570,7 +570,7 @@ mod tests { assert!(payload.is("test", "invalid")); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_status_child() { #[cfg(not(feature = "component"))] @@ -590,7 +590,7 @@ mod tests { assert_eq!(message, "Unknown child in status element."); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_attribute() { #[cfg(not(feature = "component"))] diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 0cad1cc0379fb9b7ad357821ad8f5a899057734b..0cef2c397c453bf00b9bddb5e08d43158a2a9e60 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -420,7 +420,7 @@ mod tests { assert_eq!(message, "Unknown child in event element."); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_attribute() { let elem: Element = "" diff --git a/src/roster.rs b/src/roster.rs index c0d869d1d0079196f04dfa651fbdce888178d721..c13b0f59ad1eed7cc791c8ac512a56b721056ebd 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -245,7 +245,7 @@ mod tests { assert_eq!(roster.items[0].subscription, Subscription::Remove); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid() { let elem: Element = "" diff --git a/src/sasl.rs b/src/sasl.rs index 6f922901b2475da7cb72ecdcb6c55e47ddb30f17..45a56653fdc571d7df1b48165155732d5ba03e52 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -289,7 +289,7 @@ mod tests { ); } - #[cfg(feature = "compat")] + #[cfg(feature = "disable-validation")] #[test] fn failure_with_non_prefixed_text_lang() { let elem: Element = " From 04c5bcac1d45fdf990f5ee18d84ecfcab4cc43c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Thu, 20 Dec 2018 16:11:16 +0000 Subject: [PATCH 555/698] ci: split build and test stages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- .gitlab-ci.yml | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0b4e77f06d233074e4efced741958cc6a524498c..cf5215c014f3dea2a872e706ee033404b8f4a39c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,16 +1,27 @@ stages: - build + - test -rust-latest: +rust-latest-build: stage: build image: rust:latest script: - cargo build --verbose - - cargo test --verbose -rust-nightly: +rust-nightly-build: stage: build image: rustlang/rust:nightly script: - cargo build --verbose + +rust-latest-test: + stage: test + image: rust:latest + script: + - cargo test --verbose + +rust-nightly-test: + stage: test + image: rustlang/rust:nightly + script: - cargo test --verbose From 635e8633a85075660a7e00750b715ffccb33c2f6 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 13 Jan 2019 11:56:40 +0100 Subject: [PATCH 556/698] sasl: Document the reason for the unprefixed @lang test. --- src/sasl.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sasl.rs b/src/sasl.rs index 45a56653fdc571d7df1b48165155732d5ba03e52..db9b17e6fd3a90f071898c15cad65054fdd0dde3 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -289,9 +289,11 @@ mod tests { ); } + /// Some servers apparently use a non-namespaced 'lang' attribute, which is invalid as not part + /// of the schema. This tests whether we can parse it when disabling validation. #[cfg(feature = "disable-validation")] #[test] - fn failure_with_non_prefixed_text_lang() { + fn invalid_failure_with_non_prefixed_text_lang() { let elem: Element = " Invalid username or password From 62539cbae314052c44b5f1a9426d92560e61e384 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 Jan 2019 22:50:22 +0100 Subject: [PATCH 557/698] macros: Add a singleton attribute. --- src/macros.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/macros.rs b/src/macros.rs index aa89a45cbe2b6c429ef7c6300c75cc8fcc49621c..d0dfbc0603df5dfd2e67abff8e7a10ef32ef1b00 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -107,6 +107,38 @@ macro_rules! generate_attribute { } } ); + ($(#[$meta:meta])* $elem:ident, $name:tt, ($(#[$meta_symbol:meta])* $symbol:ident => $value:tt)) => ( + $(#[$meta])* + #[derive(Debug, Clone, PartialEq)] + pub enum $elem { + $(#[$meta_symbol])* + $symbol, + /// Value when absent. + None, + } + impl ::std::str::FromStr for $elem { + type Err = crate::error::Error; + fn from_str(s: &str) -> Result { + Ok(match s { + $value => $elem::$symbol, + _ => return Err(crate::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + }) + } + } + impl ::minidom::IntoAttributeValue for $elem { + fn into_attribute_value(self) -> Option { + match self { + $elem::$symbol => Some(String::from($value)), + $elem::None => None + } + } + } + impl ::std::default::Default for $elem { + fn default() -> $elem { + $elem::None + } + } + ); ($(#[$meta:meta])* $elem:ident, $name:tt, bool) => ( $(#[$meta])* #[derive(Debug, Clone, PartialEq)] From b6796d54e6e4eca02d1ceb53da03aa1bd0c6a945 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 Jan 2019 22:51:02 +0100 Subject: [PATCH 558/698] roster: Simplify the @ask parsing using a singleton attribute. --- src/roster.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/roster.rs b/src/roster.rs index 1e049837cb9e7782e1a0806b91149f2b81e69e90..e86297275f14671d1681c30e05dafdd6b9c5ea1a 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -38,10 +38,10 @@ generate_attribute!( generate_attribute!( /// The sub-state of subscription with a contact. - Ask, "ask", { - /// Pending sub-state of the 'none' subscription state - Subscribe => "subscribe", - } + Ask, "ask", ( + /// Pending sub-state of the 'none' subscription state. + Subscribe => "subscribe" + ) ); generate_element!( @@ -58,8 +58,8 @@ generate_element!( /// Subscription status of this contact. subscription: Subscription = "subscription" => default, - /// - ask: Option = "ask" => optional_empty + /// Indicates “Pending Out” sub-states for this contact. + ask: Ask = "ask" => default, ], children: [ @@ -174,7 +174,7 @@ mod tests { ); assert_eq!(roster.items[0].name, Some(String::from("Romeo"))); assert_eq!(roster.items[0].subscription, Subscription::Both); - assert_eq!(roster.items[0].ask, None); + assert_eq!(roster.items[0].ask, Ask::None); assert_eq!( roster.items[0].groups, vec!(Group::from_str("Friends").unwrap()) @@ -186,7 +186,7 @@ mod tests { ); assert_eq!(roster.items[3].name, Some(String::from("MyContact"))); assert_eq!(roster.items[3].subscription, Subscription::None); - assert_eq!(roster.items[3].ask, Some(Ask::Subscribe)); + assert_eq!(roster.items[3].ask, Ask::Subscribe); assert_eq!( roster.items[3].groups, vec!(Group::from_str("MyBuddies").unwrap()) From 2a7cf487a49bdd8865fe674e317c9c437d4531e2 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 13 Jan 2019 12:06:37 +0100 Subject: [PATCH 559/698] roster: Also test for the size of the new Ask attribute. --- src/roster.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/roster.rs b/src/roster.rs index e86297275f14671d1681c30e05dafdd6b9c5ea1a..b40d2d5dd3741e0d77e1b8d8216ccf813fe6bc64 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -103,6 +103,7 @@ mod tests { fn test_size() { assert_size!(Group, 12); assert_size!(Subscription, 1); + assert_size!(Ask, 1); assert_size!(Item, 64); assert_size!(Roster, 24); } @@ -112,6 +113,7 @@ mod tests { fn test_size() { assert_size!(Group, 24); assert_size!(Subscription, 1); + assert_size!(Ask, 1); assert_size!(Item, 128); assert_size!(Roster, 48); } From 409a1dafa9c8165b533cc66d4a2b0d1bd5a6bb1f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 13 Jan 2019 12:39:51 +0100 Subject: [PATCH 560/698] Move Error, helpers and macros into a util module. --- src/attention.rs | 2 +- src/bind.rs | 2 +- src/blocking.rs | 2 +- src/bookmarks.rs | 2 +- src/caps.rs | 2 +- src/chatstates.rs | 2 +- src/component.rs | 2 +- src/data_forms.rs | 2 +- src/date.rs | 2 +- src/delay.rs | 4 +- src/disco.rs | 4 +- src/ecaps2.rs | 2 +- src/eme.rs | 2 +- src/forwarding.rs | 2 +- src/hashes.rs | 4 +- src/ibb.rs | 4 +- src/ibr.rs | 4 +- src/idle.rs | 2 +- src/iq.rs | 4 +- src/jingle.rs | 2 +- src/jingle_ft.rs | 2 +- src/jingle_ibb.rs | 2 +- src/jingle_message.rs | 2 +- src/jingle_s5b.rs | 4 +- src/lib.rs | 11 +---- src/mam.rs | 2 +- src/media_element.rs | 4 +- src/message.rs | 4 +- src/message_correct.rs | 2 +- src/muc/muc.rs | 4 +- src/muc/user.rs | 4 +- src/nick.rs | 2 +- src/ping.rs | 2 +- src/presence.rs | 4 +- src/pubsub/event.rs | 4 +- src/pubsub/pubsub.rs | 4 +- src/roster.rs | 4 +- src/rsm.rs | 4 +- src/sasl.rs | 4 +- src/stanza_error.rs | 2 +- src/stanza_id.rs | 2 +- src/{ => util}/compare_elements.rs | 0 src/{ => util}/error.rs | 0 src/{ => util}/helpers.rs | 2 +- src/{ => util}/macros.rs | 76 +++++++++++++++--------------- src/version.rs | 2 +- 46 files changed, 98 insertions(+), 107 deletions(-) rename src/{ => util}/compare_elements.rs (100%) rename src/{ => util}/error.rs (100%) rename src/{ => util}/helpers.rs (97%) rename src/{ => util}/macros.rs (87%) diff --git a/src/attention.rs b/src/attention.rs index bd4c7a15e431cc8f5548cd837b96f97abbc59968..1910c8c13bf75657512b3db9b07c6e549bed0f89 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -19,7 +19,7 @@ impl MessagePayload for Attention {} mod tests { use super::*; #[cfg(not(feature = "disable-validation"))] - use crate::error::Error; + use crate::util::error::Error; use minidom::Element; use try_from::TryFrom; diff --git a/src/bind.rs b/src/bind.rs index 9d3a894f8950c2aee20cda960de6b17acc6a4bac..8ded3e6ac10c8bb01e61ba9bb0d3c56b1f1d4d24 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; +use crate::util::error::Error; use crate::iq::{IqResultPayload, IqSetPayload}; use crate::ns; use jid::Jid; diff --git a/src/blocking.rs b/src/blocking.rs index 53417f7f749ccebc425fceeee2da39d63b03f337..2b021185995c8427f28bcf1c921a780a60f57076 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; +use crate::util::error::Error; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; use jid::Jid; diff --git a/src/bookmarks.rs b/src/bookmarks.rs index 6048b2f074023e7df09ef2103777d42fcdd6e0c9..12f95707fc8f7c7bfbcc61d938be68aae9aa7b2e 100644 --- a/src/bookmarks.rs +++ b/src/bookmarks.rs @@ -72,7 +72,7 @@ impl Storage { #[cfg(test)] mod tests { use super::*; - use crate::compare_elements::NamespaceAwareCompare; + use crate::util::compare_elements::NamespaceAwareCompare; use minidom::Element; use try_from::TryFrom; diff --git a/src/caps.rs b/src/caps.rs index 74042be31a368be7a0d5b756a7393ec0ad77a09c..73c79378ffa1a613c406977599e150303db59f35 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -6,7 +6,7 @@ use crate::data_forms::DataForm; use crate::disco::{DiscoInfoQuery, DiscoInfoResult, Feature, Identity}; -use crate::error::Error; +use crate::util::error::Error; use crate::hashes::{Algo, Hash}; use crate::ns; use crate::presence::PresencePayload; diff --git a/src/chatstates.rs b/src/chatstates.rs index 4eb173f11b796d1ef554f7f766638de486efef6f..2bf2d1b1fe0cbd91cc4803101bb941d03add554a 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -32,7 +32,7 @@ impl MessagePayload for ChatState {} #[cfg(test)] mod tests { use super::*; - use crate::error::Error; + use crate::util::error::Error; use crate::ns; use minidom::Element; use try_from::TryFrom; diff --git a/src/component.rs b/src/component.rs index cb42fd7a319e9b2223e93b8ff6fb2b104ced3af4..722495b8a02b6aea322c16a990070586e299ea4b 100644 --- a/src/component.rs +++ b/src/component.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::helpers::PlainText; +use crate::util::helpers::PlainText; use digest::Digest; use sha1::Sha1; diff --git a/src/data_forms.rs b/src/data_forms.rs index 9d7608dfc3cceb0fa504a77375568f9cda421542..30dbd29aa478ac2c9dd918bb3de0e7c08f4196c7 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; +use crate::util::error::Error; use crate::media_element::MediaElement; use crate::ns; use minidom::Element; diff --git a/src/date.rs b/src/date.rs index a6a99e0743ac9f78753091555fb48026eba97004..1d3389b0651623ffc7ec802a64e9de6106032b1a 100644 --- a/src/date.rs +++ b/src/date.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; +use crate::util::error::Error; use chrono::{DateTime as ChronoDateTime, FixedOffset}; use minidom::{ElementEmitter, IntoAttributeValue, IntoElements}; use std::str::FromStr; diff --git a/src/delay.rs b/src/delay.rs index 4defc94ace00bfb8f219af40d953e62ef3a12a8d..a02b44b1a4338929806b75529c2af166cb945191 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -5,7 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::date::DateTime; -use crate::helpers::PlainText; +use crate::util::helpers::PlainText; use crate::message::MessagePayload; use crate::presence::PresencePayload; use jid::Jid; @@ -32,7 +32,7 @@ impl PresencePayload for Delay {} #[cfg(test)] mod tests { use super::*; - use crate::error::Error; + use crate::util::error::Error; use minidom::Element; use std::str::FromStr; use try_from::TryFrom; diff --git a/src/disco.rs b/src/disco.rs index 79d89b26d0dfe345bc691ab7e89df4e60b1aeb7c..d61cf361dc3b3258bc238aa11f391fe22dfb282c 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -5,7 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::data_forms::{DataForm, DataFormType}; -use crate::error::Error; +use crate::util::error::Error; use crate::iq::{IqGetPayload, IqResultPayload}; use crate::ns; use jid::Jid; @@ -236,7 +236,7 @@ impl IqResultPayload for DiscoItemsResult {} #[cfg(test)] mod tests { use super::*; - use crate::compare_elements::NamespaceAwareCompare; + use crate::util::compare_elements::NamespaceAwareCompare; use std::str::FromStr; #[cfg(target_pointer_width = "32")] diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 2fc011b806408af9f4ef5300ebc67f6ab338970b..846fa6da0d75e1720e4bdff06a9a3e33cd3ffad6 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -156,7 +156,7 @@ pub fn query_ecaps2(hash: Hash) -> DiscoInfoQuery { #[cfg(test)] mod tests { use super::*; - use crate::error::Error; + use crate::util::error::Error; use minidom::Element; use try_from::TryFrom; diff --git a/src/eme.rs b/src/eme.rs index 4b8ffbe6bcb2a6ee0e990b9a8b73ad9094e05e2a..cd24e43d03b7bf95799d75b86b086d3b280d8ab1 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -24,7 +24,7 @@ impl MessagePayload for ExplicitMessageEncryption {} #[cfg(test)] mod tests { use super::*; - use crate::error::Error; + use crate::util::error::Error; use minidom::Element; use try_from::TryFrom; diff --git a/src/forwarding.rs b/src/forwarding.rs index e36ebf8eed09979f79e984e5a2d0ffe937e01f6b..5dfa4aea0413a754808f3ddbbf4f1601ac85c76e 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -26,7 +26,7 @@ generate_element!( #[cfg(test)] mod tests { use super::*; - use crate::error::Error; + use crate::util::error::Error; use minidom::Element; use try_from::TryFrom; diff --git a/src/hashes.rs b/src/hashes.rs index a7773ee1cda05bc2b509270586cb361cbd59cd38..041b81fc4795415b230bda7d2393a40a11e16cab 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -4,8 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; -use crate::helpers::Base64; +use crate::util::error::Error; +use crate::util::helpers::Base64; use base64; use minidom::IntoAttributeValue; use std::str::FromStr; diff --git a/src/ibb.rs b/src/ibb.rs index f2aff83efe950cbd8475382dc054464d09393330..960761187becc66ba7d9e1f7dd2ec02922d635ba 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::helpers::Base64; +use crate::util::helpers::Base64; use crate::iq::IqSetPayload; generate_id!( @@ -72,7 +72,7 @@ impl IqSetPayload for Close {} #[cfg(test)] mod tests { use super::*; - use crate::error::Error; + use crate::util::error::Error; use minidom::Element; use std::error::Error as StdError; use try_from::TryFrom; diff --git a/src/ibr.rs b/src/ibr.rs index 39aa229d68dbfabcc353a2fdd7f9c64d1b587758..f19c47c131ba137644addac47d35d03ee884ee63 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -5,7 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::data_forms::DataForm; -use crate::error::Error; +use crate::util::error::Error; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; use minidom::Element; @@ -118,7 +118,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use crate::compare_elements::NamespaceAwareCompare; + use crate::util::compare_elements::NamespaceAwareCompare; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/idle.rs b/src/idle.rs index df2d005d67b6a6361b3e02f51d961ee534f528a5..bbf3f15114546636f20c49de18edec939d15ce8e 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -21,7 +21,7 @@ impl PresencePayload for Idle {} #[cfg(test)] mod tests { use super::*; - use crate::error::Error; + use crate::util::error::Error; use minidom::Element; use std::error::Error as StdError; use std::str::FromStr; diff --git a/src/iq.rs b/src/iq.rs index d4ee8b4197fc5d696017fdabff473adc0e21798b..b155b97c928a7da5072e561d6d6d78888085adac 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -5,7 +5,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; +use crate::util::error::Error; use crate::ns; use crate::stanza_error::StanzaError; use jid::Jid; @@ -218,7 +218,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use crate::compare_elements::NamespaceAwareCompare; + use crate::util::compare_elements::NamespaceAwareCompare; use crate::disco::DiscoInfoQuery; use crate::stanza_error::{DefinedCondition, ErrorType}; diff --git a/src/jingle.rs b/src/jingle.rs index 2a3c09669e2eb1ec0a73901024ee99a2afd12a81..63c78a0f808ec9b678502ca1a19ddcc2e44ba2a8 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; +use crate::util::error::Error; use crate::iq::IqSetPayload; use crate::ns; use jid::Jid; diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 32dd41f6aeb7233bc5a236796ea7bfa6fa80418f..fce18cdf47ff7d8eda6b0eb153b88364ab2249fb 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -5,7 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::date::DateTime; -use crate::error::Error; +use crate::util::error::Error; use crate::hashes::Hash; use crate::jingle::{ContentId, Creator}; use crate::ns; diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 1addc1ee4f9c5d41a87494cab0f3af5e9df17ced..08370e1f7f9dd328abb9dd3c2194547c20422111 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -24,7 +24,7 @@ attributes: [ #[cfg(test)] mod tests { use super::*; - use crate::error::Error; + use crate::util::error::Error; use minidom::Element; use std::error::Error as StdError; use try_from::TryFrom; diff --git a/src/jingle_message.rs b/src/jingle_message.rs index 02c1e9924fb3e3449fce3c132ca26e7ee509aa7d..1a520223bcfadfa680e3526435d2c7db0b40724d 100644 --- a/src/jingle_message.rs +++ b/src/jingle_message.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; +use crate::util::error::Error; use crate::jingle::SessionId; use crate::ns; use minidom::Element; diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index e85390caa2b89b1b62a3cd2dcbbd226feac00db0..58c4a27dd36a705bb5a6bd159459a1700655116f 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; +use crate::util::error::Error; use crate::ns; use jid::Jid; use minidom::Element; @@ -275,7 +275,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use crate::compare_elements::NamespaceAwareCompare; + use crate::util::compare_elements::NamespaceAwareCompare; use std::str::FromStr; #[cfg(target_pointer_width = "32")] diff --git a/src/lib.rs b/src/lib.rs index 36eb788250d34834804e416b93a55a1c839e45fc..ed16e05ecf552375532a5de139310a621ace2f39 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,20 +26,11 @@ pub use minidom::Element; -/// Error type returned by every parser on failure. -pub mod error; /// XML namespace definitions used through XMPP. pub mod ns; -/// Various helpers. -mod helpers; -/// Helper macros to parse and serialise more easily. #[macro_use] -mod macros; - -#[cfg(test)] -/// Namespace-aware comparison for tests -mod compare_elements; +mod util; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod bind; diff --git a/src/mam.rs b/src/mam.rs index 8124be9feb760ef3132e9bf78b5d4bc81f45571f..453b4f606deeafbf321d58867e27564969f7e94f 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -5,7 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::data_forms::DataForm; -use crate::error::Error; +use crate::util::error::Error; use crate::forwarding::Forwarded; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::message::MessagePayload; diff --git a/src/media_element.rs b/src/media_element.rs index f3a623870c06c53da518519365e23857d804e378..639b71cea3401fcf764ffba7a54e36c760328900 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::helpers::TrimmedPlainText; +use crate::util::helpers::TrimmedPlainText; generate_element!( /// Represents an URI used in a media element. @@ -46,7 +46,7 @@ generate_element!( mod tests { use super::*; use crate::data_forms::DataForm; - use crate::error::Error; + use crate::util::error::Error; use minidom::Element; use std::error::Error as StdError; use try_from::TryFrom; diff --git a/src/message.rs b/src/message.rs index 159bcd2d8fe76492cb2a92236e24226ddf837239..fe32300e5d353a33aefda76822c6fbd66f436dfe 100644 --- a/src/message.rs +++ b/src/message.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; +use crate::util::error::Error; use crate::ns; use jid::Jid; use minidom::Element; @@ -254,7 +254,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use crate::compare_elements::NamespaceAwareCompare; + use crate::util::compare_elements::NamespaceAwareCompare; use std::str::FromStr; #[cfg(target_pointer_width = "32")] diff --git a/src/message_correct.rs b/src/message_correct.rs index 3bba10e7a187abf3cb1fcdb2e116a9b014fda2f8..05663c8daa34cb40755378242a03e74b1c37d586 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -21,7 +21,7 @@ impl MessagePayload for Replace {} #[cfg(test)] mod tests { use super::*; - use crate::error::Error; + use crate::util::error::Error; use minidom::Element; use try_from::TryFrom; diff --git a/src/muc/muc.rs b/src/muc/muc.rs index fc596fa9c95adfe4f759cefead11bbabca89974e..9d29cc963c924481d79855c4e7791aebfa4e63b3 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -102,8 +102,8 @@ impl Muc { #[cfg(test)] mod tests { use super::*; - use crate::compare_elements::NamespaceAwareCompare; - use crate::error::Error; + use crate::util::compare_elements::NamespaceAwareCompare; + use crate::util::error::Error; use minidom::Element; use std::str::FromStr; use try_from::TryFrom; diff --git a/src/muc/user.rs b/src/muc/user.rs index 56c111e8ef18b7cd8a6be65a3544bdff4bb17efb..46a893337ea7b8525c35c67850dcc31382a45487 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -5,7 +5,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; +use crate::util::error::Error; use crate::ns; use jid::Jid; use minidom::Element; @@ -238,7 +238,7 @@ generate_element!( #[cfg(test)] mod tests { use super::*; - use crate::compare_elements::NamespaceAwareCompare; + use crate::util::compare_elements::NamespaceAwareCompare; use std::error::Error as StdError; #[test] diff --git a/src/nick.rs b/src/nick.rs index 03f544aaa9455e9d9b8894972098676b9f898df2..1fbeb1026752abc44799299b2b271f1fa3f450f2 100644 --- a/src/nick.rs +++ b/src/nick.rs @@ -15,7 +15,7 @@ generate_elem_id!( mod tests { use super::*; #[cfg(not(feature = "disable-validation"))] - use crate::error::Error; + use crate::util::error::Error; use minidom::Element; use try_from::TryFrom; diff --git a/src/ping.rs b/src/ping.rs index 018531205c3d16f9b6b681251fe12091b1e091ab..62bd0b1a2e6c215f644864d43c277833ca84787a 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -21,7 +21,7 @@ impl IqGetPayload for Ping {} mod tests { use super::*; #[cfg(not(feature = "disable-validation"))] - use crate::error::Error; + use crate::util::error::Error; use minidom::Element; use try_from::TryFrom; diff --git a/src/presence.rs b/src/presence.rs index 7e3e8aa8f78a6afb68bfd7430467580f96a7e527..4a464f9c3ced4dfb767f71f38404ef203479fb09 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -5,7 +5,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; +use crate::util::error::Error; use crate::ns; use jid::Jid; use minidom::{Element, ElementEmitter, IntoAttributeValue, IntoElements}; @@ -354,7 +354,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use crate::compare_elements::NamespaceAwareCompare; + use crate::util::compare_elements::NamespaceAwareCompare; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 0cef2c397c453bf00b9bddb5e08d43158a2a9e60..4e8b9311b167292fb9e11a6b1c00ba22223bac1b 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -6,7 +6,7 @@ use crate::data_forms::DataForm; use crate::date::DateTime; -use crate::error::Error; +use crate::util::error::Error; use crate::ns; use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId}; use jid::Jid; @@ -298,7 +298,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use crate::compare_elements::NamespaceAwareCompare; + use crate::util::compare_elements::NamespaceAwareCompare; use std::str::FromStr; #[test] diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index e406d867a0eb126ea0c4c901e2c48ef53fc883b3..637b050aea1d04716d867789effcec21d862f9ea 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -5,7 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::data_forms::DataForm; -use crate::error::Error; +use crate::util::error::Error; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId}; @@ -540,7 +540,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use crate::compare_elements::NamespaceAwareCompare; + use crate::util::compare_elements::NamespaceAwareCompare; #[test] fn create() { diff --git a/src/roster.rs b/src/roster.rs index b40d2d5dd3741e0d77e1b8d8216ccf813fe6bc64..29086aec2ae7777b00ffebebcf072a4e2587c458 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -92,8 +92,8 @@ impl IqResultPayload for Roster {} #[cfg(test)] mod tests { use super::*; - use crate::compare_elements::NamespaceAwareCompare; - use crate::error::Error; + use crate::util::compare_elements::NamespaceAwareCompare; + use crate::util::error::Error; use minidom::Element; use std::str::FromStr; use try_from::TryFrom; diff --git a/src/rsm.rs b/src/rsm.rs index 0cd048ec854d5c4bb11c16906093356c0498aaff..1dd5f47f71ccfd4c0d93971379a1385b1b6a6aa6 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; +use crate::util::error::Error; use crate::ns; use minidom::Element; use try_from::TryFrom; @@ -180,7 +180,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use crate::compare_elements::NamespaceAwareCompare; + use crate::util::compare_elements::NamespaceAwareCompare; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/sasl.rs b/src/sasl.rs index db9b17e6fd3a90f071898c15cad65054fdd0dde3..fecdbfa6b6ca7017593af6d3df9bd7839daa1e9d 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -4,8 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; -use crate::helpers::Base64; +use crate::util::error::Error; +use crate::util::helpers::Base64; use crate::ns; use minidom::Element; use std::collections::BTreeMap; diff --git a/src/stanza_error.rs b/src/stanza_error.rs index c5e62026848b668e5d269d0fdeb8c4f1e3a7eeef..7a4d421aad5505ce9f02a93c2169a4180ba6bd1b 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; +use crate::util::error::Error; use crate::message::MessagePayload; use crate::ns; use crate::presence::PresencePayload; diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 2bac2de3e1d7be592ebc053bf1a84d30edb6187a..c9cefad724a1c8583bfdc908a56d777068ab915b 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -37,7 +37,7 @@ impl MessagePayload for OriginId {} #[cfg(test)] mod tests { use super::*; - use crate::error::Error; + use crate::util::error::Error; use minidom::Element; use std::str::FromStr; use try_from::TryFrom; diff --git a/src/compare_elements.rs b/src/util/compare_elements.rs similarity index 100% rename from src/compare_elements.rs rename to src/util/compare_elements.rs diff --git a/src/error.rs b/src/util/error.rs similarity index 100% rename from src/error.rs rename to src/util/error.rs diff --git a/src/helpers.rs b/src/util/helpers.rs similarity index 97% rename from src/helpers.rs rename to src/util/helpers.rs index e4d2324475a6eb3d13bb27eab7db787c4638d49e..fad0b671cc4670153339cfc31e2a3e503c375938 100644 --- a/src/helpers.rs +++ b/src/util/helpers.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; +use crate::util::error::Error; use base64; /// Codec for plain text content. diff --git a/src/macros.rs b/src/util/macros.rs similarity index 87% rename from src/macros.rs rename to src/util/macros.rs index d0dfbc0603df5dfd2e67abff8e7a10ef32ef1b00..fd3a0ada1febe68c177c15e1a4e57dc7cf4968db 100644 --- a/src/macros.rs +++ b/src/util/macros.rs @@ -25,7 +25,7 @@ macro_rules! get_attr { match $elem.attr($attr) { Some($value) => $func, None => { - return Err(crate::error::Error::ParseError(concat!( + return Err(crate::util::error::Error::ParseError(concat!( "Required attribute '", $attr, "' missing." @@ -58,11 +58,11 @@ macro_rules! generate_attribute { ),+ } impl ::std::str::FromStr for $elem { - type Err = crate::error::Error; - fn from_str(s: &str) -> Result<$elem, crate::error::Error> { + type Err = crate::util::error::Error; + fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> { Ok(match s { $($b => $elem::$a),+, - _ => return Err(crate::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + _ => return Err(crate::util::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), }) } } @@ -84,11 +84,11 @@ macro_rules! generate_attribute { ),+ } impl ::std::str::FromStr for $elem { - type Err = crate::error::Error; - fn from_str(s: &str) -> Result<$elem, crate::error::Error> { + type Err = crate::util::error::Error; + fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> { Ok(match s { $($b => $elem::$a),+, - _ => return Err(crate::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + _ => return Err(crate::util::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), }) } } @@ -117,11 +117,11 @@ macro_rules! generate_attribute { None, } impl ::std::str::FromStr for $elem { - type Err = crate::error::Error; - fn from_str(s: &str) -> Result { + type Err = crate::util::error::Error; + fn from_str(s: &str) -> Result { Ok(match s { $value => $elem::$symbol, - _ => return Err(crate::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + _ => return Err(crate::util::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), }) } } @@ -149,12 +149,12 @@ macro_rules! generate_attribute { False, } impl ::std::str::FromStr for $elem { - type Err = crate::error::Error; - fn from_str(s: &str) -> Result { + type Err = crate::util::error::Error; + fn from_str(s: &str) -> Result { Ok(match s { "true" | "1" => $elem::True, "false" | "0" => $elem::False, - _ => return Err(crate::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + _ => return Err(crate::util::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), }) } } @@ -188,14 +188,14 @@ macro_rules! generate_element_enum { ),+ } impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = crate::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::error::Error> { + type Err = crate::util::error::Error; + fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); Ok(match elem.name() { $($enum_name => $elem::$enum,)+ - _ => return Err(crate::error::Error::ParseError(concat!("This is not a ", $name, " element."))), + _ => return Err(crate::util::error::Error::ParseError(concat!("This is not a ", $name, " element."))), }) } } @@ -227,14 +227,14 @@ macro_rules! generate_attribute_enum { ),+ } impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = crate::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::error::Error> { + type Err = crate::util::error::Error; + fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); check_no_unknown_attributes!(elem, $name, [$attr]); Ok(match get_attr!(elem, $attr, required) { $($enum_name => $elem::$enum,)+ - _ => return Err(crate::error::Error::ParseError(concat!("Invalid ", $name, " ", $attr, " value."))), + _ => return Err(crate::util::error::Error::ParseError(concat!("Invalid ", $name, " ", $attr, " value."))), }) } } @@ -257,7 +257,7 @@ macro_rules! check_self { }; ($elem:ident, $name:tt, $ns:ident, $pretty_name:tt) => { if !$elem.is($name, crate::ns::$ns) { - return Err(crate::error::Error::ParseError(concat!( + return Err(crate::util::error::Error::ParseError(concat!( "This is not a ", $pretty_name, " element." @@ -269,7 +269,7 @@ macro_rules! check_self { macro_rules! check_ns_only { ($elem:ident, $name:tt, $ns:ident) => { if !$elem.has_ns(crate::ns::$ns) { - return Err(crate::error::Error::ParseError(concat!( + return Err(crate::util::error::Error::ParseError(concat!( "This is not a ", $name, " element." @@ -282,7 +282,7 @@ macro_rules! check_no_children { ($elem:ident, $name:tt) => { #[cfg(not(feature = "disable-validation"))] for _ in $elem.children() { - return Err(crate::error::Error::ParseError(concat!( + return Err(crate::util::error::Error::ParseError(concat!( "Unknown child in ", $name, " element." @@ -295,7 +295,7 @@ macro_rules! check_no_attributes { ($elem:ident, $name:tt) => { #[cfg(not(feature = "disable-validation"))] for _ in $elem.attrs() { - return Err(crate::error::Error::ParseError(concat!( + return Err(crate::util::error::Error::ParseError(concat!( "Unknown attribute in ", $name, " element." @@ -313,7 +313,7 @@ macro_rules! check_no_unknown_attributes { continue; } )* - return Err(crate::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); + return Err(crate::util::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); } ); } @@ -325,9 +325,9 @@ macro_rules! generate_empty_element { pub struct $elem; impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = crate::error::Error; + type Err = crate::util::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::error::Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); @@ -351,8 +351,8 @@ macro_rules! generate_id { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $elem(pub String); impl ::std::str::FromStr for $elem { - type Err = crate::error::Error; - fn from_str(s: &str) -> Result<$elem, crate::error::Error> { + type Err = crate::util::error::Error; + fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> { // TODO: add a way to parse that differently when needed. Ok($elem(String::from(s))) } @@ -371,15 +371,15 @@ macro_rules! generate_elem_id { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $elem(pub String); impl ::std::str::FromStr for $elem { - type Err = crate::error::Error; - fn from_str(s: &str) -> Result<$elem, crate::error::Error> { + type Err = crate::util::error::Error; + fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> { // TODO: add a way to parse that differently when needed. Ok($elem(String::from(s))) } } impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = crate::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::error::Error> { + type Err = crate::util::error::Error; + fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); @@ -440,7 +440,7 @@ macro_rules! do_parse_elem { }; ($temp:ident: Option = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => { if $temp.is_some() { - return Err(crate::error::Error::ParseError(concat!( + return Err(crate::util::error::Error::ParseError(concat!( "Element ", $parent_name, " must not have more than one ", @@ -452,7 +452,7 @@ macro_rules! do_parse_elem { }; ($temp:ident: Required = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => { if $temp.is_some() { - return Err(crate::error::Error::ParseError(concat!( + return Err(crate::util::error::Error::ParseError(concat!( "Element ", $parent_name, " must not have more than one ", @@ -472,7 +472,7 @@ macro_rules! finish_parse_elem { $temp }; ($temp:ident: Required = $name:tt, $parent_name:tt) => { - $temp.ok_or(crate::error::Error::ParseError(concat!( + $temp.ok_or(crate::util::error::Error::ParseError(concat!( "Missing child ", $name, " in ", @@ -540,9 +540,9 @@ macro_rules! generate_element { } impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = crate::error::Error; + type Err = crate::util::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::error::Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { check_self!(elem, $name, $ns); check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); $( @@ -555,7 +555,7 @@ macro_rules! generate_element { continue; } )* - return Err(crate::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); + return Err(crate::util::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); } Ok($elem { $( diff --git a/src/version.rs b/src/version.rs index c9e08c32199060179e7be0ab3b3229c3ce9a51d0..5aa4d2fbb2acbfe5e3d3d2e8207e57d43958a195 100644 --- a/src/version.rs +++ b/src/version.rs @@ -41,7 +41,7 @@ impl IqResultPayload for VersionResult {} #[cfg(test)] mod tests { use super::*; - use crate::compare_elements::NamespaceAwareCompare; + use crate::util::compare_elements::NamespaceAwareCompare; use minidom::Element; use try_from::TryFrom; From 15b9e65a5d84b9b1b9dc6e6cf9b7b350b270773f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 13 Jan 2019 14:45:13 +0100 Subject: [PATCH 561/698] util: Add forgotten mod.rs file. --- src/util/mod.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/util/mod.rs diff --git a/src/util/mod.rs b/src/util/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..2a595f98dcf136ad651002cee79194cd64c48733 --- /dev/null +++ b/src/util/mod.rs @@ -0,0 +1,19 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +/// Error type returned by every parser on failure. +pub mod error; + +/// Various helpers. +pub(crate) mod helpers; + +/// Helper macros to parse and serialise more easily. +#[macro_use] +mod macros; + +#[cfg(test)] +/// Namespace-aware comparison for tests +pub(crate) mod compare_elements; From 107e66f987ca988be6a169186f733553d8b4b9d9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 Jan 2019 13:17:33 +0100 Subject: [PATCH 562/698] ChangeLog: Add imminent version 0.12.0. --- ChangeLog | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ChangeLog b/ChangeLog index ffe9b95f53389a7bd5aaaa98599a07f9c57dde39..91d25b2945e1ffe85e28e9a085c98eb26bbf806e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,23 @@ +Version 0.12.0: +2018-09-20 Emmanuel Gil Peyrot + * Breaking changes: + - Update dependencies. + - Switch to git, upstream is now available at + https://gitlab.com/xmpp-rs/xmpp-parsers + - Switch to Edition 2018, this removes support for rustc + versions older than 1.31. + - Implement support for XEP-0030 2.5rc3, relaxing the ordering + of children in disco#info. + * Improvements: + - Test for struct size, to keep them known and avoid bloat. + - Add various constructors to make the API easier to use. + - Add forgotten 'ask' attribute on roster item (thanks O01eg!). + - Use cargo-fmt on the codebase, to lower the barrier of entry. + - Add a disable-validation feature, disabling many checks + xmpp-parsers is doing. This should be used for software + which want to let invalid XMPP pass through instead of being + rejected as invalid (thanks Astro-!). + Version 0.11.1: 2018-09-20 Emmanuel Gil Peyrot * Improvements: From 5efc64490ca34642c080d7ffc07f6407f703fa3b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 Jan 2019 13:22:07 +0100 Subject: [PATCH 563/698] lib: Reexport Jid from the jid crate. --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index ed16e05ecf552375532a5de139310a621ace2f39..1bb6263ad59c7ae20c9c803e50ba2099f054c87b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,6 +25,7 @@ #![deny(missing_docs)] pub use minidom::Element; +pub use jid::Jid; /// XML namespace definitions used through XMPP. pub mod ns; From 018a30309011578a24a964f783c180bd3390cd86 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 Jan 2019 13:23:44 +0100 Subject: [PATCH 564/698] ChangeLog: Document why Jid is getting reexported. --- ChangeLog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog b/ChangeLog index 91d25b2945e1ffe85e28e9a085c98eb26bbf806e..017b2e15386b7dc66df02facef05ce48edf9f3bd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -11,6 +11,8 @@ Version 0.12.0: * Improvements: - Test for struct size, to keep them known and avoid bloat. - Add various constructors to make the API easier to use. + - Reexport Jid from the jid crate, to avoid any weird issue on + using different incompatible versions of the same crate. - Add forgotten 'ask' attribute on roster item (thanks O01eg!). - Use cargo-fmt on the codebase, to lower the barrier of entry. - Add a disable-validation feature, disabling many checks From 66b87257ead0fe3ea4136b03cab80e7e31a01945 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 Jan 2019 13:32:55 +0100 Subject: [PATCH 565/698] Cargo.toml: Bump minidom and jid. --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0e3b9a36dbde483ea64a3f00074fcfd2d1a29199..843dde156a603585b2d60caff4e62b9983a3faed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,8 +14,8 @@ license = "MPL-2.0" edition = "2018" [dependencies] -minidom = "0.9.1" -jid = { version = "0.5.2", features = ["minidom"] } +minidom = "0.10.0" +jid = { version = "0.5.3", features = ["minidom"] } base64 = "0.10" digest = "0.8" sha-1 = "0.8" From 9946c5dc5989aac481e3b9fe59e819911e1c3fcb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 Jan 2019 13:45:19 +0100 Subject: [PATCH 566/698] Cargo.toml: Update the homepage and repository. --- Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 843dde156a603585b2d60caff4e62b9983a3faed..374d8973b5594431df5ae6358b00eb111057f9ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,9 +6,9 @@ authors = [ "Maxime “pep” Buquet ", ] description = "Collection of parsers and serialisers for XMPP extensions" -homepage = "https://hg.linkmauve.fr/xmpp-parsers" -repository = "https://hg.linkmauve.fr/xmpp-parsers" -keywords = ["xmpp", "xml"] +homepage = "https://gitlab.com/xmpp-rs/xmpp-parsers" +repository = "https://gitlab.com/xmpp-rs/xmpp-parsers" +keywords = ["xmpp", "jabber", "xml"] categories = ["parsing", "network-programming"] license = "MPL-2.0" edition = "2018" From e3d6605659562921a2332f110e98cae8dec92005 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 Jan 2019 13:48:01 +0100 Subject: [PATCH 567/698] Cargo.toml: Release version 0.12.0. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 374d8973b5594431df5ae6358b00eb111057f9ce..05cb4f83b84f14ae4ba1fd748607d1bfe7d81bf2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.11.1" +version = "0.12.0" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", From bfd90be0c9a06b1ad42f8a98e1a18d68879f0d17 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 Jan 2019 14:32:51 +0100 Subject: [PATCH 568/698] lib: Reexport JidParseError from the jid crate. --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 1bb6263ad59c7ae20c9c803e50ba2099f054c87b..7a291f7ee3927a63e596afd0ef4e86c9c6ea0970 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,7 +25,7 @@ #![deny(missing_docs)] pub use minidom::Element; -pub use jid::Jid; +pub use jid::{Jid, JidParseError}; /// XML namespace definitions used through XMPP. pub mod ns; From 0b38be9ec9ec11d341a015571c65b7cb406e7613 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 Jan 2019 14:33:08 +0100 Subject: [PATCH 569/698] ChangeLog: Fix release date of 0.12.0. --- ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 017b2e15386b7dc66df02facef05ce48edf9f3bd..fb3c8311d753bcf056ebd0dcd87cc9ea8688e240 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,5 @@ Version 0.12.0: -2018-09-20 Emmanuel Gil Peyrot +2019-01-16 Emmanuel Gil Peyrot * Breaking changes: - Update dependencies. - Switch to git, upstream is now available at From 5982f8180509baad972d3f611fe548620bb82dbf Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 Jan 2019 14:33:19 +0100 Subject: [PATCH 570/698] ChangeLog: Add imminent 0.12.1 release. --- ChangeLog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ChangeLog b/ChangeLog index fb3c8311d753bcf056ebd0dcd87cc9ea8688e240..2aff6b309f7dbdd20ea37ff79cd8a0c84a502092 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Version 0.12.1: +2019-01-16 Emmanuel Gil Peyrot + * Improvements: + - Reexport missing JidParseError from the jid crate. + Version 0.12.0: 2019-01-16 Emmanuel Gil Peyrot * Breaking changes: From 6660ca4c869191f92173c180588e77d20785f171 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 Jan 2019 14:33:37 +0100 Subject: [PATCH 571/698] Cargo.toml: Release version 0.12.1. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 05cb4f83b84f14ae4ba1fd748607d1bfe7d81bf2..c3a4afe36592cafd7438538947e7fe450c77e0a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.12.0" +version = "0.12.1" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", From b59ca1cbcbce4d7afed7b4c5a260d2812aaf4472 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 Jan 2019 15:22:51 +0100 Subject: [PATCH 572/698] lib: Reexport TryFrom and util::error::Error. --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 7a291f7ee3927a63e596afd0ef4e86c9c6ea0970..aea3e737a0aa977d3e5f05ca816f712070a0f68a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,6 +26,8 @@ pub use minidom::Element; pub use jid::{Jid, JidParseError}; +pub use try_from::TryFrom; +pub use crate::util::error::Error; /// XML namespace definitions used through XMPP. pub mod ns; From f28a27185b9a861024397c07ea2c1f4068c523f8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 Jan 2019 15:23:00 +0100 Subject: [PATCH 573/698] ChangeLog: Add imminent 0.12.2 release. --- ChangeLog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ChangeLog b/ChangeLog index 2aff6b309f7dbdd20ea37ff79cd8a0c84a502092..1c022a14b2a797e0628c581e90c616db9a8f496e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Version 0.12.2: +2019-01-16 Emmanuel Gil Peyrot + * Improvements: + - Reexport missing util::error::Error and try_from::TryFrom. + Version 0.12.1: 2019-01-16 Emmanuel Gil Peyrot * Improvements: From 71603979127b5bf03b694af265ed305d9dfc2920 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 Jan 2019 15:23:07 +0100 Subject: [PATCH 574/698] Cargo.toml: Release version 0.12.2. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c3a4afe36592cafd7438538947e7fe450c77e0a5..cf9ae10b937b6f662050aec715dd00e3749bdeb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.12.1" +version = "0.12.2" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", From 82015de9646f20fe0c0076966aede427fb55b3f5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 25 Jan 2019 02:49:56 +0100 Subject: [PATCH 575/698] hashes: Add an hex-encoded SHA-1 attribute helper. --- src/hashes.rs | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/hashes.rs b/src/hashes.rs index 041b81fc4795415b230bda7d2393a40a11e16cab..d6a1f37baf3609a24a5ff68a84b837c1b0e4ea4d 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -8,6 +8,8 @@ use crate::util::error::Error; use crate::util::helpers::Base64; use base64; use minidom::IntoAttributeValue; +use std::num::ParseIntError; +use std::ops::{Deref, DerefMut}; use std::str::FromStr; /// List of the algorithms we support, or Unknown. @@ -121,6 +123,47 @@ impl Hash { } } +/// Helper for parsing and serialising a SHA-1 attribute. +#[derive(Debug, Clone, PartialEq)] +pub struct Sha1HexAttribute(Hash); + +impl FromStr for Sha1HexAttribute { + type Err = ParseIntError; + + fn from_str(hex: &str) -> Result { + let mut bytes = vec![]; + for i in 0..hex.len() / 2 { + let byte = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16); + bytes.push(byte?); + } + Ok(Sha1HexAttribute(Hash::new(Algo::Sha_1, bytes))) + } +} + +impl IntoAttributeValue for Sha1HexAttribute { + fn into_attribute_value(self) -> Option { + let mut bytes = vec![]; + for byte in self.0.hash { + bytes.push(format!("{:02x}", byte)); + } + Some(bytes.join("")) + } +} + +impl DerefMut for Sha1HexAttribute { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl Deref for Sha1HexAttribute { + type Target = Hash; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + #[cfg(test)] mod tests { use super::*; From 35fccafc0909618f020b86fc3188d6a77d03f0d9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 25 Jan 2019 02:57:37 +0100 Subject: [PATCH 576/698] avatar: Add a new XEP-0084 parser. --- src/avatar.rs | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 ++ src/ns.rs | 5 +++ 3 files changed, 129 insertions(+) create mode 100644 src/avatar.rs diff --git a/src/avatar.rs b/src/avatar.rs new file mode 100644 index 0000000000000000000000000000000000000000..07f0bde134b34f431d17bf36f29ce59a0181f211 --- /dev/null +++ b/src/avatar.rs @@ -0,0 +1,121 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use crate::hashes::Sha1HexAttribute; +use crate::util::helpers::Base64; + +generate_element!( + /// Communicates information about an avatar. + Metadata, "metadata", AVATAR_METADATA, + children: [ + /// List of information elements describing this avatar. + infos: Vec = ("info", AVATAR_METADATA) => Info + ] +); + +generate_element!( + /// Communicates avatar metadata. + Info, "info", AVATAR_METADATA, + attributes: [ + /// The size of the image data in bytes. + bytes: u16 = "bytes" => required, + + /// The width of the image in pixels. + width: Option = "width" => optional, + + /// The height of the image in pixels. + height: Option = "height" => optional, + + /// The SHA-1 hash of the image data for the specified content-type. + id: Sha1HexAttribute = "id" => required, + + /// The IANA-registered content type of the image data. + type_: String = "type" => required, + + /// The http: or https: URL at which the image data file is hosted. + url: Option = "url" => optional, + ] +); + +generate_element!( + /// The actual avatar data. + Data, "data", AVATAR_DATA, + text: ( + /// Vector of bytes representing the avatar’s image. + data: Base64> + ) +); + +#[cfg(test)] +mod tests { + use super::*; + use crate::hashes::Algo; + use crate::util::error::Error; + use minidom::Element; + use try_from::TryFrom; + + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Metadata, 12); + assert_size!(Info, 60); + assert_size!(Data, 12); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(Metadata, 24); + assert_size!(Info, 112); + assert_size!(Data, 24); + } + + #[test] + fn test_simple() { + let elem: Element = " + + " + .parse() + .unwrap(); + let metadata = Metadata::try_from(elem).unwrap(); + assert_eq!(metadata.infos.len(), 1); + let info = &metadata.infos[0]; + assert_eq!(info.bytes, 12345); + assert_eq!(info.width, Some(64)); + assert_eq!(info.height, Some(64)); + assert_eq!(info.id.algo, Algo::Sha_1); + assert_eq!(info.type_, "image/png"); + assert_eq!(info.url, None); + assert_eq!( + info.id.hash, + [ + 17, 31, 75, 60, 80, 215, 176, 223, 114, 157, 41, 155, 198, 248, 233, 239, 144, 102, + 151, 31 + ] + ); + + let elem: Element = "AAAA" + .parse() + .unwrap(); + let data = Data::try_from(elem).unwrap(); + assert_eq!(data.data, b"\0\0\0"); + } + + #[test] + fn test_invalid() { + let elem: Element = "" + .parse() + .unwrap(); + let error = Data::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in data element.") + } +} diff --git a/src/lib.rs b/src/lib.rs index aea3e737a0aa977d3e5f05ca816f712070a0f68a..765c9a15cc14a641c05000eac73915ea4f28b949 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,6 +83,9 @@ pub mod ibr; /// XEP-0082: XMPP Date and Time Profiles pub mod date; +/// XEP-0084: User Avatar +pub mod avatar; + /// XEP-0085: Chat State Notifications pub mod chatstates; diff --git a/src/ns.rs b/src/ns.rs index 658428f9cdcd5d681bc81ff6621f374796bc445c..7aa4768cfa4a85bfc061d59368cf0640c7494e49 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -56,6 +56,11 @@ pub const PUBSUB_OWNER: &str = "http://jabber.org/protocol/pubsub#owner"; /// XEP-0077: In-Band Registration pub const REGISTER: &str = "jabber:iq:register"; +/// XEP-0084: User Avatar +pub const AVATAR_DATA: &str = "urn:xmpp:avatar:data"; +/// XEP-0084: User Avatar +pub const AVATAR_METADATA: &str = "urn:xmpp:avatar:metadata"; + /// XEP-0085: Chat State Notifications pub const CHATSTATES: &str = "http://jabber.org/protocol/chatstates"; From b6c7a06edd5b62b7ff5ea89f1a150e1a3f3a036b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 25 Jan 2019 03:45:48 +0100 Subject: [PATCH 577/698] avatar: Fix build with --features=disable-validation. --- src/avatar.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/avatar.rs b/src/avatar.rs index 07f0bde134b34f431d17bf36f29ce59a0181f211..f8507bf0e0af1d1810e16c82cf4cbe78d16535ac 100644 --- a/src/avatar.rs +++ b/src/avatar.rs @@ -106,9 +106,10 @@ mod tests { assert_eq!(data.data, b"\0\0\0"); } + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid() { - let elem: Element = "" + let elem: Element = "" .parse() .unwrap(); let error = Data::try_from(elem).unwrap_err(); From 1921f6819e5342c0502a737e9080fae22be4cc92 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Jan 2019 17:16:23 +0100 Subject: [PATCH 578/698] util.helpers: Add a whitespace-aware base64 codec. --- src/util/helpers.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/util/helpers.rs b/src/util/helpers.rs index fad0b671cc4670153339cfc31e2a3e503c375938..2aa37013ea5a7607ca057dd72846721f27e812e6 100644 --- a/src/util/helpers.rs +++ b/src/util/helpers.rs @@ -39,7 +39,7 @@ impl TrimmedPlainText { } } -/// Codec wrapping base64 encode/decode +/// Codec wrapping base64 encode/decode. pub struct Base64; impl Base64 { @@ -51,3 +51,17 @@ impl Base64 { Some(base64::encode(b)) } } + +/// Codec wrapping base64 encode/decode, while ignoring whitespace characters. +pub struct WhitespaceAwareBase64; + +impl WhitespaceAwareBase64 { + pub fn decode(s: &str) -> Result, Error> { + let s: String = s.chars().into_iter().filter(|ch| *ch != ' ' && *ch != '\n' && *ch != '\t').collect(); + Ok(base64::decode(&s)?) + } + + pub fn encode(b: &Vec) -> Option { + Some(base64::encode(b)) + } +} From 376fa9f92e4fd6036dcabba4889f8f1eef203da4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Jan 2019 17:17:12 +0100 Subject: [PATCH 579/698] avatar: Fix parsing of avatar data containing whitespace in the base64. --- src/avatar.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/avatar.rs b/src/avatar.rs index f8507bf0e0af1d1810e16c82cf4cbe78d16535ac..0f2e096244b362c548a4f0046daaa8a23554c771 100644 --- a/src/avatar.rs +++ b/src/avatar.rs @@ -5,7 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::hashes::Sha1HexAttribute; -use crate::util::helpers::Base64; +use crate::util::helpers::WhitespaceAwareBase64; generate_element!( /// Communicates information about an avatar. @@ -45,7 +45,7 @@ generate_element!( Data, "data", AVATAR_DATA, text: ( /// Vector of bytes representing the avatar’s image. - data: Base64> + data: WhitespaceAwareBase64> ) ); From ede2b08039112d414d8719a9d060969292565ea7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Jan 2019 17:18:58 +0100 Subject: [PATCH 580/698] disco: Add a helper constructor for Feature. --- src/disco.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/disco.rs b/src/disco.rs index d61cf361dc3b3258bc238aa11f391fe22dfb282c..ee673b8f92f0bfa314f4b4abfdf903725c7893f6 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -34,6 +34,15 @@ attributes: [ var: String = "var" => required, ]); +impl Feature { + /// Create a new `` with the according `@var`. + pub fn new(var: &str) -> Feature { + Feature { + var: String::from(var) + } + } +} + /// Structure representing an `` element. #[derive(Debug, Clone)] pub struct Identity { From b936ce595f2169fff4e792b21571c4db9147c815 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Jan 2019 17:20:34 +0100 Subject: [PATCH 581/698] caps: Add a helper constructor for Caps. --- src/caps.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/caps.rs b/src/caps.rs index 73c79378ffa1a613c406977599e150303db59f35..419e72ccce184ed963e4ca271d10e0e412367e71 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -71,6 +71,17 @@ impl From for Element { } } +impl Caps { + /// Create a Caps element from its node and hash. + pub fn new>(node: N, hash: Hash) -> Caps { + Caps { + ext: None, + node: node.into(), + hash, + } + } +} + fn compute_item(field: &str) -> Vec { let mut bytes = field.as_bytes().to_vec(); bytes.push(b'<'); From d60feffc22de32d0828e1a5b85e95d4873f94e19 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Jan 2019 17:40:46 +0100 Subject: [PATCH 582/698] pubsub: Add a PubSubPayload trait. --- src/avatar.rs | 5 +++++ src/pubsub/event.rs | 13 ++++++++++++- src/pubsub/mod.rs | 3 +++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/avatar.rs b/src/avatar.rs index 0f2e096244b362c548a4f0046daaa8a23554c771..48893e4c55ce34762ac735548e9fe087221d281e 100644 --- a/src/avatar.rs +++ b/src/avatar.rs @@ -5,6 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::hashes::Sha1HexAttribute; +use crate::pubsub::PubSubPayload; use crate::util::helpers::WhitespaceAwareBase64; generate_element!( @@ -16,6 +17,8 @@ generate_element!( ] ); +impl PubSubPayload for Metadata {} + generate_element!( /// Communicates avatar metadata. Info, "info", AVATAR_METADATA, @@ -49,6 +52,8 @@ generate_element!( ) ); +impl PubSubPayload for Data {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 4e8b9311b167292fb9e11a6b1c00ba22223bac1b..5e53acb870e0a8ad80267a5943925f74229bb5f4 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -8,7 +8,7 @@ use crate::data_forms::DataForm; use crate::date::DateTime; use crate::util::error::Error; use crate::ns; -use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId}; +use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId, PubSubPayload}; use jid::Jid; use minidom::Element; use try_from::TryFrom; @@ -58,6 +58,17 @@ impl From for Element { } } +impl Item { + /// Create a new item event, accepting only payloads implementing `PubSubPayload`. + pub fn new(id: Option, publisher: Option, payload: Option

) -> Item { + Item { + id, + publisher, + payload: payload.map(|payload| payload.into()), + } + } +} + /// Represents an event happening to a PubSub node. #[derive(Debug, Clone)] pub enum PubSubEvent { diff --git a/src/pubsub/mod.rs b/src/pubsub/mod.rs index a8af5c5ad067e8cd976ae279e02a291eef3ade62..dd39ea1d1179b065741a858145d93ed9d95e7417 100644 --- a/src/pubsub/mod.rs +++ b/src/pubsub/mod.rs @@ -45,3 +45,6 @@ generate_attribute!( Unconfigured => "unconfigured", }, Default = None ); + +/// This trait should be implemented on any element which can be included as a PubSub payload. +pub trait PubSubPayload: crate::TryFrom + Into {} From d811c10ed350453826dc6167b4d38245c0795625 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Jan 2019 18:57:25 +0100 Subject: [PATCH 583/698] pubsub: Make Item common to both pubsub and pubsub#event namespaces. --- src/pubsub/event.rs | 58 +++----------------------------------------- src/pubsub/mod.rs | 26 ++++++++++++++++++++ src/pubsub/pubsub.rs | 42 +++----------------------------- src/util/macros.rs | 51 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 92 deletions(-) diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 5e53acb870e0a8ad80267a5943925f74229bb5f4..e0f2b598d06b1b3fb840fe99bb434f553516fbaf 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -8,66 +8,16 @@ use crate::data_forms::DataForm; use crate::date::DateTime; use crate::util::error::Error; use crate::ns; -use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId, PubSubPayload}; +use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId, Item as PubSubItem}; use jid::Jid; use minidom::Element; use try_from::TryFrom; -/// One PubSub item from a node. +/// Event wrapper for a PubSub ``. #[derive(Debug, Clone)] -pub struct Item { - /// The identifier for this item, unique per node. - pub id: Option, +pub struct Item(pub PubSubItem); - /// The JID of the entity who published this item. - pub publisher: Option, - - /// The actual content of this item. - pub payload: Option, -} - -impl TryFrom for Item { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "item", PUBSUB_EVENT); - check_no_unknown_attributes!(elem, "item", ["id", "publisher"]); - let mut payloads = elem.children().cloned().collect::>(); - let payload = payloads.pop(); - if !payloads.is_empty() { - return Err(Error::ParseError( - "More than a single payload in item element.", - )); - } - Ok(Item { - payload, - id: get_attr!(elem, "id", optional), - publisher: get_attr!(elem, "publisher", optional), - }) - } -} - -impl From for Element { - fn from(item: Item) -> Element { - Element::builder("item") - .ns(ns::PUBSUB_EVENT) - .attr("id", item.id) - .attr("publisher", item.publisher) - .append(item.payload) - .build() - } -} - -impl Item { - /// Create a new item event, accepting only payloads implementing `PubSubPayload`. - pub fn new(id: Option, publisher: Option, payload: Option

) -> Item { - Item { - id, - publisher, - payload: payload.map(|payload| payload.into()), - } - } -} +impl_pubsub_item!(Item, PUBSUB_EVENT); /// Represents an event happening to a PubSub node. #[derive(Debug, Clone)] diff --git a/src/pubsub/mod.rs b/src/pubsub/mod.rs index dd39ea1d1179b065741a858145d93ed9d95e7417..81e4e359f14cd54d83f8aa72848a0f474e1d1d8e 100644 --- a/src/pubsub/mod.rs +++ b/src/pubsub/mod.rs @@ -13,6 +13,8 @@ pub mod pubsub; pub use self::event::PubSubEvent; pub use self::pubsub::PubSub; +use crate::{Jid, Element}; + generate_id!( /// The name of a PubSub node, used to identify it on a JID. NodeName @@ -46,5 +48,29 @@ generate_attribute!( }, Default = None ); +/// An item from a PubSub node. +#[derive(Debug, Clone)] +pub struct Item { + /// The identifier for this item, unique per node. + pub id: Option, + + /// The JID of the entity who published this item. + pub publisher: Option, + + /// The payload of this item, in an arbitrary namespace. + pub payload: Option, +} + +impl Item { + /// Create a new item, accepting only payloads implementing `PubSubPayload`. + pub fn new(id: Option, publisher: Option, payload: Option

) -> Item { + Item { + id, + publisher, + payload: payload.map(|payload| payload.into()), + } + } +} + /// This trait should be implemented on any element which can be included as a PubSub payload. pub trait PubSubPayload: crate::TryFrom + Into {} diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index 637b050aea1d04716d867789effcec21d862f9ea..c7bb6466cd948e83aa21602ef6acf1a9804e0755 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -8,7 +8,7 @@ use crate::data_forms::DataForm; use crate::util::error::Error; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; -use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId}; +use crate::pubsub::{NodeName, Subscription, SubscriptionId, Item as PubSubItem}; use jid::Jid; use minidom::Element; use try_from::TryFrom; @@ -113,45 +113,11 @@ generate_element!( ] ); -/// An item from a PubSub node. +/// Response wrapper for a PubSub ``. #[derive(Debug, Clone)] -pub struct Item { - /// The payload of this item, in an arbitrary namespace. - pub payload: Option, +pub struct Item(pub PubSubItem); - /// The 'id' attribute of this item. - pub id: Option, -} - -impl TryFrom for Item { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "item", PUBSUB); - check_no_unknown_attributes!(elem, "item", ["id"]); - let mut payloads = elem.children().cloned().collect::>(); - let payload = payloads.pop(); - if !payloads.is_empty() { - return Err(Error::ParseError( - "More than a single payload in item element.", - )); - } - Ok(Item { - payload, - id: get_attr!(elem, "id", optional), - }) - } -} - -impl From for Element { - fn from(item: Item) -> Element { - Element::builder("item") - .ns(ns::PUBSUB) - .attr("id", item.id) - .append(item.payload) - .build() - } -} +impl_pubsub_item!(Item, PUBSUB); generate_element!( /// The options associated to a subscription request. diff --git a/src/util/macros.rs b/src/util/macros.rs index fd3a0ada1febe68c177c15e1a4e57dc7cf4968db..0c296abba07e94e05d7fb32db3b7dd749ea5a0a8 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -596,3 +596,54 @@ macro_rules! assert_size ( assert_eq!(::std::mem::size_of::<$t>(), $sz); ); ); + +// TODO: move that to src/pubsub/mod.rs, once we figure out how to use macros from there. +macro_rules! impl_pubsub_item { + ($item:ident, $ns:ident) => { + impl crate::TryFrom for $item { + type Err = Error; + + fn try_from(elem: crate::Element) -> Result<$item, Error> { + check_self!(elem, "item", $ns); + check_no_unknown_attributes!(elem, "item", ["id", "publisher"]); + let mut payloads = elem.children().cloned().collect::>(); + let payload = payloads.pop(); + if !payloads.is_empty() { + return Err(Error::ParseError( + "More than a single payload in item element.", + )); + } + Ok($item(crate::pubsub::Item { + id: get_attr!(elem, "id", optional), + publisher: get_attr!(elem, "publisher", optional), + payload, + })) + } + } + + impl From<$item> for crate::Element { + fn from(item: $item) -> crate::Element { + crate::Element::builder("item") + .ns(ns::$ns) + .attr("id", item.0.id) + .attr("publisher", item.0.publisher) + .append(item.0.payload) + .build() + } + } + + impl ::std::ops::Deref for $item { + type Target = crate::pubsub::Item; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl ::std::ops::DerefMut for $item { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + } +} From 63dcba03b2c381d82802b6a22624b15c943a6418 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 21 Feb 2019 20:48:02 +0100 Subject: [PATCH 584/698] =?UTF-8?q?iq:=20Make=20@id=20required,=20as=20per?= =?UTF-8?q?=20RFC6120=20=C2=A78.1.3.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/iq.rs | 77 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 44 insertions(+), 33 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index b155b97c928a7da5072e561d6d6d78888085adac..ccb503ac18f890ea74ae0758e8dcdda09b7cf46c 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -63,7 +63,7 @@ pub struct Iq { /// The @id attribute of this stanza, which is required in order to match a /// request with its result/error. - pub id: Option, + pub id: String, /// The payload content of this stanza. pub payload: IqType, @@ -71,41 +71,41 @@ pub struct Iq { impl Iq { /// Creates an `` stanza containing a get request. - pub fn from_get(payload: impl IqGetPayload) -> Iq { + pub fn from_get(id: String, payload: impl IqGetPayload) -> Iq { Iq { from: None, to: None, - id: None, + id, payload: IqType::Get(payload.into()), } } /// Creates an `` stanza containing a set request. - pub fn from_set(payload: impl IqSetPayload) -> Iq { + pub fn from_set(id: String, payload: impl IqSetPayload) -> Iq { Iq { from: None, to: None, - id: None, + id, payload: IqType::Set(payload.into()), } } /// Creates an `` stanza containing a result. - pub fn from_result(payload: Option) -> Iq { + pub fn from_result(id: String, payload: Option) -> Iq { Iq { from: None, to: None, - id: None, + id, payload: IqType::Result(payload.map(|payload| payload.into())), } } /// Creates an `` stanza containing an error. - pub fn from_error(payload: StanzaError) -> Iq { + pub fn from_error(id: String, payload: StanzaError) -> Iq { Iq { from: None, to: None, - id: None, + id, payload: IqType::Error(payload), } } @@ -124,7 +124,7 @@ impl Iq { /// Sets the id of this stanza, in order to later match its response. pub fn with_id(mut self, id: String) -> Iq { - self.id = Some(id); + self.id = id; self } } @@ -136,7 +136,7 @@ impl TryFrom for Iq { check_self!(root, "iq", DEFAULT_NS); let from = get_attr!(root, "from", optional); let to = get_attr!(root, "to", optional); - let id = get_attr!(root, "id", optional); + let id = get_attr!(root, "id", required); let type_: String = get_attr!(root, "type", required); let mut payload = None; @@ -247,19 +247,30 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; + assert_eq!(message, "Required attribute 'id' missing."); + + #[cfg(not(feature = "component"))] + let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); + let error = Iq::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; assert_eq!(message, "Required attribute 'type' missing."); } #[test] fn test_get() { #[cfg(not(feature = "component"))] - let elem: Element = " + let elem: Element = " " .parse() .unwrap(); #[cfg(feature = "component")] - let elem: Element = " + let elem: Element = " " .parse() @@ -268,7 +279,7 @@ mod tests { let query: Element = "".parse().unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); - assert_eq!(iq.id, None); + assert_eq!(&iq.id, "foo"); assert!(match iq.payload { IqType::Get(element) => element.compare_to(&query), _ => false, @@ -278,13 +289,13 @@ mod tests { #[test] fn test_set() { #[cfg(not(feature = "component"))] - let elem: Element = " + let elem: Element = " " .parse() .unwrap(); #[cfg(feature = "component")] - let elem: Element = " + let elem: Element = " " .parse() @@ -293,7 +304,7 @@ mod tests { let vcard: Element = "".parse().unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); - assert_eq!(iq.id, None); + assert_eq!(&iq.id, "vcard"); assert!(match iq.payload { IqType::Set(element) => element.compare_to(&vcard), _ => false, @@ -303,15 +314,15 @@ mod tests { #[test] fn test_result_empty() { #[cfg(not(feature = "component"))] - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); #[cfg(feature = "component")] - let elem: Element = "" + let elem: Element = "" .parse() .unwrap(); let iq = Iq::try_from(elem).unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); - assert_eq!(iq.id, None); + assert_eq!(&iq.id, "res"); assert!(match iq.payload { IqType::Result(None) => true, _ => false, @@ -321,13 +332,13 @@ mod tests { #[test] fn test_result() { #[cfg(not(feature = "component"))] - let elem: Element = " + let elem: Element = " " .parse() .unwrap(); #[cfg(feature = "component")] - let elem: Element = " + let elem: Element = " " .parse() @@ -338,7 +349,7 @@ mod tests { .unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); - assert_eq!(iq.id, None); + assert_eq!(&iq.id, "res"); assert!(match iq.payload { IqType::Result(Some(element)) => element.compare_to(&query), _ => false, @@ -348,7 +359,7 @@ mod tests { #[test] fn test_error() { #[cfg(not(feature = "component"))] - let elem: Element = " + let elem: Element = " @@ -357,7 +368,7 @@ mod tests { .parse() .unwrap(); #[cfg(feature = "component")] - let elem: Element = " + let elem: Element = " @@ -368,7 +379,7 @@ mod tests { let iq = Iq::try_from(elem).unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); - assert_eq!(iq.id, None); + assert_eq!(iq.id, "err1"); match iq.payload { IqType::Error(error) => { assert_eq!(error.type_, ErrorType::Cancel); @@ -387,11 +398,11 @@ mod tests { #[test] fn test_children_invalid() { #[cfg(not(feature = "component"))] - let elem: Element = "" + let elem: Element = "" .parse() .unwrap(); #[cfg(feature = "component")] - let elem: Element = "" + let elem: Element = "" .parse() .unwrap(); let error = Iq::try_from(elem).unwrap_err(); @@ -405,15 +416,15 @@ mod tests { #[test] fn test_serialise() { #[cfg(not(feature = "component"))] - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); #[cfg(feature = "component")] - let elem: Element = "" + let elem: Element = "" .parse() .unwrap(); let iq2 = Iq { from: None, to: None, - id: None, + id: String::from("res"), payload: IqType::Result(None), }; let elem2 = iq2.into(); @@ -423,9 +434,9 @@ mod tests { #[test] fn test_disco() { #[cfg(not(feature = "component"))] - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); #[cfg(feature = "component")] - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); let iq = Iq::try_from(elem).unwrap(); let disco_info = match iq.payload { IqType::Get(payload) => DiscoInfoQuery::try_from(payload).unwrap(), From 637c3eadd71def30897de32ab748a531c6f3050d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 21 Feb 2019 21:00:58 +0100 Subject: [PATCH 585/698] Fix clippy lints. --- src/bookmarks.rs | 6 ++---- src/caps.rs | 4 ++-- src/component.rs | 3 ++- src/data_forms.rs | 2 +- src/disco.rs | 4 ++-- src/ecaps2.rs | 2 +- src/iq.rs | 8 ++++---- src/jingle.rs | 8 ++++---- src/jingle_ft.rs | 12 ++---------- src/jingle_s5b.rs | 8 ++++---- src/message.rs | 18 +++++++++--------- src/muc/muc.rs | 16 ++++------------ src/muc/user.rs | 4 ++-- src/presence.rs | 2 +- src/pubsub/event.rs | 2 +- src/pubsub/mod.rs | 2 +- src/sasl.rs | 4 ++-- src/sm.rs | 6 ++---- src/stanza_error.rs | 10 +++++----- src/util/helpers.rs | 10 +++++----- 20 files changed, 56 insertions(+), 75 deletions(-) diff --git a/src/bookmarks.rs b/src/bookmarks.rs index 12f95707fc8f7c7bfbcc61d938be68aae9aa7b2e..273f770875d538ebbbfc4f254a4a379d304fd89e 100644 --- a/src/bookmarks.rs +++ b/src/bookmarks.rs @@ -49,6 +49,7 @@ generate_element!( generate_element!( /// Container element for multiple bookmarks. + #[derive(Default)] Storage, "storage", BOOKMARKS, children: [ /// Conferences the user has expressed an interest in. @@ -62,10 +63,7 @@ generate_element!( impl Storage { /// Create an empty bookmarks storage. pub fn new() -> Storage { - Storage { - conferences: Vec::new(), - urls: Vec::new(), - } + Storage::default() } } diff --git a/src/caps.rs b/src/caps.rs index 419e72ccce184ed963e4ca271d10e0e412367e71..a7948d191909d55db79d2692fad3a340f1270665 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -54,7 +54,7 @@ impl TryFrom for Caps { Ok(Caps { ext: get_attr!(elem, "ext", optional), node: get_attr!(elem, "node", required), - hash: hash, + hash, }) } } @@ -202,7 +202,7 @@ pub fn hash_caps(data: &[u8], algo: Algo) -> Result { } Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)), }, - algo: algo, + algo, }) } diff --git a/src/component.rs b/src/component.rs index 722495b8a02b6aea322c16a990070586e299ea4b..4ab78c57c28bf9ffba5ad50cd4bf9588c69fad8b 100644 --- a/src/component.rs +++ b/src/component.rs @@ -10,6 +10,7 @@ use sha1::Sha1; generate_element!( /// The main authentication mechanism for components. + #[derive(Default)] Handshake, "handshake", COMPONENT, text: ( /// If Some, contains the hex-encoded SHA-1 of the concatenation of the @@ -25,7 +26,7 @@ generate_element!( impl Handshake { /// Creates a successful reply from a server. pub fn new() -> Handshake { - Handshake { data: None } + Handshake::default() } /// Creates an authentication request from the component. diff --git a/src/data_forms.rs b/src/data_forms.rs index 30dbd29aa478ac2c9dd918bb3de0e7c08f4196c7..bce941364d75e7f228bb3170a2c9852af5ccc1bd 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -222,7 +222,7 @@ impl TryFrom for DataForm { check_no_unknown_attributes!(elem, "x", ["type"]); let type_ = get_attr!(elem, "type", required); let mut form = DataForm { - type_: type_, + type_, form_type: None, title: None, instructions: None, diff --git a/src/disco.rs b/src/disco.rs index ee673b8f92f0bfa314f4b4abfdf903725c7893f6..9f72cec56677c7d072657a43a093d250f248940d 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -86,8 +86,8 @@ impl TryFrom for Identity { } Ok(Identity { - category: category, - type_: type_, + category, + type_, lang: get_attr!(elem, "xml:lang", optional), name: get_attr!(elem, "name", optional), }) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 846fa6da0d75e1720e4bdff06a9a3e33cd3ffad6..ef3a59325d5e6afb59ab8a8d40ad54f5575704d0 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -136,7 +136,7 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { Algo::Sha_1 => return Err(String::from("Disabled algorithm sha-1: unsafe.")), Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)), }, - algo: algo, + algo, }) } diff --git a/src/iq.rs b/src/iq.rs index ccb503ac18f890ea74ae0758e8dcdda09b7cf46c..dd04e6b8f9d2c385ff25e2460583c59cd5df43cc 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -96,7 +96,7 @@ impl Iq { from: None, to: None, id, - payload: IqType::Result(payload.map(|payload| payload.into())), + payload: IqType::Result(payload.map(Into::into)), } } @@ -188,9 +188,9 @@ impl TryFrom for Iq { }; Ok(Iq { - from: from, - to: to, - id: id, + from, + to, + id, payload: type_, }) } diff --git a/src/jingle.rs b/src/jingle.rs index 63c78a0f808ec9b678502ca1a19ddcc2e44ba2a8..2596687218cca28b0f0c6a0cc4ba16bbc3dc19a4 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -398,8 +398,8 @@ impl TryFrom for ReasonElement { "Reason doesn’t contain a valid reason.", ))?; Ok(ReasonElement { - reason: reason, - text: text, + reason, + text, }) } } @@ -449,8 +449,8 @@ impl Jingle { /// Create a new Jingle element. pub fn new(action: Action, sid: SessionId) -> Jingle { Jingle { - action: action, - sid: sid, + action, + sid, initiator: None, responder: None, contents: Vec::new(), diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index fce18cdf47ff7d8eda6b0eb153b88364ab2249fb..2d61569cb6209bccfed5e75d5a0e1a013614dccb 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -47,7 +47,7 @@ generate_id!( ); /// Represents a file to be transferred. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct File { /// The date of last modification of this file. pub date: Option, @@ -74,15 +74,7 @@ pub struct File { impl File { /// Creates a new file descriptor. pub fn new() -> File { - File { - date: None, - media_type: None, - name: None, - descs: BTreeMap::new(), - size: None, - range: None, - hashes: Vec::new(), - } + File::default() } /// Sets the date of last modification on this file. diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 58c4a27dd36a705bb5a6bd159459a1700655116f..613842f900ca1b8d9179d6f675945a62aa24f600 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -232,10 +232,10 @@ impl TryFrom for Transport { } let payload = payload.unwrap_or(TransportPayload::None); Ok(Transport { - sid: sid, - dstaddr: dstaddr, - mode: mode, - payload: payload, + sid, + dstaddr, + mode, + payload, }) } } diff --git a/src/message.rs b/src/message.rs index fe32300e5d353a33aefda76822c6fbd66f436dfe..f2611accd1a2ee40c6412854593ed37c62928b28 100644 --- a/src/message.rs +++ b/src/message.rs @@ -98,7 +98,7 @@ impl Message { pub fn new(to: Option) -> Message { Message { from: None, - to: to, + to, id: None, type_: MessageType::Chat, bodies: BTreeMap::new(), @@ -192,14 +192,14 @@ impl TryFrom for Message { } } Ok(Message { - from: from, - to: to, - id: id, - type_: type_, - bodies: bodies, - subjects: subjects, - thread: thread, - payloads: payloads, + from, + to, + id, + type_, + bodies, + subjects, + thread, + payloads, }) } } diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 9d29cc963c924481d79855c4e7791aebfa4e63b3..a4e60a4dd5d0e5c01a76f197fb0e0f6dead86247 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -10,7 +10,7 @@ use crate::presence::PresencePayload; generate_element!( /// Represents the query for messages before our join. - #[derive(PartialEq)] + #[derive(PartialEq, Default)] History, "history", MUC, attributes: [ /// How many characters of history to send, in XML characters. @@ -30,12 +30,7 @@ generate_element!( impl History { /// Create a new empty history element. pub fn new() -> Self { - History { - maxchars: None, - maxstanzas: None, - seconds: None, - since: None, - } + History::default() } /// Set how many characters of history to send. @@ -65,7 +60,7 @@ impl History { generate_element!( /// Represents a room join request. - #[derive(PartialEq)] + #[derive(PartialEq, Default)] Muc, "x", MUC, children: [ /// Password to use when the room is protected by a password. password: Option = ("password", MUC) => String, @@ -80,10 +75,7 @@ impl PresencePayload for Muc {} impl Muc { /// Create a new MUC join element. pub fn new() -> Self { - Muc { - password: None, - history: None, - } + Muc::default() } /// Join a room with this password. diff --git a/src/muc/user.rs b/src/muc/user.rs index 46a893337ea7b8525c35c67850dcc31382a45487..8232baf0a875d42208003bb624d6ea0106fb040e 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -100,9 +100,9 @@ impl TryFrom for Actor { match (jid, nick) { (Some(_), Some(_)) | (None, None) => { - return Err(Error::ParseError( + Err(Error::ParseError( "Either 'jid' or 'nick' attribute is required.", - )); + )) } (Some(jid), _) => Ok(Actor::Jid(jid)), (_, Some(nick)) => Ok(Actor::Nick(nick)), diff --git a/src/presence.rs b/src/presence.rs index 4a464f9c3ced4dfb767f71f38404ef203479fb09..e7d2a55df7c6b0f5d80d0f38b488c236c9677d6c 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -197,7 +197,7 @@ impl Presence { from: None, to: None, id: None, - type_: type_, + type_, show: Show::None, statuses: BTreeMap::new(), priority: 0i8, diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index e0f2b598d06b1b3fb840fe99bb434f553516fbaf..0c616ab64225b2a5c894ad42e481ae8c425840c2 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -178,7 +178,7 @@ impl TryFrom for PubSubEvent { } else if child.is("subscription", ns::PUBSUB_EVENT) { check_no_children!(child, "subscription"); payload = Some(PubSubEvent::Subscription { - node: node, + node, expiry: get_attr!(child, "expiry", optional), jid: get_attr!(child, "jid", optional), subid: get_attr!(child, "subid", optional), diff --git a/src/pubsub/mod.rs b/src/pubsub/mod.rs index 81e4e359f14cd54d83f8aa72848a0f474e1d1d8e..cde7e77529d65e99dfd0b4f96bc7f198449cd8ee 100644 --- a/src/pubsub/mod.rs +++ b/src/pubsub/mod.rs @@ -67,7 +67,7 @@ impl Item { Item { id, publisher, - payload: payload.map(|payload| payload.into()), + payload: payload.map(Into::into), } } } diff --git a/src/sasl.rs b/src/sasl.rs index fecdbfa6b6ca7017593af6d3df9bd7839daa1e9d..8f63f092a937ba38e77b2984c04f239a0bd8b745 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -192,8 +192,8 @@ impl TryFrom for Failure { defined_condition.ok_or(Error::ParseError("Failure must have a defined-condition."))?; Ok(Failure { - defined_condition: defined_condition, - texts: texts, + defined_condition, + texts, }) } } diff --git a/src/sm.rs b/src/sm.rs index fc8e7377609a0db349799f2cc9bf033fafd19fe8..1f48615e30da58c541955deade8c912e86bc04de 100644 --- a/src/sm.rs +++ b/src/sm.rs @@ -31,6 +31,7 @@ generate_attribute!( generate_element!( /// Client request for enabling stream management. + #[derive(Default)] Enable, "enable", SM, attributes: [ /// The preferred resumption time in seconds by the client. @@ -45,10 +46,7 @@ generate_element!( impl Enable { /// Generates a new `` element. pub fn new() -> Self { - Enable { - max: None, - resume: ResumeAttr::False, - } + Enable::default() } /// Sets the preferred resumption time in seconds. diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 7a4d421aad5505ce9f02a93c2169a4180ba6bd1b..d254f6b48d9a0e17c9f8fdd8d97f294a557056f6 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -258,11 +258,11 @@ impl TryFrom for StanzaError { defined_condition.ok_or(Error::ParseError("Error must have a defined-condition."))?; Ok(StanzaError { - type_: type_, - by: by, - defined_condition: defined_condition, - texts: texts, - other: other, + type_, + by, + defined_condition, + texts, + other, }) } } diff --git a/src/util/helpers.rs b/src/util/helpers.rs index 2aa37013ea5a7607ca057dd72846721f27e812e6..b3832345c33fd41698c71cce88b7f7dd913aa0e0 100644 --- a/src/util/helpers.rs +++ b/src/util/helpers.rs @@ -19,7 +19,7 @@ impl PlainText { } pub fn encode(string: &Option) -> Option { - string.as_ref().map(|text| text.to_owned()) + string.as_ref().map(ToOwned::to_owned) } } @@ -34,7 +34,7 @@ impl TrimmedPlainText { }) } - pub fn encode(string: &String) -> String { + pub fn encode(string: &str) -> String { string.to_owned() } } @@ -47,7 +47,7 @@ impl Base64 { Ok(base64::decode(s)?) } - pub fn encode(b: &Vec) -> Option { + pub fn encode(b: &[u8]) -> Option { Some(base64::encode(b)) } } @@ -57,11 +57,11 @@ pub struct WhitespaceAwareBase64; impl WhitespaceAwareBase64 { pub fn decode(s: &str) -> Result, Error> { - let s: String = s.chars().into_iter().filter(|ch| *ch != ' ' && *ch != '\n' && *ch != '\t').collect(); + let s: String = s.chars().filter(|ch| *ch != ' ' && *ch != '\n' && *ch != '\t').collect(); Ok(base64::decode(&s)?) } - pub fn encode(b: &Vec) -> Option { + pub fn encode(b: &[u8]) -> Option { Some(base64::encode(b)) } } From a076221c9a109fa1a1661330291bb547c35ab96a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Mon, 4 Feb 2019 16:05:05 +0000 Subject: [PATCH 586/698] Add rustdoc flag to sort modules by XEP number MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `--sort-modules-by-appearance` needs to be passed to rustdoc for this to happen. I haven't found a way to make it so that we don't have to add this flag manually each time we build locally. This config option should at least fix it for docs.rs. Signed-off-by: Maxime “pep” Buquet --- Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index cf9ae10b937b6f662050aec715dd00e3749bdeb4..8aab21dc5c72636969f7a1ace092cb739cf4587c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,3 +30,6 @@ try_from = "0.3.2" component = [] # Disable validation of unknown attributes. disable-validation = [] + +[package.metadata.docs.rs] +rustdoc-args = [ "--sort-modules-by-appearance", "-Z unstable-options" ] From f2c3f45a6fd30386737bcfba21432d07c833d84b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 24 Feb 2019 19:25:14 +0100 Subject: [PATCH 587/698] data_forms: Stop duplicating FORM_TYPE in memory. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The FORM_TYPE is now only present once, as the form_type member of the DataForm struct, it isn’t duplicated in fields anymore. This removes the need to ignore this special field in every single protocol built on XEP-0128. --- src/caps.rs | 4 +--- src/data_forms.rs | 16 +++++++++++++--- src/ecaps2.rs | 31 ++++++++++++++++++++----------- src/ibr.rs | 12 ------------ 4 files changed, 34 insertions(+), 29 deletions(-) diff --git a/src/caps.rs b/src/caps.rs index a7948d191909d55db79d2692fad3a340f1270665..1a0d57694c2a51430ef61e6f8d92f01565a03b1a 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -329,9 +329,7 @@ mod tests { "# .parse() .unwrap(); - let data = b"client/pc/el/\xce\xa8 0.11 for DataForm { form.instructions = Some(child.text()); } else if child.is("field", ns::DATA_FORMS) { let field = Field::try_from(child.clone())?; - if field.var == "FORM_TYPE" && field.type_ == FieldType::Hidden { + if field.var == "FORM_TYPE" { + let mut field = field; if form.form_type.is_some() { return Err(Error::ParseError("More than one FORM_TYPE in a data form.")); } + if field.type_ != FieldType::Hidden { + return Err(Error::ParseError("Invalid field type for FORM_TYPE.")); + } if field.values.len() != 1 { return Err(Error::ParseError("Wrong number of values in FORM_TYPE.")); } - form.form_type = Some(field.values[0].clone()); + form.form_type = field.values.pop(); + } else { + form.fields.push(field); } - form.fields.push(field); } else { return Err(Error::ParseError("Unknown child in data form element.")); } @@ -279,6 +284,11 @@ impl From for Element { .ns(ns::DATA_FORMS) .append(text) })) + .append(if let Some(form_type) = form.form_type { + vec![Element::builder("field").ns(ns::DATA_FORMS).attr("var", "FORM_TYPE").attr("type", "hidden").append(Element::builder("value").ns(ns::DATA_FORMS).append(form_type).build()).build()] + } else { + vec![] + }) .append(form.fields) .build() } diff --git a/src/ecaps2.rs b/src/ecaps2.rs index ef3a59325d5e6afb59ab8a8d40ad54f5575704d0..352881f105e2149f8b367d33ff1d6e0cfaf662c6 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -69,31 +69,40 @@ fn compute_identities(identities: &[Identity]) -> Vec { }) } -fn compute_extensions(extensions: &[DataForm]) -> Vec { - compute_items(extensions, 0x1c, |extension| { - compute_items(&extension.fields, 0x1d, |field| { +fn compute_extensions(extensions: &[DataForm]) -> Result, ()> { + for extension in extensions { + if extension.form_type.is_none() { + return Err(()); + } + } + Ok(compute_items(extensions, 0x1c, |extension| { + let mut bytes = compute_item("FORM_TYPE"); + bytes.append(&mut compute_item(if let Some(ref form_type) = extension.form_type { form_type } else { unreachable!() })); + bytes.push(0x1e); + bytes.append(&mut compute_items(&extension.fields, 0x1d, |field| { let mut bytes = compute_item(&field.var); bytes.append(&mut compute_items(&field.values, 0x1e, |value| { compute_item(value) })); bytes - }) - }) + })); + bytes + })) } /// Applies the [algorithm from /// XEP-0390](https://xmpp.org/extensions/xep-0390.html#algorithm-input) on a /// [disco#info query element](../disco/struct.DiscoInfoResult.html). -pub fn compute_disco(disco: &DiscoInfoResult) -> Vec { +pub fn compute_disco(disco: &DiscoInfoResult) -> Result, ()> { let features_string = compute_features(&disco.features); let identities_string = compute_identities(&disco.identities); - let extensions_string = compute_extensions(&disco.extensions); + let extensions_string = compute_extensions(&disco.extensions)?; let mut final_string = vec![]; final_string.extend(features_string); final_string.extend(identities_string); final_string.extend(extensions_string); - final_string + Ok(final_string) } fn get_hash_vec(hash: &[u8]) -> Vec { @@ -204,7 +213,7 @@ mod tests { fn test_simple() { let elem: Element = "".parse().unwrap(); let disco = DiscoInfoResult::try_from(elem).unwrap(); - let ecaps2 = compute_disco(&disco); + let ecaps2 = compute_disco(&disco).unwrap(); assert_eq!(ecaps2.len(), 54); } @@ -263,7 +272,7 @@ mod tests { 117, 115, 77, 111, 100, 31, 30, 28, 28, ]; let disco = DiscoInfoResult::try_from(elem).unwrap(); - let ecaps2 = compute_disco(&disco); + let ecaps2 = compute_disco(&disco).unwrap(); assert_eq!(ecaps2.len(), 0x1d9); assert_eq!(ecaps2, expected); @@ -424,7 +433,7 @@ mod tests { 98, 50, 41, 31, 30, 29, 28, ]; let disco = DiscoInfoResult::try_from(elem).unwrap(); - let ecaps2 = compute_disco(&disco); + let ecaps2 = compute_disco(&disco).unwrap(); assert_eq!(ecaps2.len(), 0x543); assert_eq!(ecaps2, expected); diff --git a/src/ibr.rs b/src/ibr.rs index f19c47c131ba137644addac47d35d03ee884ee63..c98c909369c4ba1a25e52ae603c6e7b59584b28c 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -207,18 +207,6 @@ mod tests { assert!(!query.fields["instructions"].is_empty()); let form = query.form.clone().unwrap(); assert!(!form.instructions.unwrap().is_empty()); - assert!(form - .fields - .binary_search_by(|field| field.var.cmp(&String::from("first"))) - .is_ok()); - assert!(form - .fields - .binary_search_by(|field| field.var.cmp(&String::from("x-gender"))) - .is_ok()); - assert!(form - .fields - .binary_search_by(|field| field.var.cmp(&String::from("coucou"))) - .is_err()); let elem2 = query.into(); assert!(elem1.compare_to(&elem2)); } From bcd42a26e3b8d27524d4eb5ffde14f3fd37e660e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 24 Feb 2019 20:26:40 +0100 Subject: [PATCH 588/698] macros: Use a nicer syntax when declaring attributes. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous version had a => required|optional|default token, this was duplicating information for Option types and didn’t look very good. This new version looks like a type, which can be either Required<_>, Option<_> or Default<_>, and means the same thing. --- src/avatar.rs | 12 +++++----- src/bookmarks.rs | 10 ++++---- src/data_forms.rs | 2 +- src/delay.rs | 4 ++-- src/disco.rs | 14 +++++------ src/eme.rs | 4 ++-- src/hashes.rs | 2 +- src/ibb.rs | 12 +++++----- src/idle.rs | 2 +- src/jingle.rs | 8 +++---- src/jingle_ft.rs | 8 +++---- src/jingle_ibb.rs | 6 ++--- src/jingle_s5b.rs | 12 +++++----- src/mam.rs | 10 ++++---- src/media_element.rs | 6 ++--- src/message_correct.rs | 2 +- src/muc/muc.rs | 8 +++---- src/muc/user.rs | 10 ++++---- src/pubsub/pubsub.rs | 50 +++++++++++++++++++-------------------- src/receipts.rs | 2 +- src/roster.rs | 10 ++++---- src/sasl.rs | 2 +- src/sm.rs | 24 +++++++++---------- src/stanza_id.rs | 6 ++--- src/stream.rs | 10 ++++---- src/util/macros.rs | 53 +++++++++++++++++++++++++++++++----------- src/websocket.rs | 10 ++++---- 27 files changed, 162 insertions(+), 137 deletions(-) diff --git a/src/avatar.rs b/src/avatar.rs index 48893e4c55ce34762ac735548e9fe087221d281e..6e2d3ce03da49f5891ef45fa28050f1ddd9b47d8 100644 --- a/src/avatar.rs +++ b/src/avatar.rs @@ -24,22 +24,22 @@ generate_element!( Info, "info", AVATAR_METADATA, attributes: [ /// The size of the image data in bytes. - bytes: u16 = "bytes" => required, + bytes: Required = "bytes", /// The width of the image in pixels. - width: Option = "width" => optional, + width: Option = "width", /// The height of the image in pixels. - height: Option = "height" => optional, + height: Option = "height", /// The SHA-1 hash of the image data for the specified content-type. - id: Sha1HexAttribute = "id" => required, + id: Required = "id", /// The IANA-registered content type of the image data. - type_: String = "type" => required, + type_: Required = "type", /// The http: or https: URL at which the image data file is hosted. - url: Option = "url" => optional, + url: Option = "url", ] ); diff --git a/src/bookmarks.rs b/src/bookmarks.rs index 273f770875d538ebbbfc4f254a4a379d304fd89e..b1c59b0e40d0372921eeb082ac29caa42d4c1116 100644 --- a/src/bookmarks.rs +++ b/src/bookmarks.rs @@ -18,13 +18,13 @@ generate_element!( Conference, "conference", BOOKMARKS, attributes: [ /// Whether a conference bookmark should be joined automatically. - autojoin: Autojoin = "autojoin" => default, + autojoin: Default = "autojoin", /// The JID of the conference. - jid: Jid = "jid" => required, + jid: Required = "jid", /// A user-defined name for this conference. - name: String = "name" => required + name: Required = "name", ], children: [ /// The nick the user will use to join this conference. @@ -40,10 +40,10 @@ generate_element!( Url, "url", BOOKMARKS, attributes: [ /// A user-defined name for this URL. - name: String = "name" => required, + name: Required = "name", /// The URL of this bookmark. - url: String = "url" => required + url: Required = "url", ] ); diff --git a/src/data_forms.rs b/src/data_forms.rs index 6e2d4e3d2c770198860c6dba11b6043e11a16572..9a1ed3e3297556ebd26cabe8be938fb64b97c8f7 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -15,7 +15,7 @@ generate_element!( Option_, "option", DATA_FORMS, attributes: [ /// The optional label to be displayed to the user for this option. - label: Option = "label" => optional + label: Option = "label" ], children: [ /// The value returned to the server when selecting this option. diff --git a/src/delay.rs b/src/delay.rs index a02b44b1a4338929806b75529c2af166cb945191..80f92edd00eb8b8023d8920df551de550493bed8 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -15,10 +15,10 @@ generate_element!( Delay, "delay", DELAY, attributes: [ /// The entity which delayed this message. - from: Option = "from" => optional, + from: Option = "from", /// The time at which this message got stored. - stamp: DateTime = "stamp" => required + stamp: Required = "stamp" ], text: ( /// The optional reason this message got delayed. diff --git a/src/disco.rs b/src/disco.rs index 9f72cec56677c7d072657a43a093d250f248940d..43c15903365dc900188ac0acc2801cc24ed2a1de 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -20,7 +20,7 @@ generate_element!( DiscoInfoQuery, "query", DISCO_INFO, attributes: [ /// Node on which we are doing the discovery. - node: Option = "node" => optional, + node: Option = "node", ]); impl IqGetPayload for DiscoInfoQuery {} @@ -31,7 +31,7 @@ generate_element!( Feature, "feature", DISCO_INFO, attributes: [ /// Namespace of the feature we want to represent. - var: String = "var" => required, + var: Required = "var", ]); impl Feature { @@ -206,7 +206,7 @@ generate_element!( DiscoItemsQuery, "query", DISCO_ITEMS, attributes: [ /// Node on which we are doing the discovery. - node: Option = "node" => optional, + node: Option = "node", ]); impl IqGetPayload for DiscoItemsQuery {} @@ -216,11 +216,11 @@ generate_element!( Item, "item", DISCO_ITEMS, attributes: [ /// JID of the entity pointed by this item. - jid: Jid = "jid" => required, + jid: Required = "jid", /// Node of the entity pointed by this item. - node: Option = "node" => optional, + node: Option = "node", /// Name of the entity pointed by this item. - name: Option = "name" => optional, + name: Option = "name", ]); generate_element!( @@ -232,7 +232,7 @@ generate_element!( DiscoItemsResult, "query", DISCO_ITEMS, attributes: [ /// Node on which we have done this discovery. - node: Option = "node" => optional + node: Option = "node" ], children: [ /// List of items pointed by this entity. diff --git a/src/eme.rs b/src/eme.rs index cd24e43d03b7bf95799d75b86b086d3b280d8ab1..20623ff2f2b628ed56310bb8d1110d6df20ead6d 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -11,11 +11,11 @@ generate_element!( ExplicitMessageEncryption, "encryption", EME, attributes: [ /// Namespace of the encryption scheme used. - namespace: String = "namespace" => required, + namespace: Required = "namespace", /// User-friendly name for the encryption scheme, should be `None` for OTR, /// legacy OpenPGP and OX. - name: Option = "name" => optional, + name: Option = "name", ] ); diff --git a/src/hashes.rs b/src/hashes.rs index d6a1f37baf3609a24a5ff68a84b837c1b0e4ea4d..c097d034690678a682b1dd9a73daa72a85e1657c 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -102,7 +102,7 @@ generate_element!( Hash, "hash", HASHES, attributes: [ /// The algorithm used to create this hash. - algo: Algo = "algo" => required + algo: Required = "algo" ], text: ( /// The hash value, as a vector of bytes. diff --git a/src/ibb.rs b/src/ibb.rs index 960761187becc66ba7d9e1f7dd2ec02922d635ba..aa1e07d3c2308cd767defbec47be9e80d72f3fd9 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -30,13 +30,13 @@ generate_element!( Open, "open", IBB, attributes: [ /// Maximum size in bytes for each chunk. - block_size: u16 = "block-size" => required, + block_size: Required = "block-size", /// The identifier to be used to create a stream. - sid: StreamId = "sid" => required, + sid: Required = "sid", /// Which stanza type to use to exchange data. - stanza: Stanza = "stanza" => default, + stanza: Default = "stanza", ]); impl IqSetPayload for Open {} @@ -46,10 +46,10 @@ generate_element!( Data, "data", IBB, attributes: [ /// Sequence number of this chunk, must wraparound after 65535. - seq: u16 = "seq" => required, + seq: Required = "seq", /// The identifier of the stream on which data is being exchanged. - sid: StreamId = "sid" => required + sid: Required = "sid" ], text: ( /// Vector of bytes to be exchanged. @@ -64,7 +64,7 @@ generate_element!( Close, "close", IBB, attributes: [ /// The identifier of the stream to be closed. - sid: StreamId = "sid" => required, + sid: Required = "sid", ]); impl IqSetPayload for Close {} diff --git a/src/idle.rs b/src/idle.rs index bbf3f15114546636f20c49de18edec939d15ce8e..89b62c0549f139c4832ea5feafbf9dc77efbe5e3 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -12,7 +12,7 @@ generate_element!( Idle, "idle", IDLE, attributes: [ /// The time at which the user stopped interacting. - since: DateTime = "since" => required, + since: Required = "since", ] ); diff --git a/src/jingle.rs b/src/jingle.rs index 2596687218cca28b0f0c6a0cc4ba16bbc3dc19a4..b891ab7caea1b7a2d7fbae352fd8ade108b645b4 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -169,16 +169,16 @@ generate_element!( Content, "content", JINGLE, attributes: [ /// Who created this content. - creator: Creator = "creator" => required, + creator: Required = "creator", /// How the content definition is to be interpreted by the recipient. - disposition: Disposition = "disposition" => default, + disposition: Default = "disposition", /// A per-session unique identifier for this content. - name: ContentId = "name" => required, + name: Required = "name", /// Who can send data for this content. - senders: Senders = "senders" => default + senders: Default = "senders", ], children: [ /// What to send. diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 2d61569cb6209bccfed5e75d5a0e1a013614dccb..67c055e23290eed991eb0a0add4a68761c123ccc 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -20,11 +20,11 @@ generate_element!( Range, "range", JINGLE_FT, attributes: [ /// The offset in bytes from the beginning of the file. - offset: u64 = "offset" => default, + offset: Default = "offset", /// The length in bytes of the range, or None to be the entire /// remaining of the file. - length: Option = "length" => optional + length: Option = "length" ], children: [ /// List of hashes for this range. @@ -344,10 +344,10 @@ generate_element!( Received, "received", JINGLE_FT, attributes: [ /// The content identifier of this Jingle session. - name: ContentId = "name" => required, + name: Required = "name", /// The creator of this file transfer. - creator: Creator = "creator" => required, + creator: Required = "creator", ] ); diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 08370e1f7f9dd328abb9dd3c2194547c20422111..019f03429f520c8983b1f937b96ba4bf8b68a3e5 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -12,13 +12,13 @@ generate_element!( Transport, "transport", JINGLE_IBB, attributes: [ /// Maximum size in bytes for each chunk. - block_size: u16 = "block-size" => required, + block_size: Required = "block-size", /// The identifier to be used to create a stream. - sid: StreamId = "sid" => required, + sid: Required = "sid", /// Which stanza type to use to exchange data. - stanza: Stanza = "stanza" => default, + stanza: Default = "stanza", ]); #[cfg(test)] diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 613842f900ca1b8d9179d6f675945a62aa24f600..1f3a3ee60fb3b7f67c3cbc472bd00237a333fe6e 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -55,23 +55,23 @@ generate_element!( Candidate, "candidate", JINGLE_S5B, attributes: [ /// The identifier for this candidate. - cid: CandidateId = "cid" => required, + cid: Required = "cid", /// The host to connect to. - host: IpAddr = "host" => required, + host: Required = "host", /// The JID to request at the given end. - jid: Jid = "jid" => required, + jid: Required = "jid", /// The port to connect to. - port: Option = "port" => optional, + port: Option = "port", /// The priority of this candidate, computed using this formula: /// priority = (2^16)*(type preference) + (local preference) - priority: u32 = "priority" => required, + priority: Required = "priority", /// The type of the connection being proposed by this candidate. - type_: Type = "type" => default, + type_: Default = "type", ] ); diff --git a/src/mam.rs b/src/mam.rs index 453b4f606deeafbf321d58867e27564969f7e94f..b68dabb272b8052bd173da25e7cb40fa38af7950 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -27,10 +27,10 @@ generate_element!( attributes: [ /// An optional identifier for matching forwarded messages to this /// query. - queryid: Option = "queryid" => optional, + queryid: Option = "queryid", /// Must be set to Some when querying a PubSub node’s archive. - node: Option = "node" => optional + node: Option = "node" ], children: [ /// Used for filtering the results. @@ -50,11 +50,11 @@ generate_element!( Result_, "result", MAM, attributes: [ /// The stanza-id under which the archive stored this stanza. - id: String = "id" => required, + id: Required = "id", /// The same queryid as the one requested in the /// [query](struct.Query.html). - queryid: Option = "queryid" => optional, + queryid: Option = "queryid", ], children: [ /// The actual stanza being forwarded. @@ -76,7 +76,7 @@ generate_element!( Fin, "fin", MAM, attributes: [ /// True when the end of a MAM query has been reached. - complete: Complete = "complete" => default + complete: Default = "complete", ], children: [ /// Describes the current page, it should contain at least [first] diff --git a/src/media_element.rs b/src/media_element.rs index 639b71cea3401fcf764ffba7a54e36c760328900..259e9d1226d5a14336f2697c58a4b0113ddfdaa3 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -17,7 +17,7 @@ generate_element!( /// accepted too. /// /// [1]: https://www.iana.org/assignments/media-types/media-types.xhtml - type_: String = "type" => required + type_: Required = "type" ], text: ( /// The actual URI contained. @@ -31,10 +31,10 @@ generate_element!( MediaElement, "media", MEDIA_ELEMENT, attributes: [ /// The recommended display width in pixels. - width: Option = "width" => optional, + width: Option = "width", /// The recommended display height in pixels. - height: Option = "height" => optional + height: Option = "height" ], children: [ /// A list of URIs referencing this media. diff --git a/src/message_correct.rs b/src/message_correct.rs index 05663c8daa34cb40755378242a03e74b1c37d586..b2600e0b9a021f227b0bef80065fb83fa1778a9f 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -12,7 +12,7 @@ generate_element!( Replace, "replace", MESSAGE_CORRECT, attributes: [ /// The 'id' attribute of the message getting corrected. - id: String = "id" => required, + id: Required = "id", ] ); diff --git a/src/muc/muc.rs b/src/muc/muc.rs index a4e60a4dd5d0e5c01a76f197fb0e0f6dead86247..898af67896f657699516615e46d3fcaa50e7a8af 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -14,16 +14,16 @@ generate_element!( History, "history", MUC, attributes: [ /// How many characters of history to send, in XML characters. - maxchars: Option = "maxchars" => optional, + maxchars: Option = "maxchars", /// How many messages to send. - maxstanzas: Option = "maxstanzas" => optional, + maxstanzas: Option = "maxstanzas", /// Only send messages received in these last seconds. - seconds: Option = "seconds" => optional, + seconds: Option = "seconds", /// Only send messages after this date. - since: Option = "since" => optional, + since: Option = "since", ] ); diff --git a/src/muc/user.rs b/src/muc/user.rs index 8232baf0a875d42208003bb624d6ea0106fb040e..73e910ae1bcccb82890fe7d9637756ec6d53486c 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -128,7 +128,7 @@ generate_element!( Continue, "continue", MUC_USER, attributes: [ /// The thread to continue in this room. - thread: Option = "thread" => optional, + thread: Option = "thread", ] ); @@ -187,16 +187,16 @@ generate_element!( /// An item representing a user in a room. Item, "item", MUC_USER, attributes: [ /// The affiliation of this user with the room. - affiliation: Affiliation = "affiliation" => required, + affiliation: Required = "affiliation", /// The real JID of this user, if you are allowed to see it. - jid: Option = "jid" => optional, + jid: Option = "jid", /// The current nickname of this user. - nick: Option = "nick" => optional, + nick: Option = "nick", /// The current role of this user. - role: Role = "role" => required + role: Required = "role", ], children: [ /// The actor affected by this item. actor: Option = ("actor", MUC_USER) => Actor, diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index c7bb6466cd948e83aa21602ef6acf1a9804e0755..5093b3e1512e692bbd02f47f779e311944ebf0e6 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -20,7 +20,7 @@ generate_element!( Affiliations, "affiliations", PUBSUB, attributes: [ /// The optional node name this request pertains to. - node: Option = "node" => optional, + node: Option = "node", ], children: [ /// The actual list of affiliation elements. @@ -56,10 +56,10 @@ generate_element!( Affiliation, "affiliation", PUBSUB, attributes: [ /// The node this affiliation pertains to. - node: NodeName = "node" => required, + node: Required = "node", /// The affiliation you currently have on this node. - affiliation: AffiliationAttribute = "affiliation" => required, + affiliation: Required = "affiliation", ] ); @@ -77,7 +77,7 @@ generate_element!( Create, "create", PUBSUB, attributes: [ /// The node name to create, if `None` the service will generate one. - node: Option = "node" => optional, + node: Option = "node", ] ); @@ -86,10 +86,10 @@ generate_element!( Default, "default", PUBSUB, attributes: [ /// The node targetted by this request, otherwise the entire service. - node: Option = "node" => optional, + node: Option = "node", // TODO: do we really want to support collection nodes? - // type: String = "type" => optional, + // type: Option = "type", ] ); @@ -99,13 +99,13 @@ generate_element!( attributes: [ // TODO: should be an xs:positiveInteger, that is, an unbounded int ≥ 1. /// Maximum number of items returned. - max_items: Option = "max_items" => optional, + max_items: Option = "max_items", /// The node queried by this request. - node: NodeName = "node" => required, + node: Required = "node", /// The subscription identifier related to this request. - subid: Option = "subid" => optional, + subid: Option = "subid", ], children: [ /// The actual list of items returned. @@ -124,13 +124,13 @@ generate_element!( Options, "options", PUBSUB, attributes: [ /// The JID affected by this request. - jid: Jid = "jid" => required, + jid: Required = "jid", /// The node affected by this request. - node: Option = "node" => optional, + node: Option = "node", /// The subscription identifier affected by this request. - subid: Option = "subid" => optional, + subid: Option = "subid", ], children: [ /// The form describing the subscription. @@ -143,7 +143,7 @@ generate_element!( Publish, "publish", PUBSUB, attributes: [ /// The target node for this operation. - node: NodeName = "node" => required, + node: Required = "node", ], children: [ /// The items you want to publish. @@ -172,10 +172,10 @@ generate_element!( Retract, "retract", PUBSUB, attributes: [ /// The node affected by this request. - node: NodeName = "node" => required, + node: Required = "node", /// Whether a retract request should notify subscribers or not. - notify: Notify = "notify" => default, + notify: Default = "notify", ], children: [ /// The items affected by this request. @@ -233,10 +233,10 @@ generate_element!( Subscribe, "subscribe", PUBSUB, attributes: [ /// The JID being subscribed. - jid: Jid = "jid" => required, + jid: Required = "jid", /// The node to subscribe to. - node: Option = "node" => optional, + node: Option = "node", ] ); @@ -245,7 +245,7 @@ generate_element!( Subscriptions, "subscriptions", PUBSUB, attributes: [ /// The node to query. - node: Option = "node" => optional, + node: Option = "node", ], children: [ /// The list of subscription elements returned. @@ -258,16 +258,16 @@ generate_element!( SubscriptionElem, "subscription", PUBSUB, attributes: [ /// The JID affected by this subscription. - jid: Jid = "jid" => required, + jid: Required = "jid", /// The node affected by this subscription. - node: Option = "node" => optional, + node: Option = "node", /// The subscription identifier for this subscription. - subid: Option = "subid" => optional, + subid: Option = "subid", /// The state of the subscription. - subscription: Option = "subscription" => optional, + subscription: Option = "subscription", ], children: [ /// The options related to this subscription. @@ -280,13 +280,13 @@ generate_element!( Unsubscribe, "unsubscribe", PUBSUB, attributes: [ /// The JID affected by this request. - jid: Jid = "jid" => required, + jid: Required = "jid", /// The node affected by this request. - node: Option = "node" => optional, + node: Option = "node", /// The subscription identifier for this subscription. - subid: Option = "subid" => optional, + subid: Option = "subid", ] ); diff --git a/src/receipts.rs b/src/receipts.rs index c97d3e118358630758fa253d5e0153fbea3cf8cc..e60900401ec1510473735441f10d17c24be516d3 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -22,7 +22,7 @@ generate_element!( Received, "received", RECEIPTS, attributes: [ /// The 'id' attribute of the received message. - id: Option = "id" => optional, + id: Option = "id", ] ); diff --git a/src/roster.rs b/src/roster.rs index 29086aec2ae7777b00ffebebcf072a4e2587c458..57d440e97733717d16ff33c86e9c16c8ee938235 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -50,16 +50,16 @@ generate_element!( Item, "item", ROSTER, attributes: [ /// JID of this contact. - jid: Jid = "jid" => required, + jid: Required = "jid", /// Name of this contact. - name: Option = "name" => optional_empty, + name: OptionEmpty = "name", /// Subscription status of this contact. - subscription: Subscription = "subscription" => default, + subscription: Default = "subscription", /// Indicates “Pending Out” sub-states for this contact. - ask: Ask = "ask" => default, + ask: Default = "ask", ], children: [ @@ -77,7 +77,7 @@ generate_element!( /// This is an opaque string that should only be sent back to the server on /// a new connection, if this client is storing the contact list between /// connections. - ver: Option = "ver" => optional + ver: Option = "ver" ], children: [ /// List of the contacts of the user. diff --git a/src/sasl.rs b/src/sasl.rs index 8f63f092a937ba38e77b2984c04f239a0bd8b745..9d35072e1c25b0bf4709c42e3f9ea4fc53432db1 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -49,7 +49,7 @@ generate_element!( Auth, "auth", SASL, attributes: [ /// The mechanism used. - mechanism: Mechanism = "mechanism" => required + mechanism: Required = "mechanism" ], text: ( /// The content of the handshake. diff --git a/src/sm.rs b/src/sm.rs index 1f48615e30da58c541955deade8c912e86bc04de..661d05b9d0c3f5f73e30a2a47cd8787f7dc84b28 100644 --- a/src/sm.rs +++ b/src/sm.rs @@ -11,7 +11,7 @@ generate_element!( A, "a", SM, attributes: [ /// The last handled stanza. - h: u32 = "h" => required, + h: Required = "h", ] ); @@ -36,10 +36,10 @@ generate_element!( attributes: [ /// The preferred resumption time in seconds by the client. // TODO: should be the infinite integer set ≥ 1. - max: Option = "max" => optional, + max: Option = "max", /// Whether the client wants to be allowed to resume the stream. - resume: ResumeAttr = "resume" => default, + resume: Default = "resume", ] ); @@ -72,18 +72,18 @@ generate_element!( Enabled, "enabled", SM, attributes: [ /// A random identifier used for stream resumption. - id: Option = "id" => optional, + id: Option = "id", /// The preferred IP, domain, IP:port or domain:port location for /// resumption. - location: Option = "location" => optional, + location: Option = "location", /// The preferred resumption time in seconds by the server. // TODO: should be the infinite integer set ≥ 1. - max: Option = "max" => optional, + max: Option = "max", /// Whether stream resumption is allowed. - resume: ResumeAttr = "resume" => default, + resume: Default = "resume", ] ); @@ -92,7 +92,7 @@ generate_element!( Failed, "failed", SM, attributes: [ /// The last handled stanza. - h: Option = "h" => optional, + h: Option = "h", ], children: [ /// The error returned. @@ -113,11 +113,11 @@ generate_element!( Resume, "resume", SM, attributes: [ /// The last handled stanza. - h: u32 = "h" => required, + h: Required = "h", /// The previous id given by the server on /// [enabled](struct.Enabled.html). - previd: StreamId = "previd" => required, + previd: Required = "previd", ] ); @@ -126,11 +126,11 @@ generate_element!( Resumed, "resumed", SM, attributes: [ /// The last handled stanza. - h: u32 = "h" => required, + h: Required = "h", /// The previous id given by the server on /// [enabled](struct.Enabled.html). - previd: StreamId = "previd" => required, + previd: Required = "previd", ] ); diff --git a/src/stanza_id.rs b/src/stanza_id.rs index c9cefad724a1c8583bfdc908a56d777068ab915b..dd8ed9e0a125044eb9587f5364793730f1a12127 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -13,10 +13,10 @@ generate_element!( StanzaId, "stanza-id", SID, attributes: [ /// The id associated to this stanza by another entity. - id: String = "id" => required, + id: Required = "id", /// The entity who stamped this stanza-id. - by: Jid = "by" => required, + by: Required = "by", ] ); @@ -28,7 +28,7 @@ generate_element!( OriginId, "origin-id", SID, attributes: [ /// The id this client set for this stanza. - id: String = "id" => required, + id: Required = "id", ] ); diff --git a/src/stream.rs b/src/stream.rs index fa416fd80dd7e361169cd909eb175e3a720de6f4..a77a73ea8ca36f59a55df520e085aa29cf9dff85 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -11,20 +11,20 @@ generate_element!( Stream, "stream", STREAM, attributes: [ /// The JID of the entity opening this stream. - from: Option = "from" => optional, + from: Option = "from", /// The JID of the entity receiving this stream opening. - to: Option = "to" => optional, + to: Option = "to", /// The id of the stream, used for authentication challenges. - id: Option = "id" => optional, + id: Option = "id", /// The XMPP version used during this stream. - version: Option = "version" => optional, + version: Option = "version", /// The default human language for all subsequent stanzas, which will /// be transmitted to other entities for better localisation. - xml_lang: Option = "xml:lang" => optional, + xml_lang: Option = "xml:lang", ] ); diff --git a/src/util/macros.rs b/src/util/macros.rs index 0c296abba07e94e05d7fb32db3b7dd749ea5a0a8..74e4009f93471cfdda0112adc51e5a47371bf27e 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -8,20 +8,20 @@ macro_rules! get_attr { ($elem:ident, $attr:tt, $type:tt) => { get_attr!($elem, $attr, $type, value, value.parse()?) }; - ($elem:ident, $attr:tt, optional_empty, $value:ident, $func:expr) => { + ($elem:ident, $attr:tt, OptionEmpty, $value:ident, $func:expr) => { match $elem.attr($attr) { Some("") => None, Some($value) => Some($func), None => None, } }; - ($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => { + ($elem:ident, $attr:tt, Option, $value:ident, $func:expr) => { match $elem.attr($attr) { Some($value) => Some($func), None => None, } }; - ($elem:ident, $attr:tt, required, $value:ident, $func:expr) => { + ($elem:ident, $attr:tt, Required, $value:ident, $func:expr) => { match $elem.attr($attr) { Some($value) => $func, None => { @@ -33,12 +33,22 @@ macro_rules! get_attr { } } }; - ($elem:ident, $attr:tt, default, $value:ident, $func:expr) => { + ($elem:ident, $attr:tt, Default, $value:ident, $func:expr) => { match $elem.attr($attr) { Some($value) => $func, None => ::std::default::Default::default(), } }; + // The remaining ones are only for backwards-compatibility. + ($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => { + get_attr!($elem, $attr, Option, $value, $func) + }; + ($elem:ident, $attr:tt, required, $value:ident, $func:expr) => { + get_attr!($elem, $attr, Required, $value, $func) + }; + ($elem:ident, $attr:tt, default, $value:ident, $func:expr) => { + get_attr!($elem, $attr, Default, $value, $func) + }; } macro_rules! generate_attribute { @@ -398,6 +408,21 @@ macro_rules! generate_elem_id { ); } +macro_rules! decl_attr { + (OptionEmpty, $type:ty) => ( + Option<$type> + ); + (Option, $type:ty) => ( + Option<$type> + ); + (Required, $type:ty) => ( + $type + ); + (Default, $type:ty) => ( + $type + ); +} + macro_rules! start_decl { (Vec, $type:ty) => ( Vec<$type> @@ -503,31 +528,31 @@ macro_rules! generate_serialiser { } macro_rules! generate_element { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => ( - generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),+,]) => ( + generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: []); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => ( - generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),+]) => ( + generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: []); ); ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => ( generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => ( - generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => ( + generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]); ); ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >)) => ( generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [], text: ($(#[$text_meta])* $text_ident: $codec<$text_type>)); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+], text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >)) => ( - generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [], text: ($(#[$text_meta])* $text_ident: $codec<$text_type>)); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),+], text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >)) => ( + generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: [], text: ($(#[$text_meta])* $text_ident: $codec<$text_type>)); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*] $(, text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >))*) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*] $(, text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >))*) => ( $(#[$meta])* #[derive(Debug, Clone)] pub struct $elem { $( $(#[$attr_meta])* - pub $attr: $attr_type, + pub $attr: decl_attr!($attr_action, $attr_type), )* $( $(#[$child_meta])* diff --git a/src/websocket.rs b/src/websocket.rs index 10693ca7d4bee4f9c6278e65c5a8815791a0123b..03a1d0401b97103d42e81ba45f10f110dae3bea5 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -11,20 +11,20 @@ generate_element!( Open, "open", WEBSOCKET, attributes: [ /// The JID of the entity opening this stream. - from: Option = "from" => optional, + from: Option = "from", /// The JID of the entity receiving this stream opening. - to: Option = "to" => optional, + to: Option = "to", /// The id of the stream, used for authentication challenges. - id: Option = "id" => optional, + id: Option = "id", /// The XMPP version used during this stream. - version: Option = "version" => optional, + version: Option = "version", /// The default human language for all subsequent stanzas, which will /// be transmitted to other entities for better localisation. - xml_lang: Option = "xml:lang" => optional, + xml_lang: Option = "xml:lang", ] ); From ae3a5b67252f870362716d7112b2dbc52d21cd0d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 24 Feb 2019 20:48:19 +0100 Subject: [PATCH 589/698] macros: Remove backwards-compatibility variants introduced in the previous commit. --- src/blocking.rs | 2 +- src/caps.rs | 8 ++++---- src/data_forms.rs | 8 ++++---- src/disco.rs | 10 +++++----- src/iq.rs | 8 ++++---- src/jingle.rs | 8 ++++---- src/jingle_ft.rs | 6 +++--- src/jingle_message.rs | 2 +- src/jingle_s5b.rs | 10 +++++----- src/mam.rs | 2 +- src/message.rs | 12 ++++++------ src/muc/user.rs | 4 ++-- src/presence.rs | 10 +++++----- src/pubsub/event.rs | 14 +++++++------- src/rsm.rs | 2 +- src/sasl.rs | 2 +- src/stanza_error.rs | 6 +++--- src/util/macros.rs | 16 +++------------- 18 files changed, 60 insertions(+), 70 deletions(-) diff --git a/src/blocking.rs b/src/blocking.rs index 2b021185995c8427f28bcf1c921a780a60f57076..74e84bc6f46d44921c832c643f4d702ce694a4ad 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -41,7 +41,7 @@ macro_rules! generate_blocking_element { check_self!(child, "item", BLOCKING); check_no_unknown_attributes!(child, "item", ["jid"]); check_no_children!(child, "item"); - items.push(get_attr!(child, "jid", required)); + items.push(get_attr!(child, "jid", Required)); } Ok($elem { items }) } diff --git a/src/caps.rs b/src/caps.rs index 1a0d57694c2a51430ef61e6f8d92f01565a03b1a..452fa6d56acfd8920e73916a0eab3a101f57db14 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -46,14 +46,14 @@ impl TryFrom for Caps { check_self!(elem, "c", CAPS, "caps"); check_no_children!(elem, "caps"); check_no_unknown_attributes!(elem, "caps", ["hash", "ver", "ext", "node"]); - let ver: String = get_attr!(elem, "ver", required); + let ver: String = get_attr!(elem, "ver", Required); let hash = Hash { - algo: get_attr!(elem, "hash", required), + algo: get_attr!(elem, "hash", Required), hash: base64::decode(&ver)?, }; Ok(Caps { - ext: get_attr!(elem, "ext", optional), - node: get_attr!(elem, "node", required), + ext: get_attr!(elem, "ext", Option), + node: get_attr!(elem, "node", Required), hash, }) } diff --git a/src/data_forms.rs b/src/data_forms.rs index 9a1ed3e3297556ebd26cabe8be938fb64b97c8f7..296614e975dfd118707a93e2c0d9e45e8ccf742f 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -106,9 +106,9 @@ impl TryFrom for Field { check_self!(elem, "field", DATA_FORMS); check_no_unknown_attributes!(elem, "field", ["label", "type", "var"]); let mut field = Field { - var: get_attr!(elem, "var", required), - type_: get_attr!(elem, "type", default), - label: get_attr!(elem, "label", optional), + var: get_attr!(elem, "var", Required), + type_: get_attr!(elem, "type", Default), + label: get_attr!(elem, "label", Option), required: false, options: vec![], values: vec![], @@ -220,7 +220,7 @@ impl TryFrom for DataForm { fn try_from(elem: Element) -> Result { check_self!(elem, "x", DATA_FORMS); check_no_unknown_attributes!(elem, "x", ["type"]); - let type_ = get_attr!(elem, "type", required); + let type_ = get_attr!(elem, "type", Required); let mut form = DataForm { type_, form_type: None, diff --git a/src/disco.rs b/src/disco.rs index 43c15903365dc900188ac0acc2801cc24ed2a1de..0acdcb86c30bf391f41f2cbc8e5e1b5e5309d67a 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -71,14 +71,14 @@ impl TryFrom for Identity { ["category", "type", "xml:lang", "name"] ); - let category = get_attr!(elem, "category", required); + let category = get_attr!(elem, "category", Required); if category == "" { return Err(Error::ParseError( "Identity must have a non-empty 'category' attribute.", )); } - let type_ = get_attr!(elem, "type", required); + let type_ = get_attr!(elem, "type", Required); if type_ == "" { return Err(Error::ParseError( "Identity must have a non-empty 'type' attribute.", @@ -88,8 +88,8 @@ impl TryFrom for Identity { Ok(Identity { category, type_, - lang: get_attr!(elem, "xml:lang", optional), - name: get_attr!(elem, "name", optional), + lang: get_attr!(elem, "xml:lang", Option), + name: get_attr!(elem, "name", Option), }) } } @@ -135,7 +135,7 @@ impl TryFrom for DiscoInfoResult { check_no_unknown_attributes!(elem, "disco#info result", ["node"]); let mut result = DiscoInfoResult { - node: get_attr!(elem, "node", optional), + node: get_attr!(elem, "node", Option), identities: vec![], features: vec![], extensions: vec![], diff --git a/src/iq.rs b/src/iq.rs index dd04e6b8f9d2c385ff25e2460583c59cd5df43cc..e62a4cd839c71af8d7d575cce4607d6cabbf7f35 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -134,10 +134,10 @@ impl TryFrom for Iq { fn try_from(root: Element) -> Result { check_self!(root, "iq", DEFAULT_NS); - let from = get_attr!(root, "from", optional); - let to = get_attr!(root, "to", optional); - let id = get_attr!(root, "id", required); - let type_: String = get_attr!(root, "type", required); + let from = get_attr!(root, "from", Option); + let to = get_attr!(root, "to", Option); + let id = get_attr!(root, "id", Required); + let type_: String = get_attr!(root, "type", Required); let mut payload = None; let mut error_payload = None; diff --git a/src/jingle.rs b/src/jingle.rs index b891ab7caea1b7a2d7fbae352fd8ade108b645b4..2c6c3fb896b2907d80b31a75f158450cc2b8448e 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -492,10 +492,10 @@ impl TryFrom for Jingle { check_no_unknown_attributes!(root, "Jingle", ["action", "initiator", "responder", "sid"]); let mut jingle = Jingle { - action: get_attr!(root, "action", required), - initiator: get_attr!(root, "initiator", optional), - responder: get_attr!(root, "responder", optional), - sid: get_attr!(root, "sid", required), + action: get_attr!(root, "action", Required), + initiator: get_attr!(root, "initiator", Option), + responder: get_attr!(root, "responder", Option), + sid: get_attr!(root, "sid", Required), contents: vec![], reason: None, other: vec![], diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 67c055e23290eed991eb0a0add4a68761c123ccc..3cd9e00abe7fdc94bc94ea778b36c40b1e812eb6 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -163,7 +163,7 @@ impl TryFrom for File { } file.name = Some(child.text()); } else if child.is("desc", ns::JINGLE_FT) { - let lang = get_attr!(child, "xml:lang", default); + let lang = get_attr!(child, "xml:lang", Default); let desc = Desc(child.text()); if file.descs.insert(lang, desc).is_some() { return Err(Error::ParseError( @@ -321,8 +321,8 @@ impl TryFrom for Checksum { )); } Ok(Checksum { - name: get_attr!(elem, "name", required), - creator: get_attr!(elem, "creator", required), + name: get_attr!(elem, "name", Required), + creator: get_attr!(elem, "creator", Required), file: file.unwrap(), }) } diff --git a/src/jingle_message.rs b/src/jingle_message.rs index 1a520223bcfadfa680e3526435d2c7db0b40724d..8561a649b792a7d422ca10cb7380d6a2aafdf38a 100644 --- a/src/jingle_message.rs +++ b/src/jingle_message.rs @@ -39,7 +39,7 @@ pub enum JingleMI { fn get_sid(elem: Element) -> Result { check_no_unknown_attributes!(elem, "Jingle message", ["id"]); - Ok(SessionId(get_attr!(elem, "id", required))) + Ok(SessionId(get_attr!(elem, "id", Required))) } fn check_empty_and_get_sid(elem: Element) -> Result { diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 1f3a3ee60fb3b7f67c3cbc472bd00237a333fe6e..02b97361352b517dc471c7e1816826370a9ab98a 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -177,9 +177,9 @@ impl TryFrom for Transport { fn try_from(elem: Element) -> Result { check_self!(elem, "transport", JINGLE_S5B); check_no_unknown_attributes!(elem, "transport", ["sid", "dstaddr", "mode"]); - let sid = get_attr!(elem, "sid", required); - let dstaddr = get_attr!(elem, "dstaddr", optional); - let mode = get_attr!(elem, "mode", default); + let sid = get_attr!(elem, "sid", Required); + let dstaddr = get_attr!(elem, "dstaddr", Option); + let mode = get_attr!(elem, "mode", Default); let mut payload = None; for child in elem.children() { @@ -200,7 +200,7 @@ impl TryFrom for Transport { "Non-activated child already present in JingleS5B transport element.", )); } - let cid = get_attr!(child, "cid", required); + let cid = get_attr!(child, "cid", Required); TransportPayload::Activated(cid) } else if child.is("candidate-error", ns::JINGLE_S5B) { if payload.is_some() { @@ -215,7 +215,7 @@ impl TryFrom for Transport { "Non-candidate-used child already present in JingleS5B transport element.", )); } - let cid = get_attr!(child, "cid", required); + let cid = get_attr!(child, "cid", Required); TransportPayload::CandidateUsed(cid) } else if child.is("proxy-error", ns::JINGLE_S5B) { if payload.is_some() { diff --git a/src/mam.rs b/src/mam.rs index b68dabb272b8052bd173da25e7cb40fa38af7950..cc606923f19bc15fb9a2f59a658f6ff5c90f3386 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -152,7 +152,7 @@ impl TryFrom for Prefs { return Err(Error::ParseError("Unknown child in prefs element.")); } } - let default_ = get_attr!(elem, "default", required); + let default_ = get_attr!(elem, "default", Required); Ok(Prefs { default_, always, diff --git a/src/message.rs b/src/message.rs index f2611accd1a2ee40c6412854593ed37c62928b28..29708c32e1d2e710ca27eda4790b6b0f6af7c0ac 100644 --- a/src/message.rs +++ b/src/message.rs @@ -154,10 +154,10 @@ impl TryFrom for Message { fn try_from(root: Element) -> Result { check_self!(root, "message", DEFAULT_NS); - let from = get_attr!(root, "from", optional); - let to = get_attr!(root, "to", optional); - let id = get_attr!(root, "id", optional); - let type_ = get_attr!(root, "type", default); + let from = get_attr!(root, "from", Option); + let to = get_attr!(root, "to", Option); + let id = get_attr!(root, "id", Option); + let type_ = get_attr!(root, "type", Default); let mut bodies = BTreeMap::new(); let mut subjects = BTreeMap::new(); let mut thread = None; @@ -165,7 +165,7 @@ impl TryFrom for Message { for elem in root.children() { if elem.is("body", ns::DEFAULT_NS) { check_no_children!(elem, "body"); - let lang = get_attr!(elem, "xml:lang", default); + let lang = get_attr!(elem, "xml:lang", Default); let body = Body(elem.text()); if bodies.insert(lang, body).is_some() { return Err(Error::ParseError( @@ -174,7 +174,7 @@ impl TryFrom for Message { } } else if elem.is("subject", ns::DEFAULT_NS) { check_no_children!(elem, "subject"); - let lang = get_attr!(elem, "xml:lang", default); + let lang = get_attr!(elem, "xml:lang", Default); let subject = Subject(elem.text()); if subjects.insert(lang, subject).is_some() { return Err(Error::ParseError( diff --git a/src/muc/user.rs b/src/muc/user.rs index 73e910ae1bcccb82890fe7d9637756ec6d53486c..8965529fa9486a7629472c188634afb731fd8fa0 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -95,8 +95,8 @@ impl TryFrom for Actor { check_self!(elem, "actor", MUC_USER); check_no_unknown_attributes!(elem, "actor", ["jid", "nick"]); check_no_children!(elem, "actor"); - let jid: Option = get_attr!(elem, "jid", optional); - let nick = get_attr!(elem, "nick", optional); + let jid: Option = get_attr!(elem, "jid", Option); + let nick = get_attr!(elem, "nick", Option); match (jid, nick) { (Some(_), Some(_)) | (None, None) => { diff --git a/src/presence.rs b/src/presence.rs index e7d2a55df7c6b0f5d80d0f38b488c236c9677d6c..2bf09d04df224d48f538ed651c018aec7dc811d0 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -263,10 +263,10 @@ impl TryFrom for Presence { let mut show = None; let mut priority = None; let mut presence = Presence { - from: get_attr!(root, "from", optional), - to: get_attr!(root, "to", optional), - id: get_attr!(root, "id", optional), - type_: get_attr!(root, "type", default), + from: get_attr!(root, "from", Option), + to: get_attr!(root, "to", Option), + id: get_attr!(root, "id", Option), + type_: get_attr!(root, "type", Default), show: Show::None, statuses: BTreeMap::new(), priority: 0i8, @@ -285,7 +285,7 @@ impl TryFrom for Presence { } else if elem.is("status", ns::DEFAULT_NS) { check_no_unknown_attributes!(elem, "status", ["xml:lang"]); check_no_children!(elem, "status"); - let lang = get_attr!(elem, "xml:lang", default); + let lang = get_attr!(elem, "xml:lang", Default); if presence.statuses.insert(lang, elem.text()).is_some() { return Err(Error::ParseError( "Status element present twice for the same xml:lang.", diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 0c616ab64225b2a5c894ad42e481ae8c425840c2..8f6951d9e67081f62ffbfe630553868021b0d2f7 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -115,7 +115,7 @@ fn parse_items(elem: Element, node: NodeName) -> Result { } check_no_children!(child, "retract"); check_no_unknown_attributes!(child, "retract", ["id"]); - let id = get_attr!(child, "id", required); + let id = get_attr!(child, "id", Required); retracts.push(id); } else { return Err(Error::ParseError("Invalid child in items element.")); @@ -140,7 +140,7 @@ impl TryFrom for PubSubEvent { let mut payload = None; for child in elem.children() { - let node = get_attr!(child, "node", required); + let node = get_attr!(child, "node", Required); if child.is("configuration", ns::PUBSUB_EVENT) { let mut payloads = child.children().cloned().collect::>(); let item = payloads.pop(); @@ -163,7 +163,7 @@ impl TryFrom for PubSubEvent { "More than one redirect in delete element.", )); } - let uri = get_attr!(item, "uri", required); + let uri = get_attr!(item, "uri", Required); redirect = Some(uri); } else { return Err(Error::ParseError("Unknown child in delete element.")); @@ -179,10 +179,10 @@ impl TryFrom for PubSubEvent { check_no_children!(child, "subscription"); payload = Some(PubSubEvent::Subscription { node, - expiry: get_attr!(child, "expiry", optional), - jid: get_attr!(child, "jid", optional), - subid: get_attr!(child, "subid", optional), - subscription: get_attr!(child, "subscription", optional), + expiry: get_attr!(child, "expiry", Option), + jid: get_attr!(child, "jid", Option), + subid: get_attr!(child, "subid", Option), + subscription: get_attr!(child, "subscription", Option), }); } else { return Err(Error::ParseError("Unknown child in event element.")); diff --git a/src/rsm.rs b/src/rsm.rs index 1dd5f47f71ccfd4c0d93971379a1385b1b6a6aa6..6b65e3b48d9cda6f193fb96005293f0cd4d46f1d 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -131,7 +131,7 @@ impl TryFrom for SetResult { if set.first.is_some() { return Err(Error::ParseError("Set can’t have more than one first.")); } - set.first_index = get_attr!(child, "index", optional); + set.first_index = get_attr!(child, "index", Option); set.first = Some(child.text()); } else if child.is("last", ns::RSM) { if set.last.is_some() { diff --git a/src/sasl.rs b/src/sasl.rs index 9d35072e1c25b0bf4709c42e3f9ea4fc53432db1..ec84fe6cb99df44fa0ea8cf9b76111239eca044b 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -164,7 +164,7 @@ impl TryFrom for Failure { if child.is("text", ns::SASL) { check_no_unknown_attributes!(child, "text", ["xml:lang"]); check_no_children!(child, "text"); - let lang = get_attr!(child, "xml:lang", default); + let lang = get_attr!(child, "xml:lang", Default); if texts.insert(lang, child.text()).is_some() { return Err(Error::ParseError( "Text element present twice for the same xml:lang in failure element.", diff --git a/src/stanza_error.rs b/src/stanza_error.rs index d254f6b48d9a0e17c9f8fdd8d97f294a557056f6..3488b9c13c96a9007b69592496744a1163b3cf5a 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -221,8 +221,8 @@ impl TryFrom for StanzaError { fn try_from(elem: Element) -> Result { check_self!(elem, "error", DEFAULT_NS); - let type_ = get_attr!(elem, "type", required); - let by = get_attr!(elem, "by", optional); + let type_ = get_attr!(elem, "type", Required); + let by = get_attr!(elem, "by", Option); let mut defined_condition = None; let mut texts = BTreeMap::new(); let mut other = None; @@ -230,7 +230,7 @@ impl TryFrom for StanzaError { for child in elem.children() { if child.is("text", ns::XMPP_STANZAS) { check_no_children!(child, "text"); - let lang = get_attr!(elem, "xml:lang", default); + let lang = get_attr!(elem, "xml:lang", Default); if texts.insert(lang, child.text()).is_some() { return Err(Error::ParseError( "Text element present twice for the same xml:lang.", diff --git a/src/util/macros.rs b/src/util/macros.rs index 74e4009f93471cfdda0112adc51e5a47371bf27e..a67afe1f835818f220e1b6d5529f61d2313e00b9 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -39,16 +39,6 @@ macro_rules! get_attr { None => ::std::default::Default::default(), } }; - // The remaining ones are only for backwards-compatibility. - ($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => { - get_attr!($elem, $attr, Option, $value, $func) - }; - ($elem:ident, $attr:tt, required, $value:ident, $func:expr) => { - get_attr!($elem, $attr, Required, $value, $func) - }; - ($elem:ident, $attr:tt, default, $value:ident, $func:expr) => { - get_attr!($elem, $attr, Default, $value, $func) - }; } macro_rules! generate_attribute { @@ -242,7 +232,7 @@ macro_rules! generate_attribute_enum { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); check_no_unknown_attributes!(elem, $name, [$attr]); - Ok(match get_attr!(elem, $attr, required) { + Ok(match get_attr!(elem, $attr, Required) { $($enum_name => $elem::$enum,)+ _ => return Err(crate::util::error::Error::ParseError(concat!("Invalid ", $name, " ", $attr, " value."))), }) @@ -639,8 +629,8 @@ macro_rules! impl_pubsub_item { )); } Ok($item(crate::pubsub::Item { - id: get_attr!(elem, "id", optional), - publisher: get_attr!(elem, "publisher", optional), + id: get_attr!(elem, "id", Option), + publisher: get_attr!(elem, "publisher", Option), payload, })) } From 82eda09ca5a54e37b072449901ba95e96c78fb61 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 26 Feb 2019 19:25:43 +0100 Subject: [PATCH 590/698] =?UTF-8?q?iq:=20Allow=20any=20Into=20for?= =?UTF-8?q?=20the=20constructors=E2=80=99=20id.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/iq.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index e62a4cd839c71af8d7d575cce4607d6cabbf7f35..bca785e90af177b7f351c55ea368056ce3913335 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -71,41 +71,41 @@ pub struct Iq { impl Iq { /// Creates an `` stanza containing a get request. - pub fn from_get(id: String, payload: impl IqGetPayload) -> Iq { + pub fn from_get>(id: S, payload: impl IqGetPayload) -> Iq { Iq { from: None, to: None, - id, + id: id.into(), payload: IqType::Get(payload.into()), } } /// Creates an `` stanza containing a set request. - pub fn from_set(id: String, payload: impl IqSetPayload) -> Iq { + pub fn from_set>(id: S, payload: impl IqSetPayload) -> Iq { Iq { from: None, to: None, - id, + id: id.into(), payload: IqType::Set(payload.into()), } } /// Creates an `` stanza containing a result. - pub fn from_result(id: String, payload: Option) -> Iq { + pub fn from_result>(id: S, payload: Option) -> Iq { Iq { from: None, to: None, - id, + id: id.into(), payload: IqType::Result(payload.map(Into::into)), } } /// Creates an `` stanza containing an error. - pub fn from_error(id: String, payload: StanzaError) -> Iq { + pub fn from_error>(id: S, payload: StanzaError) -> Iq { Iq { from: None, to: None, - id, + id: id.into(), payload: IqType::Error(payload), } } From dd80f55c5f5bcdfa52e3a2da9bbe262c64efa9d4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 26 Feb 2019 19:43:29 +0100 Subject: [PATCH 591/698] =?UTF-8?q?disco:=20Add=20constructors=20for=20Ide?= =?UTF-8?q?ntity,=20and=20fix=20Feature=E2=80=99s.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/disco.rs | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index 0acdcb86c30bf391f41f2cbc8e5e1b5e5309d67a..b958cce573ee03f96675fc6b16b238d82cb61d0f 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -36,9 +36,9 @@ attributes: [ impl Feature { /// Create a new `` with the according `@var`. - pub fn new(var: &str) -> Feature { + pub fn new>(var: S) -> Feature { Feature { - var: String::from(var) + var: var.into(), } } } @@ -59,6 +59,36 @@ pub struct Identity { pub name: Option, } +impl Identity { + /// Create a new ``. + pub fn new(category: C, type_: T, lang: L, name: N) -> Identity + where C: Into, + T: Into, + L: Into, + N: Into, + { + Identity { + category: category.into(), + type_: type_.into(), + lang: Some(lang.into()), + name: Some(name.into()), + } + } + + /// Create a new `` without a name. + pub fn new_anonymous(category: C, type_: T) -> Identity + where C: Into, + T: Into, + { + Identity { + category: category.into(), + type_: type_.into(), + lang: None, + name: None, + } + } +} + impl TryFrom for Identity { type Err = Error; From ab28824b1a653a2a1052db8ccafca374e42ca87d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 26 Feb 2019 19:51:41 +0100 Subject: [PATCH 592/698] presence: Simplify Presence::set_status with Into. --- src/presence.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index 2bf09d04df224d48f538ed651c018aec7dc811d0..fba1773c917fca83a3f6c3fc2738f738873b26cc 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -245,8 +245,11 @@ impl Presence { } /// Set the availability information of this presence. - pub fn set_status(&mut self, lang: Lang, status: Status) { - self.statuses.insert(lang, status); + pub fn set_status(&mut self, lang: L, status: S) + where L: Into, + S: Into, + { + self.statuses.insert(lang.into(), status.into()); } /// Add a payload to this presence. From 017fb0fbd1d4c803c865d2abd23e7305bcceeb74 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 26 Feb 2019 20:21:26 +0100 Subject: [PATCH 593/698] stanza_error: Add a constructor. --- src/stanza_error.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 3488b9c13c96a9007b69592496744a1163b3cf5a..4c09eea792bedf03dbd0e18c996d3bf23f60d03f 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -215,6 +215,26 @@ pub struct StanzaError { impl MessagePayload for StanzaError {} impl PresencePayload for StanzaError {} +impl StanzaError { + /// Create a new `` with the according content. + pub fn new(type_: ErrorType, defined_condition: DefinedCondition, lang: L, text: T) -> StanzaError + where L: Into, + T: Into, + { + StanzaError { + type_, + by: None, + defined_condition, + texts: { + let mut map = BTreeMap::new(); + map.insert(lang.into(), text.into()); + map + }, + other: None, + } + } +} + impl TryFrom for StanzaError { type Err = Error; From 40d397c1fe32dd929998b22ad97be798ff97d0ed Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 27 Feb 2019 18:13:06 +0100 Subject: [PATCH 594/698] jingle_rtp: Add a new parser/serialiser for XEP-0167. --- src/jingle_rtp.rs | 141 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 + src/ns.rs | 7 +++ 3 files changed, 151 insertions(+) create mode 100644 src/jingle_rtp.rs diff --git a/src/jingle_rtp.rs b/src/jingle_rtp.rs new file mode 100644 index 0000000000000000000000000000000000000000..1ab50d4d325446993488378162c3a251a2111756 --- /dev/null +++ b/src/jingle_rtp.rs @@ -0,0 +1,141 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::num::ParseIntError; +use std::str::FromStr; +use minidom::IntoAttributeValue; + +generate_element!( + /// Wrapper element describing an RTP session. + Description, "description", JINGLE_RTP, + attributes: [ + /// Namespace of the encryption scheme used. + media: Required = "media", + + /// User-friendly name for the encryption scheme, should be `None` for OTR, + /// legacy OpenPGP and OX. + // XXX: is this a String or an u32?! Refer to RFC 3550. + ssrc: Option = "ssrc", + ], + children: [ + /// List of encodings that can be used for this RTP stream. + payload_types: Vec = ("payload-type", JINGLE_RTP) => PayloadType + + // TODO: Add support for and . + ] +); + +/// The number of channels. +#[derive(Debug, Clone)] +pub struct Channels(pub u8); + +impl Default for Channels { + fn default() -> Channels { + Channels(1) + } +} + +impl FromStr for Channels { + type Err = ParseIntError; + + fn from_str(s: &str) -> Result { + Ok(Channels(u8::from_str(s)?)) + } +} + +impl IntoAttributeValue for Channels { + fn into_attribute_value(self) -> Option { + if self.0 == 1 { + None + } else { + Some(format!("{}", self.0)) + } + } +} + +generate_element!( + /// An encoding that can be used for an RTP stream. + PayloadType, "payload-type", JINGLE_RTP, + attributes: [ + /// The number of channels. + channels: Default = "channels", + + /// The sampling frequency in Hertz. + clockrate: Option = "clockrate", + + /// The payload identifier. + id: Required = "id", + + /// Maximum packet time as specified in RFC 4566. + maxptime: Option = "maxptime", + + /// The appropriate subtype of the MIME type. + name: Option = "name", + + /// Packet time as specified in RFC 4566. + ptime: Option = "ptime", + ], + children: [ + /// List of parameters specifying this payload-type. + /// + /// Their order MUST be ignored. + parameters: Vec = ("parameter", JINGLE_RTP) => Parameter + ] +); + +generate_element!( + /// Parameter related to a payload. + Parameter, "parameter", JINGLE_RTP, + attributes: [ + /// The name of the parameter, from the list at + /// https://www.iana.org/assignments/sdp-parameters/sdp-parameters.xhtml + name: Required = "name", + + /// The value of this parameter. + value: Required = "value", + ] +); + +#[cfg(test)] +mod tests { + use super::*; + use minidom::Element; + use try_from::TryFrom; + + #[test] + fn test_simple() { + let elem: Element = " + + + + + + + + + + + + + + + + + + + + + + + +" + .parse() + .unwrap(); + let desc = Description::try_from(elem).unwrap(); + assert_eq!(desc.media, "audio"); + assert_eq!(desc.ssrc, None); + } +} diff --git a/src/lib.rs b/src/lib.rs index 765c9a15cc14a641c05000eac73915ea4f28b949..773c120211c95ccc264b1393a80d5fd7bcea99c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -104,6 +104,9 @@ pub mod caps; /// XEP-0166: Jingle pub mod jingle; +/// XEP-0167: Jingle RTP Sessions +pub mod jingle_rtp; + /// XEP-0172: User Nickname pub mod nick; diff --git a/src/ns.rs b/src/ns.rs index 7aa4768cfa4a85bfc061d59368cf0640c7494e49..f825d4dca701baff75dfd1830ba1fc7f58916bdf 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -82,6 +82,13 @@ pub const CAPS: &str = "http://jabber.org/protocol/caps"; /// XEP-0166: Jingle pub const JINGLE: &str = "urn:xmpp:jingle:1"; +/// XEP-0167: Jingle RTP Sessions +pub const JINGLE_RTP: &str = "urn:xmpp:jingle:apps:rtp:1"; +/// XEP-0167: Jingle RTP Sessions +pub const JINGLE_RTP_AUDIO: &str = "urn:xmpp:jingle:apps:rtp:audio"; +/// XEP-0167: Jingle RTP Sessions +pub const JINGLE_RTP_VIDEO: &str = "urn:xmpp:jingle:apps:rtp:video"; + /// XEP-0172: User Nickname pub const NICK: &str = "http://jabber.org/protocol/nick"; From beacbaeb86cf28f57f8cae956416bcf93c38b85e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 27 Feb 2019 18:13:18 +0100 Subject: [PATCH 595/698] jingle_ice_udp: Add a new parser/serialiser for XEP-0176. --- src/lib.rs | 3 +++ src/ns.rs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 773c120211c95ccc264b1393a80d5fd7bcea99c3..fa1153fee2d9301a465ca7e4db88f114d7ba981a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -110,6 +110,9 @@ pub mod jingle_rtp; /// XEP-0172: User Nickname pub mod nick; +/// XEP-0176: Jingle ICE-UDP Transport Method +pub mod jingle_ice_udp; + /// XEP-0184: Message Delivery Receipts pub mod receipts; diff --git a/src/ns.rs b/src/ns.rs index f825d4dca701baff75dfd1830ba1fc7f58916bdf..321c6fa8362ee14bc8a1035638350c90db2419f4 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -92,6 +92,9 @@ pub const JINGLE_RTP_VIDEO: &str = "urn:xmpp:jingle:apps:rtp:video"; /// XEP-0172: User Nickname pub const NICK: &str = "http://jabber.org/protocol/nick"; +/// XEP-0176: Jingle ICE-UDP Transport Method +pub const JINGLE_ICE_UDP: &str = "urn:xmpp:jingle:transports:ice-udp:1"; + /// XEP-0184: Message Delivery Receipts pub const RECEIPTS: &str = "urn:xmpp:receipts"; From f8aedb0da13bd34a30ef1784353840416e39e1da Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 27 Feb 2019 18:13:18 +0100 Subject: [PATCH 596/698] =?UTF-8?q?jingle=5Fice=5Fudp:=20Add=20missing=20f?= =?UTF-8?q?ile=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/jingle_ice_udp.rs | 122 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 src/jingle_ice_udp.rs diff --git a/src/jingle_ice_udp.rs b/src/jingle_ice_udp.rs new file mode 100644 index 0000000000000000000000000000000000000000..5713c2e66949b350ab5d4c9326cdeeb161f5c110 --- /dev/null +++ b/src/jingle_ice_udp.rs @@ -0,0 +1,122 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::net::IpAddr; + +generate_element!( + /// Wrapper element for an ICE-UDP transport. + Transport, "transport", JINGLE_ICE_UDP, + attributes: [ + /// A Password as defined in ICE-CORE. + pwd: Option = "pwd", + + /// A User Fragment as defined in ICE-CORE. + ufrag: Option = "ufrag", + ], + children: [ + /// List of candidates for this ICE-UDP session. + candidates: Vec = ("candidate", JINGLE_ICE_UDP) => Candidate + ] +); + +generate_attribute!( + /// A Candidate Type as defined in ICE-CORE. + Type, "type", { + /// Host candidate. + Host => "host", + + /// Peer reflexive candidate. + Prflx => "prflx", + + /// Relayed candidate. + Relay => "relay", + + /// Server reflexive candidate. + Srflx => "srflx", + } +); + +generate_element!( + /// A candidate for an ICE-UDP session. + Candidate, "candidate", JINGLE_ICE_UDP, + attributes: [ + /// A Component ID as defined in ICE-CORE. + component: Required = "component", + + /// A Foundation as defined in ICE-CORE. + foundation: Required = "foundation", + + /// An index, starting at 0, that enables the parties to keep track of updates to the + /// candidate throughout the life of the session. + generation: Required = "generation", + + /// A unique identifier for the candidate. + id: Required = "id", + + /// The Internet Protocol (IP) address for the candidate transport mechanism; this can be + /// either an IPv4 address or an IPv6 address. + ip: Required = "ip", + + /// An index, starting at 0, referencing which network this candidate is on for a given + /// peer. + network: Required = "network", + + /// The port at the candidate IP address. + port: Required = "port", + + /// A Priority as defined in ICE-CORE. + priority: Required = "priority", + + /// The protocol to be used. The only value defined by this specification is "udp". + protocol: Required = "protocol", + + /// A related address as defined in ICE-CORE. + rel_addr: Option = "rel-addr", + + /// A related port as defined in ICE-CORE. + rel_port: Option = "rel-port", + + /// A Candidate Type as defined in ICE-CORE. + type_: Required = "type", + ] +); + +#[cfg(test)] +mod tests { + use super::*; + use minidom::Element; + use try_from::TryFrom; + + #[test] + fn test_simple() { + let elem: Element = " + + + + + + + + + + + + + + + + + + + +" + .parse() + .unwrap(); + let transport = Transport::try_from(elem).unwrap(); + assert_eq!(transport.pwd.unwrap(), "wakMJ8Ydd5rqnPaFerws5o"); + assert_eq!(transport.ufrag.unwrap(), "aeXX"); + } +} From c4d867571e6d82751283308961e4c4a0ce692612 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 27 Feb 2019 23:27:40 +0100 Subject: [PATCH 597/698] Also reexport TryInto from try_from. --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index fa1153fee2d9301a465ca7e4db88f114d7ba981a..ca7840b165c34ebe920b7db035759f15aa06517b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,7 +26,7 @@ pub use minidom::Element; pub use jid::{Jid, JidParseError}; -pub use try_from::TryFrom; +pub use try_from::{TryFrom, TryInto}; pub use crate::util::error::Error; /// XML namespace definitions used through XMPP. From b56582c8b5983b40eb64686abe15194803d4b2fb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 28 Feb 2019 02:26:10 +0100 Subject: [PATCH 598/698] disco: Use a macro for . --- src/disco.rs | 79 +++++++++++----------------------------------- src/util/macros.rs | 22 +++++++++++++ 2 files changed, 40 insertions(+), 61 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index b958cce573ee03f96675fc6b16b238d82cb61d0f..87145208d96aeb819e913eb7ed8a7981665bf718 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -43,21 +43,25 @@ impl Feature { } } -/// Structure representing an `` element. -#[derive(Debug, Clone)] -pub struct Identity { - /// Category of this identity. - pub category: String, // TODO: use an enum here. +generate_element!( + /// Structure representing an `` element. + Identity, "identity", DISCO_INFO, + attributes: [ + /// Category of this identity. + // TODO: use an enum here. + category: RequiredNonEmpty = "category", - /// Type of this identity. - pub type_: String, // TODO: use an enum here. + /// Type of this identity. + // TODO: use an enum here. + type_: RequiredNonEmpty = "type", - /// Lang of the name of this identity. - pub lang: Option, + /// Lang of the name of this identity. + lang: Option = "xml:lang", - /// Name of this identity. - pub name: Option, -} + /// Name of this identity. + name: Option = "name", + ] +); impl Identity { /// Create a new ``. @@ -89,53 +93,6 @@ impl Identity { } } -impl TryFrom for Identity { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "identity", DISCO_INFO, "disco#info identity"); - check_no_children!(elem, "disco#info identity"); - check_no_unknown_attributes!( - elem, - "disco#info identity", - ["category", "type", "xml:lang", "name"] - ); - - let category = get_attr!(elem, "category", Required); - if category == "" { - return Err(Error::ParseError( - "Identity must have a non-empty 'category' attribute.", - )); - } - - let type_ = get_attr!(elem, "type", Required); - if type_ == "" { - return Err(Error::ParseError( - "Identity must have a non-empty 'type' attribute.", - )); - } - - Ok(Identity { - category, - type_, - lang: get_attr!(elem, "xml:lang", Option), - name: get_attr!(elem, "name", Option), - }) - } -} - -impl From for Element { - fn from(identity: Identity) -> Element { - Element::builder("identity") - .ns(ns::DISCO_INFO) - .attr("category", identity.category) - .attr("type", identity.type_) - .attr("xml:lang", identity.lang) - .attr("name", identity.name) - .build() - } -} - /// Structure representing a `` element. /// /// It should only be used in an ``, as it can only @@ -385,7 +342,7 @@ mod tests { }; assert_eq!( message, - "Identity must have a non-empty 'category' attribute." + "Required attribute 'category' must not be empty." ); let elem: Element = "".parse().unwrap(); @@ -402,7 +359,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Identity must have a non-empty 'type' attribute."); + assert_eq!(message, "Required attribute 'type' must not be empty."); } #[test] diff --git a/src/util/macros.rs b/src/util/macros.rs index a67afe1f835818f220e1b6d5529f61d2313e00b9..bc2b8342ea3a7503bbc23839b8cd63081485220d 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -33,6 +33,25 @@ macro_rules! get_attr { } } }; + ($elem:ident, $attr:tt, RequiredNonEmpty, $value:ident, $func:expr) => { + match $elem.attr($attr) { + Some("") => { + return Err(crate::util::error::Error::ParseError(concat!( + "Required attribute '", + $attr, + "' must not be empty." + ))); + }, + Some($value) => $func, + None => { + return Err(crate::util::error::Error::ParseError(concat!( + "Required attribute '", + $attr, + "' missing." + ))); + } + } + }; ($elem:ident, $attr:tt, Default, $value:ident, $func:expr) => { match $elem.attr($attr) { Some($value) => $func, @@ -408,6 +427,9 @@ macro_rules! decl_attr { (Required, $type:ty) => ( $type ); + (RequiredNonEmpty, $type:ty) => ( + $type + ); (Default, $type:ty) => ( $type ); From 2b9a6d57b63538b3e6d3627d92cfc995a89362c0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 28 Feb 2019 02:40:04 +0100 Subject: [PATCH 599/698] jingle: Support more than one with different @xml:lang. --- src/jingle.rs | 62 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index 2c6c3fb896b2907d80b31a75f158450cc2b8448e..9edb318ff43b5903366fc0b1ee7c7186ac708c72 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -9,6 +9,7 @@ use crate::iq::IqSetPayload; use crate::ns; use jid::Jid; use minidom::Element; +use std::collections::BTreeMap; use std::str::FromStr; use try_from::TryFrom; @@ -354,6 +355,8 @@ impl From for Element { } } +type Lang = String; + /// Informs the recipient of something. #[derive(Debug, Clone)] pub struct ReasonElement { @@ -361,7 +364,7 @@ pub struct ReasonElement { pub reason: Reason, /// A human-readable description of this reason. - pub text: Option, + pub texts: BTreeMap, } impl TryFrom for ReasonElement { @@ -369,29 +372,30 @@ impl TryFrom for ReasonElement { fn try_from(elem: Element) -> Result { check_self!(elem, "reason", JINGLE); + check_no_attributes!(elem, "reason"); let mut reason = None; - let mut text = None; + let mut texts = BTreeMap::new(); for child in elem.children() { - if !child.has_ns(ns::JINGLE) { - return Err(Error::ParseError("Reason contains a foreign element.")); - } - match child.name() { - "text" => { - if text.is_some() { - return Err(Error::ParseError( - "Reason must not have more than one text.", - )); - } - text = Some(child.text()); + if child.is("text", ns::JINGLE) { + check_no_children!(child, "text"); + check_no_unknown_attributes!(child, "text", ["xml:lang"]); + let lang = get_attr!(elem, "xml:lang", Default); + if texts.insert(lang, child.text()).is_some() { + return Err(Error::ParseError( + "Text element present twice for the same xml:lang.", + )); } - name => { - if reason.is_some() { - return Err(Error::ParseError( - "Reason must not have more than one reason.", - )); - } - reason = Some(name.parse()?); + } else if child.has_ns(ns::JINGLE) { + if reason.is_some() { + return Err(Error::ParseError( + "Reason must not have more than one reason.", + )); } + check_no_children!(child, "reason"); + check_no_attributes!(child, "reason"); + reason = Some(child.name().parse()?); + } else { + return Err(Error::ParseError("Reason contains a foreign element.")); } } let reason = reason.ok_or(Error::ParseError( @@ -399,7 +403,7 @@ impl TryFrom for ReasonElement { ))?; Ok(ReasonElement { reason, - text, + texts, }) } } @@ -407,8 +411,16 @@ impl TryFrom for ReasonElement { impl From for Element { fn from(reason: ReasonElement) -> Element { Element::builder("reason") + .ns(ns::JINGLE) .append(Element::from(reason.reason)) - .append(reason.text) + .append( + reason.texts.into_iter().map(|(lang, text)| { + Element::builder("text") + .ns(ns::JINGLE) + .attr("xml:lang", lang) + .append(text) + .build() + }).collect::>()) .build() } } @@ -679,13 +691,13 @@ mod tests { let jingle = Jingle::try_from(elem).unwrap(); let reason = jingle.reason.unwrap(); assert_eq!(reason.reason, Reason::Success); - assert_eq!(reason.text, None); + assert_eq!(reason.texts, BTreeMap::new()); let elem: Element = "coucou".parse().unwrap(); let jingle = Jingle::try_from(elem).unwrap(); let reason = jingle.reason.unwrap(); assert_eq!(reason.reason, Reason::Success); - assert_eq!(reason.text, Some(String::from("coucou"))); + assert_eq!(reason.texts.get(""), Some(&String::from("coucou"))); } #[test] @@ -728,6 +740,6 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Reason must not have more than one text."); + assert_eq!(message, "Text element present twice for the same xml:lang."); } } From 4f64754bdcd45712d37a71587fe538d84287b060 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 28 Feb 2019 02:44:31 +0100 Subject: [PATCH 600/698] stanza_error: Simplify serialisation. --- src/stanza_error.rs | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 4c09eea792bedf03dbd0e18c996d3bf23f60d03f..fdc0d52253c28f40afc01631c512d5b7608137a0 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -289,24 +289,21 @@ impl TryFrom for StanzaError { impl From for Element { fn from(err: StanzaError) -> Element { - let mut root = Element::builder("error") + Element::builder("error") .ns(ns::DEFAULT_NS) .attr("type", err.type_) .attr("by", err.by) .append(err.defined_condition) - .build(); - for (lang, text) in err.texts { - let elem = Element::builder("text") - .ns(ns::XMPP_STANZAS) - .attr("xml:lang", lang) - .append(text) - .build(); - root.append_child(elem); - } - if let Some(other) = err.other { - root.append_child(other); - } - root + .append( + err.texts.into_iter().map(|(lang, text)| { + Element::builder("text") + .ns(ns::XMPP_STANZAS) + .attr("xml:lang", lang) + .append(text) + .build() + }).collect::>()) + .append(err.other) + .build() } } From fb4a3dcba86a48073fc15e6160fbafe78c3d497f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 28 Feb 2019 02:48:50 +0100 Subject: [PATCH 601/698] stanza_error: Add missing attribute checks. --- src/stanza_error.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/stanza_error.rs b/src/stanza_error.rs index fdc0d52253c28f40afc01631c512d5b7608137a0..fd0522d7f65c51f2bb41b29d51db48e78c125ff9 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -240,6 +240,7 @@ impl TryFrom for StanzaError { fn try_from(elem: Element) -> Result { check_self!(elem, "error", DEFAULT_NS); + check_no_unknown_attributes!(elem, "error", ["type", "by"]); let type_ = get_attr!(elem, "type", Required); let by = get_attr!(elem, "by", Option); @@ -250,6 +251,7 @@ impl TryFrom for StanzaError { for child in elem.children() { if child.is("text", ns::XMPP_STANZAS) { check_no_children!(child, "text"); + check_no_unknown_attributes!(child, "text", ["xml:lang"]); let lang = get_attr!(elem, "xml:lang", Default); if texts.insert(lang, child.text()).is_some() { return Err(Error::ParseError( @@ -263,6 +265,7 @@ impl TryFrom for StanzaError { )); } check_no_children!(child, "defined-condition"); + check_no_attributes!(child, "defined-condition"); let condition = DefinedCondition::try_from(child.clone())?; defined_condition = Some(condition); } else { From a62b184d5474d430ab920cc6dba07cf94d225211 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 28 Feb 2019 02:54:13 +0100 Subject: [PATCH 602/698] stanza_error: Use a mutable struct first. --- src/stanza_error.rs | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/stanza_error.rs b/src/stanza_error.rs index fd0522d7f65c51f2bb41b29d51db48e78c125ff9..dcaf5cb884f6146839ccf557426eef424cfa3960 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -242,18 +242,21 @@ impl TryFrom for StanzaError { check_self!(elem, "error", DEFAULT_NS); check_no_unknown_attributes!(elem, "error", ["type", "by"]); - let type_ = get_attr!(elem, "type", Required); - let by = get_attr!(elem, "by", Option); + let mut stanza_error = StanzaError { + type_: get_attr!(elem, "type", Required), + by: get_attr!(elem, "by", Option), + defined_condition: DefinedCondition::UndefinedCondition, + texts: BTreeMap::new(), + other: None, + }; let mut defined_condition = None; - let mut texts = BTreeMap::new(); - let mut other = None; for child in elem.children() { if child.is("text", ns::XMPP_STANZAS) { check_no_children!(child, "text"); check_no_unknown_attributes!(child, "text", ["xml:lang"]); let lang = get_attr!(elem, "xml:lang", Default); - if texts.insert(lang, child.text()).is_some() { + if stanza_error.texts.insert(lang, child.text()).is_some() { return Err(Error::ParseError( "Text element present twice for the same xml:lang.", )); @@ -269,24 +272,18 @@ impl TryFrom for StanzaError { let condition = DefinedCondition::try_from(child.clone())?; defined_condition = Some(condition); } else { - if other.is_some() { + if stanza_error.other.is_some() { return Err(Error::ParseError( "Error must not have more than one other element.", )); } - other = Some(child.clone()); + stanza_error.other = Some(child.clone()); } } - let defined_condition = + stanza_error.defined_condition = defined_condition.ok_or(Error::ParseError("Error must have a defined-condition."))?; - Ok(StanzaError { - type_, - by, - defined_condition, - texts, - other, - }) + Ok(stanza_error) } } From ba875cfd2a23459233b0e7a19874d3ac8fe71f2c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 28 Feb 2019 03:10:21 +0100 Subject: [PATCH 603/698] jingle_rtp: Put Channel code into a macro, to generate it automatically. --- src/jingle_rtp.rs | 35 ++++------------------------------- src/util/macros.rs | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/src/jingle_rtp.rs b/src/jingle_rtp.rs index 1ab50d4d325446993488378162c3a251a2111756..95194edc2b9fe5afa64f539db6a77d11135dd5e9 100644 --- a/src/jingle_rtp.rs +++ b/src/jingle_rtp.rs @@ -4,10 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::num::ParseIntError; -use std::str::FromStr; -use minidom::IntoAttributeValue; - generate_element!( /// Wrapper element describing an RTP session. Description, "description", JINGLE_RTP, @@ -28,33 +24,10 @@ generate_element!( ] ); -/// The number of channels. -#[derive(Debug, Clone)] -pub struct Channels(pub u8); - -impl Default for Channels { - fn default() -> Channels { - Channels(1) - } -} - -impl FromStr for Channels { - type Err = ParseIntError; - - fn from_str(s: &str) -> Result { - Ok(Channels(u8::from_str(s)?)) - } -} - -impl IntoAttributeValue for Channels { - fn into_attribute_value(self) -> Option { - if self.0 == 1 { - None - } else { - Some(format!("{}", self.0)) - } - } -} +generate_attribute!( + /// The number of channels. + Channels, "channels", u8, Default = 1 +); generate_element!( /// An encoding that can be used for an RTP stream. diff --git a/src/util/macros.rs b/src/util/macros.rs index bc2b8342ea3a7503bbc23839b8cd63081485220d..f4b08335fa29301d1d263e4315ea3938dfdfaa2a 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -191,6 +191,30 @@ macro_rules! generate_attribute { } } ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $type:tt, Default = $default:expr) => ( + $(#[$meta])* + #[derive(Debug, Clone, PartialEq)] + pub struct $elem(pub $type); + impl ::std::str::FromStr for $elem { + type Err = crate::util::error::Error; + fn from_str(s: &str) -> Result { + Ok($elem($type::from_str(s)?)) + } + } + impl ::minidom::IntoAttributeValue for $elem { + fn into_attribute_value(self) -> Option { + match self { + $elem($default) => None, + $elem(value) => Some(format!("{}", value)), + } + } + } + impl ::std::default::Default for $elem { + fn default() -> $elem { + $elem($default) + } + } + ); } macro_rules! generate_element_enum { From 5592c11745e26d57071d24129e1374a5ef994927 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 28 Feb 2019 03:36:04 +0100 Subject: [PATCH 604/698] jingle_ice_udp: Use an IpAddr for rel-addr too. --- src/jingle_ice_udp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jingle_ice_udp.rs b/src/jingle_ice_udp.rs index 5713c2e66949b350ab5d4c9326cdeeb161f5c110..8cb47da825567c0dbbbbe79b2f6db4da44cbdc2a 100644 --- a/src/jingle_ice_udp.rs +++ b/src/jingle_ice_udp.rs @@ -74,7 +74,7 @@ generate_element!( protocol: Required = "protocol", /// A related address as defined in ICE-CORE. - rel_addr: Option = "rel-addr", + rel_addr: Option = "rel-addr", /// A related port as defined in ICE-CORE. rel_port: Option = "rel-port", From 7fc5dea4d690ca6bfb498757eb3657bfd8415474 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 28 Feb 2019 03:47:04 +0100 Subject: [PATCH 605/698] jingle_ice_udp, jingle_rtp: Add a size test. --- src/jingle_ice_udp.rs | 16 ++++++++++++++++ src/jingle_rtp.rs | 18 ++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/jingle_ice_udp.rs b/src/jingle_ice_udp.rs index 8cb47da825567c0dbbbbe79b2f6db4da44cbdc2a..40a07a62cd2f5e9f99e1237e0862d56af6ed91e5 100644 --- a/src/jingle_ice_udp.rs +++ b/src/jingle_ice_udp.rs @@ -90,6 +90,22 @@ mod tests { use minidom::Element; use try_from::TryFrom; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Transport, 36); + assert_size!(Type, 1); + assert_size!(Candidate, 72); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(Transport, 72); + assert_size!(Type, 1); + assert_size!(Candidate, 104); + } + #[test] fn test_simple() { let elem: Element = " diff --git a/src/jingle_rtp.rs b/src/jingle_rtp.rs index 95194edc2b9fe5afa64f539db6a77d11135dd5e9..366cfeeca4397a5e5d5d4cb4db12efb3b2b4141d 100644 --- a/src/jingle_rtp.rs +++ b/src/jingle_rtp.rs @@ -78,6 +78,24 @@ mod tests { use minidom::Element; use try_from::TryFrom; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Description, 36); + assert_size!(Channels, 1); + assert_size!(PayloadType, 52); + assert_size!(Parameter, 24); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(Description, 72); + assert_size!(Channels, 1); + assert_size!(PayloadType, 80); + assert_size!(Parameter, 48); + } + #[test] fn test_simple() { let elem: Element = " From 429fc387b5090e4e681f9075c12af9d28d4cd6dd Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 28 Feb 2019 13:32:52 +0100 Subject: [PATCH 606/698] jingle_drls_srtp: Add a new parser and serialiser. --- src/hashes.rs | 4 +-- src/jingle_dtls_srtp.rs | 78 +++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 ++ src/ns.rs | 3 ++ src/util/helpers.rs | 25 +++++++++++++ 5 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 src/jingle_dtls_srtp.rs diff --git a/src/hashes.rs b/src/hashes.rs index c097d034690678a682b1dd9a73daa72a85e1657c..e1be62b1ceb967a7517f563a4814b6aaca109e44 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -133,8 +133,8 @@ impl FromStr for Sha1HexAttribute { fn from_str(hex: &str) -> Result { let mut bytes = vec![]; for i in 0..hex.len() / 2 { - let byte = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16); - bytes.push(byte?); + let byte = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16)?; + bytes.push(byte); } Ok(Sha1HexAttribute(Hash::new(Algo::Sha_1, bytes))) } diff --git a/src/jingle_dtls_srtp.rs b/src/jingle_dtls_srtp.rs new file mode 100644 index 0000000000000000000000000000000000000000..29b6417392e73c6165ef193c307e922b6817c13b --- /dev/null +++ b/src/jingle_dtls_srtp.rs @@ -0,0 +1,78 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use crate::util::helpers::ColonSeparatedHex; +use crate::hashes::Algo; + +generate_attribute!( + /// Indicates which of the end points should initiate the TCP connection establishment. + Setup, "setup", { + /// The endpoint will initiate an outgoing connection. + Active => "active", + + /// The endpoint will accept an incoming connection. + Passive => "passive", + + /// The endpoint is willing to accept an incoming connection or to initiate an outgoing + /// connection. + Actpass => "actpass", + + /* + /// The endpoint does not want the connection to be established for the time being. + /// + /// Note that this value isn’t used, as per the XEP. + Holdconn => "holdconn", + */ + } +); + +// TODO: use a hashes::Hash instead of two different fields here. +generate_element!( + /// Fingerprint of the key used for a DTLS handshake. + Fingerprint, "fingerprint", JINGLE_DTLS, + attributes: [ + /// The hash algorithm used for this fingerprint. + hash: Required = "hash", + + /// Indicates which of the end points should initiate the TCP connection establishment. + setup: Required = "setup" + ], + text: ( + /// Hash value of this fingerprint. + value: ColonSeparatedHex> + ) +); + +mod tests { + use super::*; + use minidom::Element; + use try_from::TryFrom; + + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Setup, 1); + assert_size!(Fingerprint, 32); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(Setup, 1); + assert_size!(Fingerprint, 64); + } + + #[test] + fn test_ex1() { + let elem: Element = "02:1A:CC:54:27:AB:EB:9C:53:3F:3E:4B:65:2E:7D:46:3F:54:42:CD:54:F1:7A:03:A2:7D:F9:B0:7F:46:19:B2" + .parse() + .unwrap(); + let fingerprint = Fingerprint::try_from(elem).unwrap(); + assert_eq!(fingerprint.setup, Setup::Actpass); + assert_eq!(fingerprint.hash, Algo::Sha_256); + assert_eq!(fingerprint.value, [2, 26, 204, 84, 39, 171, 235, 156, 83, 63, 62, 75, 101, 46, 125, 70, 63, 84, 66, 205, 84, 241, 122, 3, 162, 125, 249, 176, 127, 70, 25, 178]); + } +} diff --git a/src/lib.rs b/src/lib.rs index ca7840b165c34ebe920b7db035759f15aa06517b..2a6c3abb2cf8d82836babedc4c2d00cdd4868c3b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -158,6 +158,9 @@ pub mod mam; /// XEP-0319: Last User Interaction in Presence pub mod idle; +/// XEP-0320: Use of DTLS-SRTP in Jingle Sessions +pub mod jingle_dtls_srtp; + /// XEP-0353: Jingle Message Initiation pub mod jingle_message; diff --git a/src/ns.rs b/src/ns.rs index 321c6fa8362ee14bc8a1035638350c90db2419f4..1897dbb48dba0b37a7a77d628db93d8c510e112a 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -156,6 +156,9 @@ pub const MAM: &str = "urn:xmpp:mam:2"; /// XEP-0319: Last User Interaction in Presence pub const IDLE: &str = "urn:xmpp:idle:1"; +/// XEP-0320: Use of DTLS-SRTP in Jingle Sessions +pub const JINGLE_DTLS: &str = "urn:xmpp:jingle:apps:dtls:0"; + /// XEP-0353: Jingle Message Initiation pub const JINGLE_MESSAGE: &str = "urn:xmpp:jingle-message:0"; diff --git a/src/util/helpers.rs b/src/util/helpers.rs index b3832345c33fd41698c71cce88b7f7dd913aa0e0..b7bdc8443cbaf4cbf1f5d3f3f6b055bd7ef16550 100644 --- a/src/util/helpers.rs +++ b/src/util/helpers.rs @@ -65,3 +65,28 @@ impl WhitespaceAwareBase64 { Some(base64::encode(b)) } } + +/// Codec for colon-separated bytes of uppercase hexadecimal. +pub struct ColonSeparatedHex; + +impl ColonSeparatedHex { + pub fn decode(s: &str) -> Result, Error> { + let mut bytes = vec![]; + for i in 0..(1 + s.len()) / 3 { + let byte = u8::from_str_radix(&s[3 * i..3 * i + 2], 16)?; + if 3 * i + 2 < s.len() { + assert_eq!(&s[3 * i + 2..3 * i + 3], ":"); + } + bytes.push(byte); + } + Ok(bytes) + } + + pub fn encode(b: &[u8]) -> Option { + let mut bytes = vec![]; + for byte in b { + bytes.push(format!("{:02X}", byte)); + } + Some(bytes.join(":")) + } +} From 36d6169386e04b6ebdb75489fce3b4d216292148 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 28 Feb 2019 13:35:09 +0100 Subject: [PATCH 607/698] jingle_ice_udp: Add DTLS-SRTP support. --- src/jingle_ice_udp.rs | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/src/jingle_ice_udp.rs b/src/jingle_ice_udp.rs index 40a07a62cd2f5e9f99e1237e0862d56af6ed91e5..3b386705362b9ee6cb545c29540f74263c5cb19e 100644 --- a/src/jingle_ice_udp.rs +++ b/src/jingle_ice_udp.rs @@ -4,6 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use crate::jingle_dtls_srtp::Fingerprint; use std::net::IpAddr; generate_element!( @@ -17,8 +18,11 @@ generate_element!( ufrag: Option = "ufrag", ], children: [ - /// List of candidates for this ICE-UDP session. - candidates: Vec = ("candidate", JINGLE_ICE_UDP) => Candidate + /// List of candidates for this ICE-UDP session. + candidates: Vec = ("candidate", JINGLE_ICE_UDP) => Candidate, + + /// Fingerprint of the key used for the DTLS handshake. + fingerprint: Option = ("fingerprint", JINGLE_DTLS) => Fingerprint ] ); @@ -89,25 +93,27 @@ mod tests { use super::*; use minidom::Element; use try_from::TryFrom; + use crate::hashes::Algo; + use crate::jingle_dtls_srtp::Setup; #[cfg(target_pointer_width = "32")] #[test] fn test_size() { - assert_size!(Transport, 36); + assert_size!(Transport, 68); assert_size!(Type, 1); - assert_size!(Candidate, 72); + assert_size!(Candidate, 80); } #[cfg(target_pointer_width = "64")] #[test] fn test_size() { - assert_size!(Transport, 72); + assert_size!(Transport, 136); assert_size!(Type, 1); assert_size!(Candidate, 104); } #[test] - fn test_simple() { + fn test_gajim() { let elem: Element = " @@ -135,4 +141,25 @@ mod tests { assert_eq!(transport.pwd.unwrap(), "wakMJ8Ydd5rqnPaFerws5o"); assert_eq!(transport.ufrag.unwrap(), "aeXX"); } + + #[test] + fn test_jitsi_meet() { + let elem: Element = " + + 97:F2:B5:BE:DB:A6:00:B1:3E:40:B2:41:3C:0D:FC:E0:BD:B2:A0:E8 + + + +" + .parse() + .unwrap(); + let transport = Transport::try_from(elem).unwrap(); + assert_eq!(transport.pwd.unwrap(), "7lk9uul39gckit6t02oavv2r9j"); + assert_eq!(transport.ufrag.unwrap(), "2acq51d4p07v2m"); + + let fingerprint = transport.fingerprint.unwrap(); + assert_eq!(fingerprint.hash, Algo::Sha_1); + assert_eq!(fingerprint.setup, Setup::Actpass); + assert_eq!(fingerprint.value, [151, 242, 181, 190, 219, 166, 0, 177, 62, 64, 178, 65, 60, 13, 252, 224, 189, 178, 160, 232]); + } } From af8cf177cef941959c52cf23029b476143906e11 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 3 Mar 2019 20:08:27 +0100 Subject: [PATCH 608/698] ecaps2: Add a constructor. --- src/ecaps2.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 352881f105e2149f8b367d33ff1d6e0cfaf662c6..36e55c2c34f6069bc5444664b9c836191369ee8e 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -28,6 +28,15 @@ generate_element!( impl PresencePayload for ECaps2 {} +impl ECaps2 { + /// Create an ECaps2 element from a list of hashes. + pub fn new(hashes: Vec) -> ECaps2 { + ECaps2 { + hashes, + } + } +} + fn compute_item(field: &str) -> Vec { let mut bytes = field.as_bytes().to_vec(); bytes.push(0x1f); From 41bcf2dce794019c3e50b447eb7f172ebc150f9b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 3 Mar 2019 20:13:51 +0100 Subject: [PATCH 609/698] examples: Add a caps/ecaps2 generator. --- examples/generate-caps.rs | 62 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 examples/generate-caps.rs diff --git a/examples/generate-caps.rs b/examples/generate-caps.rs new file mode 100644 index 0000000000000000000000000000000000000000..f47715c1e6d7dd4f46871fd856e676b2e561c263 --- /dev/null +++ b/examples/generate-caps.rs @@ -0,0 +1,62 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::io::{self, Read}; +use std::env; +use xmpp_parsers::{ + TryFrom, + Element, + disco::DiscoInfoResult, + caps::{Caps, compute_disco as compute_disco_caps, hash_caps}, + ecaps2::{ECaps2, compute_disco as compute_disco_ecaps2, hash_ecaps2}, + hashes::Algo, +}; + +fn get_caps(disco: &DiscoInfoResult, node: String) -> Result { + let caps_data = compute_disco_caps(&disco); + let caps_hash = hash_caps(&caps_data, Algo::Sha_1)?; + Ok(Caps::new(node, caps_hash)) +} + +fn get_ecaps2(disco: &DiscoInfoResult) -> Result { + let ecaps2_data = compute_disco_ecaps2(&disco).unwrap(); + let ecaps2_sha256 = hash_ecaps2(&ecaps2_data, Algo::Sha_256)?; + let ecaps2_sha3_256 = hash_ecaps2(&ecaps2_data, Algo::Sha3_256)?; + Ok(ECaps2::new(vec![ecaps2_sha256, ecaps2_sha3_256])) +} + +fn main() -> Result<(), ()> { + let args: Vec<_> = env::args().collect(); + if args.len() != 2 { + println!("Usage: {} ", args[0]); + return Err(()); + } + let node = args[1].clone(); + + eprintln!("Reading a disco#info payload from stdin..."); + + // Read from stdin. + let stdin = io::stdin(); + let mut data = String::new(); + let mut handle = stdin.lock(); + handle.read_to_string(&mut data).unwrap(); + + // Parse the payload into a DiscoInfoResult. + let elem: Element = data.parse().unwrap(); + let disco = DiscoInfoResult::try_from(elem).unwrap(); + + // Compute both kinds of caps. + let caps = get_caps(&disco, node).unwrap(); + let ecaps2 = get_ecaps2(&disco).unwrap(); + + // Print them. + let caps_elem = Element::from(caps); + let ecaps2_elem = Element::from(ecaps2); + println!("{}", String::from(&caps_elem)); + println!("{}", String::from(&ecaps2_elem)); + + Ok(()) +} From 3e41442702674b184024bab542e4966739d65680 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 20 Mar 2019 17:48:37 +0100 Subject: [PATCH 610/698] jingle_drls_srtp: Add missing #[cfg(test)] on mod test. --- src/jingle_dtls_srtp.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/jingle_dtls_srtp.rs b/src/jingle_dtls_srtp.rs index 29b6417392e73c6165ef193c307e922b6817c13b..84f646506b3ca265650c3f03e8816ae8dfce000d 100644 --- a/src/jingle_dtls_srtp.rs +++ b/src/jingle_dtls_srtp.rs @@ -46,6 +46,7 @@ generate_element!( ) ); +#[cfg(test)] mod tests { use super::*; use minidom::Element; From 50579056502ad56cba1ae18b5ef2d445f7d16ce8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 20 Mar 2019 18:52:20 +0100 Subject: [PATCH 611/698] ChangeLog: Add imminent 0.13.0 release. --- ChangeLog | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/ChangeLog b/ChangeLog index 1c022a14b2a797e0628c581e90c616db9a8f496e..3a608458e89f42f90fadaa33b0e79fbe2c31ebbd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +Version 0.13.0: +2019-03-20 Emmanuel Gil Peyrot + * New parsers/serialisers: + - User Avatar (XEP-0084). + - Jingle RTP Sessions (XEP-0167). + - Jingle ICE-UDP Transport Method (XEP-0176). + - Use of DTLS-SRTP in Jingle Sessions (XEP-0320). + * Breaking changes: + - Make 'id' required on iq, as per RFC6120 §8.1.3. + - Refactor PubSub to have more type-safety. + - Treat FORM_TYPE as a special case in data forms, to avoid + duplicating it into a field. + - Add forgotten i18n to Jingle text element. + * Improvements: + - Add various helpers for hash representations. + - Add helpers constructors for multiple extensions (disco, caps, + pubsub, stanza_error). + - Use Into in more constructors. + - Internal change on attribute declaration in macros. + - Reexport missing try_from::TryInto. + Version 0.12.2: 2019-01-16 Emmanuel Gil Peyrot * Improvements: From 107bd90a87808e62c3bc5950430861902c5d510d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sat, 26 Jan 2019 20:06:51 +0000 Subject: [PATCH 612/698] Change pep's email address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 8aab21dc5c72636969f7a1ace092cb739cf4587c..f99e9a5c1ebfe255e3f7fd0e0cfd95d05ef07eb9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "xmpp-parsers" version = "0.12.2" authors = [ "Emmanuel Gil Peyrot ", - "Maxime “pep” Buquet ", + "Maxime “pep” Buquet ", ] description = "Collection of parsers and serialisers for XMPP extensions" homepage = "https://gitlab.com/xmpp-rs/xmpp-parsers" From ba5a014de9c1c9247befef60a4e394d260176bbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sat, 26 Jan 2019 20:05:48 +0000 Subject: [PATCH 613/698] XEP-0157: Contact Addresses for XMPP Services. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- src/lib.rs | 3 + src/ns.rs | 3 + src/server_info.rs | 234 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 240 insertions(+) create mode 100644 src/server_info.rs diff --git a/src/lib.rs b/src/lib.rs index 2a6c3abb2cf8d82836babedc4c2d00cdd4868c3b..eb3cc3b4aa52e818df45738a9f4a9fc058cca312 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -101,6 +101,9 @@ pub mod component; /// XEP-0115: Entity Capabilities pub mod caps; +/// XEP-0157: Contact Addresses for XMPP Services +pub mod server_info; + /// XEP-0166: Jingle pub mod jingle; diff --git a/src/ns.rs b/src/ns.rs index 1897dbb48dba0b37a7a77d628db93d8c510e112a..4f3d2d299d3dcd1f756427b03603fd8b0225aff6 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -79,6 +79,9 @@ pub const COMPONENT: &str = "jabber:component:accept"; /// XEP-0115: Entity Capabilities pub const CAPS: &str = "http://jabber.org/protocol/caps"; +/// XEP-0157: Contact Addresses for XMPP Services +pub const SERVER_INFO: &str = "http://jabber.org/network/serverinfo"; + /// XEP-0166: Jingle pub const JINGLE: &str = "urn:xmpp:jingle:1"; diff --git a/src/server_info.rs b/src/server_info.rs new file mode 100644 index 0000000000000000000000000000000000000000..cf1b75c4c6e8f12d4497dbf1c69d91c80d7abcf5 --- /dev/null +++ b/src/server_info.rs @@ -0,0 +1,234 @@ +// Copyright (C) 2019 Maxime “pep” Buquet +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use crate::data_forms::{DataForm, DataFormType, Field, FieldType}; +use crate::ns; +use crate::util::error::Error; +use try_from::TryFrom; + +/// Structure representing a `http://jabber.org/network/serverinfo` form type. +#[derive(Debug, Clone)] +pub struct ServerInfo { + /// Abuse addresses + pub abuse: Vec, + + /// Admin addresses + pub admin: Vec, + + /// Feedback addresses + pub feedback: Vec, + + /// Sales addresses + pub sales: Vec, + + /// Security addresses + pub security: Vec, + + /// Support addresses + pub support: Vec, +} + +impl TryFrom for ServerInfo { + type Err = Error; + + fn try_from(form: DataForm) -> Result { + if form.type_ != DataFormType::Result_ { + return Err(Error::ParseError("Wrong type of form.")); + } + if form.form_type != Some(String::from(ns::SERVER_INFO)) { + return Err(Error::ParseError("Wrong FORM_TYPE for form.")); + } + let mut server_info = ServerInfo { + abuse: vec![], + admin: vec![], + feedback: vec![], + sales: vec![], + security: vec![], + support: vec![], + }; + for field in form.fields { + if field.type_ != FieldType::ListMulti { + return Err(Error::ParseError("Field is not of the required type.")); + } + if field.var == "abuse-addresses" { + server_info.abuse = field.values; + } else if field.var == "admin-addresses" { + server_info.admin = field.values; + } else if field.var == "feedback-addresses" { + server_info.feedback = field.values; + } else if field.var == "sales-addresses" { + server_info.sales = field.values; + } else if field.var == "security-addresses" { + server_info.security = field.values; + } else if field.var == "support-addresses" { + server_info.support = field.values; + } else { + return Err(Error::ParseError("Unknown form field var.")); + } + } + + Ok(server_info) + } +} + +impl From for DataForm { + fn from(server_info: ServerInfo) -> DataForm { + DataForm { + type_: DataFormType::Result_, + form_type: Some(String::from(ns::SERVER_INFO)), + title: None, + instructions: None, + fields: vec![ + Field { + var: String::from("FORM_TYPE"), + type_: FieldType::Hidden, + label: None, + required: false, + options: vec![], + values: vec![String::from(ns::SERVER_INFO)], + media: vec![], + }, + generate_address_field("abuse-addresses", server_info.abuse), + generate_address_field("admin-addresses", server_info.admin), + generate_address_field("feedback-addresses", server_info.feedback), + generate_address_field("sales-addresses", server_info.sales), + generate_address_field("security-addresses", server_info.security), + generate_address_field("support-addresses", server_info.support), + ], + } + } +} + +/// Generate `Field` for addresses +pub fn generate_address_field>(var: S, values: Vec) -> Field { + Field { + var: var.into(), + type_: FieldType::ListMulti, + label: None, + required: false, + options: vec![], + values, + media: vec![], + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::data_forms::{DataForm, DataFormType, Field, FieldType}; + + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(ServerInfo, 72); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(ServerInfo, 144); + } + + #[test] + fn test_simple() { + let form = DataForm { + type_: DataFormType::Result_, + form_type: Some(String::from(ns::SERVER_INFO)), + title: None, + instructions: None, + fields: vec![ + Field { + var: String::from("FORM_TYPE"), + type_: FieldType::Hidden, + label: None, + required: false, + options: vec![], + values: vec![String::from(ns::SERVER_INFO)], + media: vec![], + }, + Field { + var: String::from("abuse-addresses"), + type_: FieldType::ListMulti, + label: None, + required: false, + options: vec![], + values: vec![], + media: vec![], + }, + Field { + var: String::from("admin-addresses"), + type_: FieldType::ListMulti, + label: None, + required: false, + options: vec![], + values: vec![ + String::from("xmpp:admin@foo.bar"), + String::from("https://foo.bar/chat/"), + String::from("mailto:admin@foo.bar"), + ], + media: vec![], + }, + Field { + var: String::from("feedback-addresses"), + type_: FieldType::ListMulti, + label: None, + required: false, + options: vec![], + values: vec![], + media: vec![], + }, + Field { + var: String::from("sales-addresses"), + type_: FieldType::ListMulti, + label: None, + required: false, + options: vec![], + values: vec![], + media: vec![], + }, + Field { + var: String::from("security-addresses"), + type_: FieldType::ListMulti, + label: None, + required: false, + options: vec![], + values: vec![ + String::from("xmpp:security@foo.bar"), + String::from("mailto:security@foo.bar"), + ], + media: vec![], + }, + Field { + var: String::from("support-addresses"), + type_: FieldType::ListMulti, + label: None, + required: false, + options: vec![], + values: vec![String::from("mailto:support@foo.bar")], + media: vec![], + }, + ], + }; + + let server_info = ServerInfo { + abuse: vec![], + admin: vec![ + String::from("xmpp:admin@foo.bar"), + String::from("https://foo.bar/chat/"), + String::from("mailto:admin@foo.bar"), + ], + feedback: vec![], + sales: vec![], + security: vec![ + String::from("xmpp:security@foo.bar"), + String::from("mailto:security@foo.bar"), + ], + support: vec![String::from("mailto:support@foo.bar")], + }; + + // assert_eq!(DataForm::from(server_info), form); + // assert_eq!(ServerInfo::try_from(form), Ok(form)); + } +} From cbc787a9d40868d650525849fb13120dff358716 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 20 Mar 2019 19:09:14 +0100 Subject: [PATCH 614/698] server_info: Remove remaining FORM_TYPE handling. Also reenables the test. --- src/server_info.rs | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/src/server_info.rs b/src/server_info.rs index cf1b75c4c6e8f12d4497dbf1c69d91c80d7abcf5..6f18c7c92ab6203c81b075b955186544442f9e0f 100644 --- a/src/server_info.rs +++ b/src/server_info.rs @@ -9,7 +9,7 @@ use crate::util::error::Error; use try_from::TryFrom; /// Structure representing a `http://jabber.org/network/serverinfo` form type. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct ServerInfo { /// Abuse addresses pub abuse: Vec, @@ -81,15 +81,6 @@ impl From for DataForm { title: None, instructions: None, fields: vec![ - Field { - var: String::from("FORM_TYPE"), - type_: FieldType::Hidden, - label: None, - required: false, - options: vec![], - values: vec![String::from(ns::SERVER_INFO)], - media: vec![], - }, generate_address_field("abuse-addresses", server_info.abuse), generate_address_field("admin-addresses", server_info.admin), generate_address_field("feedback-addresses", server_info.feedback), @@ -139,15 +130,6 @@ mod tests { title: None, instructions: None, fields: vec![ - Field { - var: String::from("FORM_TYPE"), - type_: FieldType::Hidden, - label: None, - required: false, - options: vec![], - values: vec![String::from(ns::SERVER_INFO)], - media: vec![], - }, Field { var: String::from("abuse-addresses"), type_: FieldType::ListMulti, @@ -229,6 +211,6 @@ mod tests { }; // assert_eq!(DataForm::from(server_info), form); - // assert_eq!(ServerInfo::try_from(form), Ok(form)); + assert_eq!(ServerInfo::try_from(form).unwrap(), server_info); } } From 00e19012e5c063729cb0555c1592ff625a947119 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 20 Mar 2019 19:10:01 +0100 Subject: [PATCH 615/698] server_info: Implement Default and use it. --- src/server_info.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/server_info.rs b/src/server_info.rs index 6f18c7c92ab6203c81b075b955186544442f9e0f..53cef3838bcd2cc55ad1e75de8827f8863183049 100644 --- a/src/server_info.rs +++ b/src/server_info.rs @@ -9,7 +9,7 @@ use crate::util::error::Error; use try_from::TryFrom; /// Structure representing a `http://jabber.org/network/serverinfo` form type. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Default)] pub struct ServerInfo { /// Abuse addresses pub abuse: Vec, @@ -40,14 +40,7 @@ impl TryFrom for ServerInfo { if form.form_type != Some(String::from(ns::SERVER_INFO)) { return Err(Error::ParseError("Wrong FORM_TYPE for form.")); } - let mut server_info = ServerInfo { - abuse: vec![], - admin: vec![], - feedback: vec![], - sales: vec![], - security: vec![], - support: vec![], - }; + let mut server_info = ServerInfo::default(); for field in form.fields { if field.type_ != FieldType::ListMulti { return Err(Error::ParseError("Field is not of the required type.")); From d329a473d408fbe8a16d4960f473b15751d10b58 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 20 Mar 2019 19:17:21 +0100 Subject: [PATCH 616/698] ChangeLog: Mention XEP-0157 support, thanks pep.! --- ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog b/ChangeLog index 3a608458e89f42f90fadaa33b0e79fbe2c31ebbd..12a14a0150956cedea332ec69094af3bd6174548 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,7 @@ Version 0.13.0: 2019-03-20 Emmanuel Gil Peyrot * New parsers/serialisers: - User Avatar (XEP-0084). + - Contact Addresses for XMPP Services (XEP-0157). - Jingle RTP Sessions (XEP-0167). - Jingle ICE-UDP Transport Method (XEP-0176). - Use of DTLS-SRTP in Jingle Sessions (XEP-0320). From 98c4c5b9010d018b56cb69613f142b7be562527b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 20 Mar 2019 19:19:27 +0100 Subject: [PATCH 617/698] Cargo.toml: Release version 0.13.0. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f99e9a5c1ebfe255e3f7fd0e0cfd95d05ef07eb9..6ddde6f7b94773886e02ca0577893fdf43865699 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.12.2" +version = "0.13.0" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", From 12265e9237787d3cbf13cfb0939f9bd62c25fad1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 21 Mar 2019 01:39:21 +0100 Subject: [PATCH 618/698] presence: Fix serialisation of priority. --- src/presence.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/presence.rs b/src/presence.rs index fba1773c917fca83a3f6c3fc2738f738873b26cc..c6a1a102974f2e2701e42d89318f9f821c3ca2fa 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -347,7 +347,11 @@ impl From for Element { .append(if presence.priority == 0 { None } else { - Some(format!("{}", presence.priority)) + Some( + Element::builder("priority") + .append(format!("{}", presence.priority)) + .build() + ) }) .append(presence.payloads) .build() @@ -622,4 +626,15 @@ mod tests { assert!(elem.is("presence", ns::DEFAULT_NS)); assert!(elem.children().next().unwrap().is("status", ns::DEFAULT_NS)); } + + #[test] + fn test_serialise_priority() { + let presence = Presence::new(Type::None) + .with_priority(42); + let elem: Element = presence.into(); + assert!(elem.is("presence", ns::DEFAULT_NS)); + let priority = elem.children().next().unwrap(); + assert!(priority.is("priority", ns::DEFAULT_NS)); + assert_eq!(priority.text(), "42"); + } } From 0d893edb69860eaa33caeb1dbd7b986810353e03 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 8 Apr 2019 21:32:57 +0200 Subject: [PATCH 619/698] avatar: Bump width/height to u16 This mirrors XEP revision 1.1.2, which made width and height xs:unsignedShort instead of xs:unsignedByte, as per common usage. --- src/avatar.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/avatar.rs b/src/avatar.rs index 6e2d3ce03da49f5891ef45fa28050f1ddd9b47d8..7693c05c16ea954eb90e8ce6ddad287ef8a81d8a 100644 --- a/src/avatar.rs +++ b/src/avatar.rs @@ -27,10 +27,10 @@ generate_element!( bytes: Required = "bytes", /// The width of the image in pixels. - width: Option = "width", + width: Option = "width", /// The height of the image in pixels. - height: Option = "height", + height: Option = "height", /// The SHA-1 hash of the image data for the specified content-type. id: Required = "id", @@ -66,7 +66,7 @@ mod tests { #[test] fn test_size() { assert_size!(Metadata, 12); - assert_size!(Info, 60); + assert_size!(Info, 64); assert_size!(Data, 12); } @@ -74,7 +74,7 @@ mod tests { #[test] fn test_size() { assert_size!(Metadata, 24); - assert_size!(Info, 112); + assert_size!(Info, 120); assert_size!(Data, 24); } From 5bf14b0b22f0020463de381179c71f4b57f75a6e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 12 Apr 2019 10:58:42 +0200 Subject: [PATCH 620/698] Drop dependency on try_from. This bumps the minimum supported stable Rust version to 1.34. The TryFrom and TryInto traits are still reexported to not break the API, but these reexports are deprecated and will be removed in a future release. --- Cargo.toml | 1 - examples/generate-caps.rs | 2 +- src/attention.rs | 2 +- src/avatar.rs | 2 +- src/bind.rs | 4 ++-- src/blocking.rs | 4 ++-- src/bookmarks.rs | 2 +- src/caps.rs | 4 ++-- src/chatstates.rs | 2 +- src/component.rs | 2 +- src/data_forms.rs | 6 +++--- src/delay.rs | 2 +- src/disco.rs | 4 ++-- src/ecaps2.rs | 2 +- src/eme.rs | 2 +- src/forwarding.rs | 2 +- src/hashes.rs | 2 +- src/ibb.rs | 2 +- src/ibr.rs | 4 ++-- src/idle.rs | 2 +- src/iq.rs | 4 ++-- src/jingle.rs | 6 +++--- src/jingle_dtls_srtp.rs | 2 +- src/jingle_ft.rs | 8 ++++---- src/jingle_ibb.rs | 2 +- src/jingle_ice_udp.rs | 2 +- src/jingle_message.rs | 4 ++-- src/jingle_rtp.rs | 2 +- src/jingle_s5b.rs | 4 ++-- src/lib.rs | 2 +- src/mam.rs | 4 ++-- src/media_element.rs | 2 +- src/message.rs | 4 ++-- src/message_correct.rs | 2 +- src/mood.rs | 2 +- src/muc/muc.rs | 2 +- src/muc/user.rs | 4 ++-- src/nick.rs | 2 +- src/ping.rs | 2 +- src/presence.rs | 4 ++-- src/pubsub/event.rs | 4 ++-- src/pubsub/pubsub.rs | 6 +++--- src/receipts.rs | 2 +- src/roster.rs | 2 +- src/rsm.rs | 6 +++--- src/sasl.rs | 6 +++--- src/server_info.rs | 4 ++-- src/sm.rs | 2 +- src/stanza_error.rs | 4 ++-- src/stanza_id.rs | 2 +- src/stream.rs | 2 +- src/util/macros.rs | 22 +++++++++++----------- src/version.rs | 2 +- src/websocket.rs | 2 +- 54 files changed, 91 insertions(+), 92 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6ddde6f7b94773886e02ca0577893fdf43865699..36ed320c4e0249f5a33d6830a166d41688366258 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,6 @@ sha2 = "0.8" sha3 = "0.8" blake2 = "0.8" chrono = "0.4.5" -try_from = "0.3.2" [features] # Build xmpp-parsers to make components instead of clients. diff --git a/examples/generate-caps.rs b/examples/generate-caps.rs index f47715c1e6d7dd4f46871fd856e676b2e561c263..80ba863f12be8e5f9f229638966fef8ac1d8f800 100644 --- a/examples/generate-caps.rs +++ b/examples/generate-caps.rs @@ -4,10 +4,10 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; use std::io::{self, Read}; use std::env; use xmpp_parsers::{ - TryFrom, Element, disco::DiscoInfoResult, caps::{Caps, compute_disco as compute_disco_caps, hash_caps}, diff --git a/src/attention.rs b/src/attention.rs index 1910c8c13bf75657512b3db9b07c6e549bed0f89..a2af3a9cd79a9be54cef033e5b7468da397c1ccc 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -21,7 +21,7 @@ mod tests { #[cfg(not(feature = "disable-validation"))] use crate::util::error::Error; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[test] fn test_size() { diff --git a/src/avatar.rs b/src/avatar.rs index 7693c05c16ea954eb90e8ce6ddad287ef8a81d8a..e5c09d2a14957737c50a266ced9db2d1f25be35f 100644 --- a/src/avatar.rs +++ b/src/avatar.rs @@ -60,7 +60,7 @@ mod tests { use crate::hashes::Algo; use crate::util::error::Error; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/bind.rs b/src/bind.rs index 8ded3e6ac10c8bb01e61ba9bb0d3c56b1f1d4d24..1d509638c8fb0545f80a996bcc81a4d8f652e69b 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -10,7 +10,7 @@ use crate::ns; use jid::Jid; use minidom::Element; use std::str::FromStr; -use try_from::TryFrom; +use std::convert::TryFrom; /// The request for resource binding, which is the process by which a client /// can obtain a full JID and start exchanging on the XMPP network. @@ -43,7 +43,7 @@ impl IqSetPayload for Bind {} impl IqResultPayload for Bind {} impl TryFrom for Bind { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "bind", BIND); diff --git a/src/blocking.rs b/src/blocking.rs index 74e84bc6f46d44921c832c643f4d702ce694a4ad..b44c4111936b1c2af458f09bc8383c2a01f6351a 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -9,7 +9,7 @@ use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; use jid::Jid; use minidom::Element; -use try_from::TryFrom; +use std::convert::TryFrom; generate_empty_element!( /// The element requesting the blocklist, the result iq will contain a @@ -31,7 +31,7 @@ macro_rules! generate_blocking_element { } impl TryFrom for $elem { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result<$elem, Error> { check_self!(elem, $name, BLOCKING); diff --git a/src/bookmarks.rs b/src/bookmarks.rs index b1c59b0e40d0372921eeb082ac29caa42d4c1116..d8b5a9ccc1bbce7141144df5ca699ef1e2bd9754 100644 --- a/src/bookmarks.rs +++ b/src/bookmarks.rs @@ -72,7 +72,7 @@ mod tests { use super::*; use crate::util::compare_elements::NamespaceAwareCompare; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/caps.rs b/src/caps.rs index 452fa6d56acfd8920e73916a0eab3a101f57db14..e5e998b1df7561085baa580473f576030cd1c0af 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -17,7 +17,7 @@ use minidom::Element; use sha1::Sha1; use sha2::{Sha256, Sha512}; use sha3::{Sha3_256, Sha3_512}; -use try_from::TryFrom; +use std::convert::TryFrom; /// Represents a capability hash for a given client. #[derive(Debug, Clone)] @@ -40,7 +40,7 @@ pub struct Caps { impl PresencePayload for Caps {} impl TryFrom for Caps { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "c", CAPS, "caps"); diff --git a/src/chatstates.rs b/src/chatstates.rs index 2bf2d1b1fe0cbd91cc4803101bb941d03add554a..d7ae1e37d8a6dbe94bdf41b76ae6351a4da26b1b 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -35,7 +35,7 @@ mod tests { use crate::util::error::Error; use crate::ns; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[test] fn test_size() { diff --git a/src/component.rs b/src/component.rs index 4ab78c57c28bf9ffba5ad50cd4bf9588c69fad8b..a0588177fecc45c3c133b319ea4d2fb8238bdcef 100644 --- a/src/component.rs +++ b/src/component.rs @@ -44,7 +44,7 @@ impl Handshake { mod tests { use super::*; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/data_forms.rs b/src/data_forms.rs index 296614e975dfd118707a93e2c0d9e45e8ccf742f..323e91ed091b7411dc1a2eb82e40b28fc4357853 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -8,7 +8,7 @@ use crate::util::error::Error; use crate::media_element::MediaElement; use crate::ns; use minidom::Element; -use try_from::TryFrom; +use std::convert::TryFrom; generate_element!( /// Represents one of the possible values for a list- field. @@ -100,7 +100,7 @@ impl Field { } impl TryFrom for Field { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "field", DATA_FORMS); @@ -215,7 +215,7 @@ pub struct DataForm { } impl TryFrom for DataForm { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "x", DATA_FORMS); diff --git a/src/delay.rs b/src/delay.rs index 80f92edd00eb8b8023d8920df551de550493bed8..97a32d2598933c069a0215d152e42ae5ae4b93c4 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -35,7 +35,7 @@ mod tests { use crate::util::error::Error; use minidom::Element; use std::str::FromStr; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/disco.rs b/src/disco.rs index 87145208d96aeb819e913eb7ed8a7981665bf718..9d8781dcdb95d99ae78f7178b68e6620c5e66fba 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -10,7 +10,7 @@ use crate::iq::{IqGetPayload, IqResultPayload}; use crate::ns; use jid::Jid; use minidom::Element; -use try_from::TryFrom; +use std::convert::TryFrom; generate_element!( /// Structure representing a `` element. @@ -115,7 +115,7 @@ pub struct DiscoInfoResult { impl IqResultPayload for DiscoInfoResult {} impl TryFrom for DiscoInfoResult { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "query", DISCO_INFO, "disco#info result"); diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 36e55c2c34f6069bc5444664b9c836191369ee8e..d408b28de48f1ccc74c09ad074e0322b35b278b0 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -176,7 +176,7 @@ mod tests { use super::*; use crate::util::error::Error; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/eme.rs b/src/eme.rs index 20623ff2f2b628ed56310bb8d1110d6df20ead6d..623a0cd1eb1378c6b24a9fb55e15eb72ee633167 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -26,7 +26,7 @@ mod tests { use super::*; use crate::util::error::Error; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/forwarding.rs b/src/forwarding.rs index 5dfa4aea0413a754808f3ddbbf4f1601ac85c76e..4adb47964422e79070a494dcb8b1264c567e7ced 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -28,7 +28,7 @@ mod tests { use super::*; use crate::util::error::Error; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/hashes.rs b/src/hashes.rs index e1be62b1ceb967a7517f563a4814b6aaca109e44..f4b19faad26f62324286715e220194fcd66af5f5 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -168,7 +168,7 @@ impl Deref for Sha1HexAttribute { mod tests { use super::*; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/ibb.rs b/src/ibb.rs index aa1e07d3c2308cd767defbec47be9e80d72f3fd9..8a81b651826903733a271e3aab7c9953302ace4d 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -75,7 +75,7 @@ mod tests { use crate::util::error::Error; use minidom::Element; use std::error::Error as StdError; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/ibr.rs b/src/ibr.rs index c98c909369c4ba1a25e52ae603c6e7b59584b28c..911b800228b76fe5b2038b1e0198df01e3ae9e1a 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -10,7 +10,7 @@ use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; use minidom::Element; use std::collections::HashMap; -use try_from::TryFrom; +use std::convert::TryFrom; /// Query for registering against a service. #[derive(Debug, Clone)] @@ -36,7 +36,7 @@ impl IqSetPayload for Query {} impl IqResultPayload for Query {} impl TryFrom for Query { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "query", REGISTER, "IBR query"); diff --git a/src/idle.rs b/src/idle.rs index 89b62c0549f139c4832ea5feafbf9dc77efbe5e3..cba3698b4db829c20cc6afd7232cd7cef1d7e07b 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -25,7 +25,7 @@ mod tests { use minidom::Element; use std::error::Error as StdError; use std::str::FromStr; - use try_from::TryFrom; + use std::convert::TryFrom; #[test] fn test_size() { diff --git a/src/iq.rs b/src/iq.rs index bca785e90af177b7f351c55ea368056ce3913335..c5a195d404b68001960cb65b2edc20791a79c1fe 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -11,7 +11,7 @@ use crate::stanza_error::StanzaError; use jid::Jid; use minidom::Element; use minidom::IntoAttributeValue; -use try_from::TryFrom; +use std::convert::TryFrom; /// Should be implemented on every known payload of an ``. pub trait IqGetPayload: TryFrom + Into {} @@ -130,7 +130,7 @@ impl Iq { } impl TryFrom for Iq { - type Err = Error; + type Error = Error; fn try_from(root: Element) -> Result { check_self!(root, "iq", DEFAULT_NS); diff --git a/src/jingle.rs b/src/jingle.rs index 9edb318ff43b5903366fc0b1ee7c7186ac708c72..ed4fe4a8609e920d87f99e4b28c0a92b610986d1 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -11,7 +11,7 @@ use jid::Jid; use minidom::Element; use std::collections::BTreeMap; use std::str::FromStr; -use try_from::TryFrom; +use std::convert::TryFrom; generate_attribute!( /// The action attribute. @@ -368,7 +368,7 @@ pub struct ReasonElement { } impl TryFrom for ReasonElement { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "reason", JINGLE); @@ -497,7 +497,7 @@ impl Jingle { } impl TryFrom for Jingle { - type Err = Error; + type Error = Error; fn try_from(root: Element) -> Result { check_self!(root, "jingle", JINGLE, "Jingle"); diff --git a/src/jingle_dtls_srtp.rs b/src/jingle_dtls_srtp.rs index 84f646506b3ca265650c3f03e8816ae8dfce000d..e65c368170a229208e130dc0a9f4d8b4fb63449c 100644 --- a/src/jingle_dtls_srtp.rs +++ b/src/jingle_dtls_srtp.rs @@ -50,7 +50,7 @@ generate_element!( mod tests { use super::*; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 3cd9e00abe7fdc94bc94ea778b36c40b1e812eb6..f0b7cf32461fe8e1982bc9a309f67f3fa3db5f74 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -12,7 +12,7 @@ use crate::ns; use minidom::Element; use std::collections::BTreeMap; use std::str::FromStr; -use try_from::TryFrom; +use std::convert::TryFrom; generate_element!( /// Represents a range in a file. @@ -128,7 +128,7 @@ impl File { } impl TryFrom for File { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "file", JINGLE_FT); @@ -253,7 +253,7 @@ pub struct Description { } impl TryFrom for Description { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "description", JINGLE_FT, "JingleFT description"); @@ -301,7 +301,7 @@ pub struct Checksum { } impl TryFrom for Checksum { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "checksum", JINGLE_FT); diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 019f03429f520c8983b1f937b96ba4bf8b68a3e5..d1fe61f9d295271c6582a3285f3e178f32cccb70 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -27,7 +27,7 @@ mod tests { use crate::util::error::Error; use minidom::Element; use std::error::Error as StdError; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/jingle_ice_udp.rs b/src/jingle_ice_udp.rs index 3b386705362b9ee6cb545c29540f74263c5cb19e..323014380d89a17a26d30390ae96e337e58d075c 100644 --- a/src/jingle_ice_udp.rs +++ b/src/jingle_ice_udp.rs @@ -92,7 +92,7 @@ generate_element!( mod tests { use super::*; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; use crate::hashes::Algo; use crate::jingle_dtls_srtp::Setup; diff --git a/src/jingle_message.rs b/src/jingle_message.rs index 8561a649b792a7d422ca10cb7380d6a2aafdf38a..bf0478cf4801aed8018eae1a115a7069ac3626a1 100644 --- a/src/jingle_message.rs +++ b/src/jingle_message.rs @@ -8,7 +8,7 @@ use crate::util::error::Error; use crate::jingle::SessionId; use crate::ns; use minidom::Element; -use try_from::TryFrom; +use std::convert::TryFrom; /// Defines a protocol for broadcasting Jingle requests to all of the clients /// of a user. @@ -48,7 +48,7 @@ fn check_empty_and_get_sid(elem: Element) -> Result { } impl TryFrom for JingleMI { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { if !elem.has_ns(ns::JINGLE_MESSAGE) { diff --git a/src/jingle_rtp.rs b/src/jingle_rtp.rs index 366cfeeca4397a5e5d5d4cb4db12efb3b2b4141d..108010d00404ae87fdc3ccbf4086c146ed6660da 100644 --- a/src/jingle_rtp.rs +++ b/src/jingle_rtp.rs @@ -76,7 +76,7 @@ generate_element!( mod tests { use super::*; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 02b97361352b517dc471c7e1816826370a9ab98a..0aa797211481d754a269176c9a86019754516659 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -9,7 +9,7 @@ use crate::ns; use jid::Jid; use minidom::Element; use std::net::IpAddr; -use try_from::TryFrom; +use std::convert::TryFrom; generate_attribute!( /// The type of the connection being proposed by this candidate. @@ -172,7 +172,7 @@ impl Transport { } impl TryFrom for Transport { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "transport", JINGLE_S5B); diff --git a/src/lib.rs b/src/lib.rs index eb3cc3b4aa52e818df45738a9f4a9fc058cca312..38546ace262a5aaaa1df7a9f206b48040b167e3b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,7 +26,7 @@ pub use minidom::Element; pub use jid::{Jid, JidParseError}; -pub use try_from::{TryFrom, TryInto}; +pub use std::convert::{TryFrom, TryInto}; pub use crate::util::error::Error; /// XML namespace definitions used through XMPP. diff --git a/src/mam.rs b/src/mam.rs index cc606923f19bc15fb9a2f59a658f6ff5c90f3386..bd97ed9902943262f05fc1ef94eabae70ac7cdb9 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -14,7 +14,7 @@ use crate::pubsub::NodeName; use crate::rsm::{SetQuery, SetResult}; use jid::Jid; use minidom::Element; -use try_from::TryFrom; +use std::convert::TryFrom; generate_id!( /// An identifier matching a result message to the query requesting it. @@ -126,7 +126,7 @@ impl IqSetPayload for Prefs {} impl IqResultPayload for Prefs {} impl TryFrom for Prefs { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "prefs", MAM); diff --git a/src/media_element.rs b/src/media_element.rs index 259e9d1226d5a14336f2697c58a4b0113ddfdaa3..f632e48c13428c4e722e9871a62019f07d3f2b9b 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -49,7 +49,7 @@ mod tests { use crate::util::error::Error; use minidom::Element; use std::error::Error as StdError; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/message.rs b/src/message.rs index 29708c32e1d2e710ca27eda4790b6b0f6af7c0ac..582c7f1a70e7763d53c2748302b35b20e544e418 100644 --- a/src/message.rs +++ b/src/message.rs @@ -9,7 +9,7 @@ use crate::ns; use jid::Jid; use minidom::Element; use std::collections::BTreeMap; -use try_from::TryFrom; +use std::convert::TryFrom; /// Should be implemented on every known payload of a ``. pub trait MessagePayload: TryFrom + Into {} @@ -150,7 +150,7 @@ impl Message { } impl TryFrom for Message { - type Err = Error; + type Error = Error; fn try_from(root: Element) -> Result { check_self!(root, "message", DEFAULT_NS); diff --git a/src/message_correct.rs b/src/message_correct.rs index b2600e0b9a021f227b0bef80065fb83fa1778a9f..1055af9bd261bc0e3a9cb0b72c0a5996aab92fed 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -23,7 +23,7 @@ mod tests { use super::*; use crate::util::error::Error; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/mood.rs b/src/mood.rs index da07bf50a296d6a5190c56b6bc24cab306eb11ab..8d27c4f0629db50ff50675003532f7923535e4be 100644 --- a/src/mood.rs +++ b/src/mood.rs @@ -272,7 +272,7 @@ generate_elem_id!( mod tests { use super::*; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 898af67896f657699516615e46d3fcaa50e7a8af..5428a60278e7cd220d10275ba89882f211ac3468 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -98,7 +98,7 @@ mod tests { use crate::util::error::Error; use minidom::Element; use std::str::FromStr; - use try_from::TryFrom; + use std::convert::TryFrom; #[test] fn test_muc_simple() { diff --git a/src/muc/user.rs b/src/muc/user.rs index 8965529fa9486a7629472c188634afb731fd8fa0..bc8012d6b90801239f25601358ed25578ac55eca 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -9,7 +9,7 @@ use crate::util::error::Error; use crate::ns; use jid::Jid; use minidom::Element; -use try_from::TryFrom; +use std::convert::TryFrom; generate_attribute_enum!( /// Lists all of the possible status codes used in MUC presences. @@ -89,7 +89,7 @@ pub enum Actor { } impl TryFrom for Actor { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "actor", MUC_USER); diff --git a/src/nick.rs b/src/nick.rs index 1fbeb1026752abc44799299b2b271f1fa3f450f2..07f07f770125550a41cbd5e53674ff842f8586c1 100644 --- a/src/nick.rs +++ b/src/nick.rs @@ -17,7 +17,7 @@ mod tests { #[cfg(not(feature = "disable-validation"))] use crate::util::error::Error; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/ping.rs b/src/ping.rs index 62bd0b1a2e6c215f644864d43c277833ca84787a..f110088b33edf9a717437ba4b80bcdf2c522b226 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -23,7 +23,7 @@ mod tests { #[cfg(not(feature = "disable-validation"))] use crate::util::error::Error; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[test] fn test_size() { diff --git a/src/presence.rs b/src/presence.rs index c6a1a102974f2e2701e42d89318f9f821c3ca2fa..de2b77c37dd54423c79cceb436c5c24334d32d09 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -11,7 +11,7 @@ use jid::Jid; use minidom::{Element, ElementEmitter, IntoAttributeValue, IntoElements}; use std::collections::BTreeMap; use std::str::FromStr; -use try_from::TryFrom; +use std::convert::TryFrom; /// Should be implemented on every known payload of a ``. pub trait PresencePayload: TryFrom + Into {} @@ -259,7 +259,7 @@ impl Presence { } impl TryFrom for Presence { - type Err = Error; + type Error = Error; fn try_from(root: Element) -> Result { check_self!(root, "presence", DEFAULT_NS); diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 8f6951d9e67081f62ffbfe630553868021b0d2f7..5d65eb783d9411505b26065f8aee07c25e476605 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -11,7 +11,7 @@ use crate::ns; use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId, Item as PubSubItem}; use jid::Jid; use minidom::Element; -use try_from::TryFrom; +use std::convert::TryFrom; /// Event wrapper for a PubSub ``. #[derive(Debug, Clone)] @@ -132,7 +132,7 @@ fn parse_items(elem: Element, node: NodeName) -> Result { } impl TryFrom for PubSubEvent { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "event", PUBSUB_EVENT); diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index 5093b3e1512e692bbd02f47f779e311944ebf0e6..0e166bd3ebcf86cd17e2327f864e5f53a0ba670f 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -11,7 +11,7 @@ use crate::ns; use crate::pubsub::{NodeName, Subscription, SubscriptionId, Item as PubSubItem}; use jid::Jid; use minidom::Element; -use try_from::TryFrom; +use std::convert::TryFrom; // TODO: a better solution would be to split this into a query and a result elements, like for // XEP-0030. @@ -191,7 +191,7 @@ pub struct SubscribeOptions { } impl TryFrom for SubscribeOptions { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "subscribe-options", PUBSUB); @@ -340,7 +340,7 @@ impl IqSetPayload for PubSub {} impl IqResultPayload for PubSub {} impl TryFrom for PubSub { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "pubsub", PUBSUB); diff --git a/src/receipts.rs b/src/receipts.rs index e60900401ec1510473735441f10d17c24be516d3..3b54d3cddb1663a8c4084f15493c29ae12667393 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -33,7 +33,7 @@ mod tests { use super::*; use crate::ns; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/roster.rs b/src/roster.rs index 57d440e97733717d16ff33c86e9c16c8ee938235..65c1fdf57f593b6a927728dfb1a550c99241e62b 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -96,7 +96,7 @@ mod tests { use crate::util::error::Error; use minidom::Element; use std::str::FromStr; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/rsm.rs b/src/rsm.rs index 6b65e3b48d9cda6f193fb96005293f0cd4d46f1d..a4f97365388da78a6e47e3ebc7555afd0a2623f2 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -7,7 +7,7 @@ use crate::util::error::Error; use crate::ns; use minidom::Element; -use try_from::TryFrom; +use std::convert::TryFrom; /// Requests paging through a potentially big set of items (represented by an /// UID). @@ -29,7 +29,7 @@ pub struct SetQuery { } impl TryFrom for SetQuery { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "set", RSM, "RSM set"); @@ -116,7 +116,7 @@ pub struct SetResult { } impl TryFrom for SetResult { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "set", RSM, "RSM set"); diff --git a/src/sasl.rs b/src/sasl.rs index ec84fe6cb99df44fa0ea8cf9b76111239eca044b..3deed9e1aa6b0fface1dcd41319f8eafc96641c0 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -9,7 +9,7 @@ use crate::util::helpers::Base64; use crate::ns; use minidom::Element; use std::collections::BTreeMap; -use try_from::TryFrom; +use std::convert::TryFrom; generate_attribute!( /// The list of available SASL mechanisms. @@ -151,7 +151,7 @@ pub struct Failure { } impl TryFrom for Failure { - type Err = Error; + type Error = Error; fn try_from(root: Element) -> Result { check_self!(root, "failure", SASL); @@ -224,7 +224,7 @@ impl From for Element { mod tests { use super::*; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/server_info.rs b/src/server_info.rs index 53cef3838bcd2cc55ad1e75de8827f8863183049..3ad9e0008786092f03391d6a1617095447ec54dc 100644 --- a/src/server_info.rs +++ b/src/server_info.rs @@ -6,7 +6,7 @@ use crate::data_forms::{DataForm, DataFormType, Field, FieldType}; use crate::ns; use crate::util::error::Error; -use try_from::TryFrom; +use std::convert::TryFrom; /// Structure representing a `http://jabber.org/network/serverinfo` form type. #[derive(Debug, Clone, PartialEq, Default)] @@ -31,7 +31,7 @@ pub struct ServerInfo { } impl TryFrom for ServerInfo { - type Err = Error; + type Error = Error; fn try_from(form: DataForm) -> Result { if form.type_ != DataFormType::Result_ { diff --git a/src/sm.rs b/src/sm.rs index 661d05b9d0c3f5f73e30a2a47cd8787f7dc84b28..b483514023031ca34bdf02fe07ddf1c75daacdfb 100644 --- a/src/sm.rs +++ b/src/sm.rs @@ -146,7 +146,7 @@ generate_empty_element!( mod tests { use super::*; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/stanza_error.rs b/src/stanza_error.rs index dcaf5cb884f6146839ccf557426eef424cfa3960..f2ae500e2592ed59662542e9e20d839bd2c14e8c 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -11,7 +11,7 @@ use crate::presence::PresencePayload; use jid::Jid; use minidom::Element; use std::collections::BTreeMap; -use try_from::TryFrom; +use std::convert::TryFrom; generate_attribute!( /// The type of the error. @@ -236,7 +236,7 @@ impl StanzaError { } impl TryFrom for StanzaError { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "error", DEFAULT_NS); diff --git a/src/stanza_id.rs b/src/stanza_id.rs index dd8ed9e0a125044eb9587f5364793730f1a12127..2178d7a94014034f91b14f3d8c7d6859ed31265d 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -40,7 +40,7 @@ mod tests { use crate::util::error::Error; use minidom::Element; use std::str::FromStr; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/stream.rs b/src/stream.rs index a77a73ea8ca36f59a55df520e085aa29cf9dff85..1d4715f2d1c164cfc158500f12348bccfd808ab1 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -74,7 +74,7 @@ impl Stream { mod tests { use super::*; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/util/macros.rs b/src/util/macros.rs index f4b08335fa29301d1d263e4315ea3938dfdfaa2a..010cde52bb48675b7ba8a43cbb2b45365c734923 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -230,8 +230,8 @@ macro_rules! generate_element_enum { $enum ),+ } - impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = crate::util::error::Error; + impl ::std::convert::TryFrom<::minidom::Element> for $elem { + type Error = crate::util::error::Error; fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); @@ -269,8 +269,8 @@ macro_rules! generate_attribute_enum { $enum ),+ } - impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = crate::util::error::Error; + impl ::std::convert::TryFrom<::minidom::Element> for $elem { + type Error = crate::util::error::Error; fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); @@ -367,8 +367,8 @@ macro_rules! generate_empty_element { #[derive(Debug, Clone)] pub struct $elem; - impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = crate::util::error::Error; + impl ::std::convert::TryFrom<::minidom::Element> for $elem { + type Error = crate::util::error::Error; fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { check_self!(elem, $name, $ns); @@ -420,8 +420,8 @@ macro_rules! generate_elem_id { Ok($elem(String::from(s))) } } - impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = crate::util::error::Error; + impl ::std::convert::TryFrom<::minidom::Element> for $elem { + type Error = crate::util::error::Error; fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); @@ -600,8 +600,8 @@ macro_rules! generate_element { )* } - impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = crate::util::error::Error; + impl ::std::convert::TryFrom<::minidom::Element> for $elem { + type Error = crate::util::error::Error; fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { check_self!(elem, $name, $ns); @@ -662,7 +662,7 @@ macro_rules! assert_size ( macro_rules! impl_pubsub_item { ($item:ident, $ns:ident) => { impl crate::TryFrom for $item { - type Err = Error; + type Error = Error; fn try_from(elem: crate::Element) -> Result<$item, Error> { check_self!(elem, "item", $ns); diff --git a/src/version.rs b/src/version.rs index 5aa4d2fbb2acbfe5e3d3d2e8207e57d43958a195..afc617897adf033e0dfd46865165e04af4e551f2 100644 --- a/src/version.rs +++ b/src/version.rs @@ -43,7 +43,7 @@ mod tests { use super::*; use crate::util::compare_elements::NamespaceAwareCompare; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/websocket.rs b/src/websocket.rs index 03a1d0401b97103d42e81ba45f10f110dae3bea5..b8c02e2b44f736f76c9447e97d25ebcef6e16bcb 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -73,7 +73,7 @@ impl Open { mod tests { use super::*; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] From 8db6b5602eda1c50b7ba2b7cb824d8c27ef76d45 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 12 Apr 2019 12:15:40 +0200 Subject: [PATCH 621/698] Cargo.toml, ChangeLog: Release version 0.13.1. --- Cargo.toml | 2 +- ChangeLog | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 36ed320c4e0249f5a33d6830a166d41688366258..08e98139f06bec43948da8873f3a6715e074fa6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.13.0" +version = "0.13.1" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", diff --git a/ChangeLog b/ChangeLog index 12a14a0150956cedea332ec69094af3bd6174548..f0129488451a36c12b719a150a95dc5792e2633d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +Version 0.13.1: +2019-04-12 Emmanuel Gil Peyrot + * Bugfixes: + - Fix invalid serialisation of priority in presence. + - Bump image size to u16 from u8, as per XEP-0084 version 1.1.2. + * Improvements: + - Drop try_from dependency, as std::convert::TryFrom got + stabilised. + Version 0.13.0: 2019-03-20 Emmanuel Gil Peyrot * New parsers/serialisers: From 2d7bf32ad452cea31d9c64e71142cb2a0179e911 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 22 Apr 2019 11:58:13 +0200 Subject: [PATCH 622/698] Add a XEP-0202 implementation. Fixes #7. --- ChangeLog | 5 +++ src/date.rs | 15 +++++++ src/lib.rs | 3 ++ src/ns.rs | 3 ++ src/time.rs | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 138 insertions(+) create mode 100644 src/time.rs diff --git a/ChangeLog b/ChangeLog index f0129488451a36c12b719a150a95dc5792e2633d..82253779eb3c2f54efcbd543c328a72cbd5a8632 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Version TODO: +TODO Emmanuel Gil Peyrot + * New parsers/serialisers: + - Entity Time (XEP-0202). + Version 0.13.1: 2019-04-12 Emmanuel Gil Peyrot * Bugfixes: diff --git a/src/date.rs b/src/date.rs index 1d3389b0651623ffc7ec802a64e9de6106032b1a..f50a1c4f55eda931c0c3c0a23977879d988fde32 100644 --- a/src/date.rs +++ b/src/date.rs @@ -15,6 +15,20 @@ use std::str::FromStr; #[derive(Debug, Clone, PartialEq)] pub struct DateTime(ChronoDateTime); +impl DateTime { + pub fn timezone(&self) -> FixedOffset { + self.0.timezone() + } + + pub fn with_timezone(&self, tz: &FixedOffset) -> DateTime { + DateTime(self.0.with_timezone(tz)) + } + + pub fn format(&self, fmt: &str) -> String { + format!("{}", self.0.format(fmt)) + } +} + impl FromStr for DateTime { type Err = Error; @@ -41,6 +55,7 @@ mod tests { use chrono::{Datelike, Timelike}; use std::error::Error as StdError; + // DateTime’s size doesn’t depend on the architecture. #[test] fn test_size() { assert_size!(DateTime, 16); diff --git a/src/lib.rs b/src/lib.rs index 38546ace262a5aaaa1df7a9f206b48040b167e3b..ebeddf2faf51dc7de0388affc41c9698a4b359b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -128,6 +128,9 @@ pub mod sm; /// XEP-0199: XMPP Ping pub mod ping; +/// XEP-0202: Entity Time +pub mod time; + /// XEP-0203: Delayed Delivery pub mod delay; diff --git a/src/ns.rs b/src/ns.rs index 4f3d2d299d3dcd1f756427b03603fd8b0225aff6..978a85233d6557ffe1e4f4ddf27f2aa85175dde8 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -112,6 +112,9 @@ pub const SM: &str = "urn:xmpp:sm:3"; /// XEP-0199: XMPP Ping pub const PING: &str = "urn:xmpp:ping"; +/// XEP-0202: Entity Time +pub const TIME: &str = "urn:xmpp:time"; + /// XEP-0203: Delayed Delivery pub const DELAY: &str = "urn:xmpp:delay"; diff --git a/src/time.rs b/src/time.rs new file mode 100644 index 0000000000000000000000000000000000000000..c107393856d8559f926658143d6a8d240ff25b33 --- /dev/null +++ b/src/time.rs @@ -0,0 +1,112 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use chrono::FixedOffset; +use crate::date::DateTime; +use crate::iq::{IqGetPayload, IqResultPayload}; +use crate::ns; +use crate::util::error::Error; +use minidom::Element; +use std::convert::TryFrom; +use std::str::FromStr; + +generate_empty_element!( + TimeQuery, "time", TIME +); + +impl IqGetPayload for TimeQuery {} + +#[derive(Debug, Clone)] +pub struct TimeResult(pub DateTime); + +impl IqResultPayload for TimeResult {} + +impl TryFrom for TimeResult { + type Error = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "time", TIME); + check_no_attributes!(elem, "time"); + + let mut tzo = None; + let mut utc = None; + + for child in elem.children() { + if child.is("tzo", ns::TIME) { + if tzo.is_some() { + return Err(Error::ParseError("More than one tzo element in time.")); + } + check_no_children!(child, "tzo"); + check_no_attributes!(child, "tzo"); + // TODO: Add a FromStr implementation to FixedOffset to avoid this hack. + let fake_date = String::from("2019-04-22T11:38:00") + &child.text(); + let date_time = DateTime::from_str(&fake_date)?; + tzo = Some(date_time.timezone()); + } else if child.is("utc", ns::TIME) { + if utc.is_some() { + return Err(Error::ParseError("More than one utc element in time.")); + } + check_no_children!(child, "utc"); + check_no_attributes!(child, "utc"); + let date_time = DateTime::from_str(&child.text())?; + if date_time.timezone() != FixedOffset::east(0) { + return Err(Error::ParseError("Non-UTC timezone for utc element.")); + } + utc = Some(date_time); + } else { + return Err(Error::ParseError( + "Unknown child in time element.", + )); + } + } + + let tzo = tzo.ok_or(Error::ParseError("Missing tzo child in time element."))?; + let utc = utc.ok_or(Error::ParseError("Missing utc child in time element."))?; + let date = utc.with_timezone(&tzo); + + Ok(TimeResult(date)) + } +} + +impl From for Element { + fn from(time: TimeResult) -> Element { + Element::builder("time") + .ns(ns::TIME) + .append(Element::builder("tzo") + .append(format!("{}", time.0.timezone())) + .build()) + .append(Element::builder("utc") + .append(time.0.with_timezone(&FixedOffset::east(0)).format("%FT%TZ")) + .build()) + .build() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + // DateTime’s size doesn’t depend on the architecture. + #[test] + fn test_size() { + assert_size!(TimeQuery, 0); + assert_size!(TimeResult, 16); + } + + #[test] + fn parse_response() { + let elem: Element = + "" + .parse() + .unwrap(); + let elem1 = elem.clone(); + let time = TimeResult::try_from(elem).unwrap(); + assert_eq!(time.0.timezone(), FixedOffset::west(6 * 3600)); + assert_eq!(time.0, DateTime::from_str("2006-12-19T12:58:35-05:00").unwrap()); + let elem2 = Element::from(time); + assert_eq!(elem1, elem2); + } +} From e1bd0086f19d6da0c1b9980e65c2fd0dc965abfc Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 22 Apr 2019 13:33:29 +0200 Subject: [PATCH 623/698] time, date: Add missing docs. --- src/date.rs | 3 +++ src/time.rs | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/date.rs b/src/date.rs index f50a1c4f55eda931c0c3c0a23977879d988fde32..73dd3bd7e9dbb7777a376afd2ae8d2c4328663f2 100644 --- a/src/date.rs +++ b/src/date.rs @@ -16,14 +16,17 @@ use std::str::FromStr; pub struct DateTime(ChronoDateTime); impl DateTime { + /// Retrieves the associated timezone. pub fn timezone(&self) -> FixedOffset { self.0.timezone() } + /// Returns a new `DateTime` with a different timezone. pub fn with_timezone(&self, tz: &FixedOffset) -> DateTime { DateTime(self.0.with_timezone(tz)) } + /// Formats this `DateTime` with the specified format string. pub fn format(&self, fmt: &str) -> String { format!("{}", self.0.format(fmt)) } diff --git a/src/time.rs b/src/time.rs index c107393856d8559f926658143d6a8d240ff25b33..91866660acaf01c034bd3e0e3169d53b6cba77d0 100644 --- a/src/time.rs +++ b/src/time.rs @@ -14,11 +14,13 @@ use std::convert::TryFrom; use std::str::FromStr; generate_empty_element!( + /// An entity time query. TimeQuery, "time", TIME ); impl IqGetPayload for TimeQuery {} +/// An entity time result, containing an unique DateTime. #[derive(Debug, Clone)] pub struct TimeResult(pub DateTime); From dabdc1db68330e36d85850ee6c9a31134c88c181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Mon, 22 Apr 2019 12:43:22 +0100 Subject: [PATCH 624/698] Fix rustdoc-args option for docs.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 08e98139f06bec43948da8873f3a6715e074fa6b..69b94a5725af853a6f10ab74c451d9cb360a5f6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,4 +31,4 @@ component = [] disable-validation = [] [package.metadata.docs.rs] -rustdoc-args = [ "--sort-modules-by-appearance", "-Z unstable-options" ] +rustdoc-args = [ "--sort-modules-by-appearance", "-Zunstable-options" ] From e6542fdb6be9702f8d4a8c2b6a60b7e1fda22a80 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 9 May 2019 16:21:28 +0200 Subject: [PATCH 625/698] =?UTF-8?q?ibr:=20Ignore=20size=20tests,=20HashMap?= =?UTF-8?q?=20implementation=20changed=20and=20this=20added=2016=C2=A0byte?= =?UTF-8?q?s=20to=20every=20instance.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ibr.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ibr.rs b/src/ibr.rs index 911b800228b76fe5b2038b1e0198df01e3ae9e1a..2d41a6fa2eb1deb86b70c50a778f460155d4d621 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -120,14 +120,21 @@ mod tests { use super::*; use crate::util::compare_elements::NamespaceAwareCompare; + // TODO: These size tests are sensible to the size of HashMap, which recently grew of two + // pointers and is thus different on stable and nightly. Let’s wait for this issue before + // attempting a fix: + // https://github.com/rust-lang/hashbrown/issues/69 + #[cfg(target_pointer_width = "32")] #[test] + #[ignore] fn test_size() { assert_size!(Query, 88); } #[cfg(target_pointer_width = "64")] #[test] + #[ignore] fn test_size() { assert_size!(Query, 152); } From 80bb6635a9e17b17bf2d601cdf78e572ab6ec7a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Fri, 19 Apr 2019 23:03:50 +0100 Subject: [PATCH 626/698] ns: add XEP-0277 microblog namespace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- src/ns.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ns.rs b/src/ns.rs index 978a85233d6557ffe1e4f4ddf27f2aa85175dde8..9d59435d2b48db9b92965bcf537b097f71e76292 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -135,6 +135,9 @@ pub const JINGLE_S5B: &str = "urn:xmpp:jingle:transports:s5b:1"; /// XEP-0261: Jingle In-Band Bytestreams Transport Method pub const JINGLE_IBB: &str = "urn:xmpp:jingle:transports:ibb:1"; +/// XEP-0277: Microblogging over XMPP +pub const MICROBLOG: &str = "urn:xmpp:microblog:0"; + /// XEP-0297: Stanza Forwarding pub const FORWARD: &str = "urn:xmpp:forward:0"; From 1e3f940db9f812591d7b34f9e68989b9079ec6ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Mon, 10 Jun 2019 23:17:09 +0200 Subject: [PATCH 627/698] Update jid dependency to 0.6.0: Jid split change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- Cargo.toml | 2 +- src/bind.rs | 6 +++--- src/blocking.rs | 11 +++++------ src/bookmarks.rs | 6 +++--- src/lib.rs | 6 +++--- src/muc/user.rs | 10 +++++----- src/roster.rs | 14 +++++++------- src/stream.rs | 12 ++++++------ src/websocket.rs | 10 +++++----- 9 files changed, 38 insertions(+), 39 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 69b94a5725af853a6f10ab74c451d9cb360a5f6f..b1e84c36487e3eaff92260380c7c84b53db56a6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ edition = "2018" [dependencies] minidom = "0.10.0" -jid = { version = "0.5.3", features = ["minidom"] } +jid = { version = "0.6.0", features = ["minidom"] } base64 = "0.10" digest = "0.8" sha-1 = "0.8" diff --git a/src/bind.rs b/src/bind.rs index 1d509638c8fb0545f80a996bcc81a4d8f652e69b..408c08bb56ce4e2b3612d07b394561646d4e7b38 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -7,7 +7,7 @@ use crate::util::error::Error; use crate::iq::{IqResultPayload, IqSetPayload}; use crate::ns; -use jid::Jid; +use jid::FullJid; use minidom::Element; use std::str::FromStr; use std::convert::TryFrom; @@ -26,7 +26,7 @@ pub enum Bind { Resource(String), /// The full JID returned by the server for this client. - Jid(Jid), + Jid(FullJid), } impl Bind { @@ -61,7 +61,7 @@ impl TryFrom for Bind { } else if child.is("jid", ns::BIND) { check_no_attributes!(child, "jid"); check_no_children!(child, "jid"); - bind = Bind::Jid(Jid::from_str(&child.text())?); + bind = Bind::Jid(FullJid::from_str(&child.text())?); } else { return Err(Error::ParseError("Unknown element in bind.")); } diff --git a/src/blocking.rs b/src/blocking.rs index b44c4111936b1c2af458f09bc8383c2a01f6351a..f218b21d4985dd4894d56d1c3e1e9dcc34c16902 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -101,6 +101,7 @@ generate_empty_element!( #[cfg(test)] mod tests { use super::*; + use jid::BareJid; #[cfg(target_pointer_width = "32")] #[test] @@ -143,16 +144,14 @@ mod tests { fn test_items() { let elem: Element = "".parse().unwrap(); let two_items = vec![ - Jid { + Jid::Bare(BareJid { node: Some(String::from("coucou")), domain: String::from("coucou"), - resource: None, - }, - Jid { + }), + Jid::Bare(BareJid { node: None, domain: String::from("domain"), - resource: None, - }, + }), ]; let result_elem = elem.clone(); diff --git a/src/bookmarks.rs b/src/bookmarks.rs index d8b5a9ccc1bbce7141144df5ca699ef1e2bd9754..1424686b0fcbf7a75402e3ff51b348a4d74f29cd 100644 --- a/src/bookmarks.rs +++ b/src/bookmarks.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use jid::Jid; +use jid::BareJid; generate_attribute!( /// Whether a conference bookmark should be joined automatically. @@ -21,7 +21,7 @@ generate_element!( autojoin: Default = "autojoin", /// The JID of the conference. - jid: Required = "jid", + jid: Required = "jid", /// A user-defined name for this conference. name: Required = "name", @@ -113,7 +113,7 @@ mod tests { assert_eq!(storage.conferences[0].autojoin, Autojoin::True); assert_eq!( storage.conferences[0].jid, - Jid::bare("test-muc", "muc.localhost") + BareJid::new("test-muc", "muc.localhost") ); assert_eq!(storage.conferences[0].name, "Test MUC"); assert_eq!(storage.conferences[0].clone().nick.unwrap(), "Coucou"); diff --git a/src/lib.rs b/src/lib.rs index ebeddf2faf51dc7de0388affc41c9698a4b359b7..6863afec8de05478a5f207251cf55c3cb75b37ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,8 +15,8 @@ //! [`TryFrom`]: ../try_from/trait.TryFrom.html //! [`Element`]: ../minidom/element/struct.Element.html -// Copyright (c) 2017-2018 Emmanuel Gil Peyrot -// Copyright (c) 2017 Maxime “pep” Buquet +// Copyright (c) 2017-2019 Emmanuel Gil Peyrot +// Copyright (c) 2017-2019 Maxime “pep” Buquet // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -25,7 +25,7 @@ #![deny(missing_docs)] pub use minidom::Element; -pub use jid::{Jid, JidParseError}; +pub use jid::{Jid, BareJid, FullJid, JidParseError}; pub use std::convert::{TryFrom, TryInto}; pub use crate::util::error::Error; diff --git a/src/muc/user.rs b/src/muc/user.rs index bc8012d6b90801239f25601358ed25578ac55eca..3621450ab89263a396d5a77a0c417d40a286aa18 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -7,7 +7,7 @@ use crate::util::error::Error; use crate::ns; -use jid::Jid; +use jid::FullJid; use minidom::Element; use std::convert::TryFrom; @@ -82,7 +82,7 @@ Status, "status", MUC_USER, "code", { #[derive(Debug, Clone, PartialEq)] pub enum Actor { /// The full JID associated with this user. - Jid(Jid), + Jid(FullJid), /// The nickname of this user. Nick(String), @@ -95,7 +95,7 @@ impl TryFrom for Actor { check_self!(elem, "actor", MUC_USER); check_no_unknown_attributes!(elem, "actor", ["jid", "nick"]); check_no_children!(elem, "actor"); - let jid: Option = get_attr!(elem, "jid", Option); + let jid: Option = get_attr!(elem, "jid", Option); let nick = get_attr!(elem, "nick", Option); match (jid, nick) { @@ -190,7 +190,7 @@ generate_element!( affiliation: Required = "affiliation", /// The real JID of this user, if you are allowed to see it. - jid: Option = "jid", + jid: Option = "jid", /// The current nickname of this user. nick: Option = "nick", @@ -448,7 +448,7 @@ mod tests { Actor::Jid(jid) => jid, _ => panic!(), }; - assert_eq!(jid, "foo@bar/baz".parse::().unwrap()); + assert_eq!(jid, "foo@bar/baz".parse::().unwrap()); } #[test] diff --git a/src/roster.rs b/src/roster.rs index 65c1fdf57f593b6a927728dfb1a550c99241e62b..dc615c786f3a6174d15145b8421f14c6766bc291 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -5,7 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; -use jid::Jid; +use jid::BareJid; generate_elem_id!( /// Represents a group a contact is part of. @@ -50,7 +50,7 @@ generate_element!( Item, "item", ROSTER, attributes: [ /// JID of this contact. - jid: Required = "jid", + jid: Required = "jid", /// Name of this contact. name: OptionEmpty = "name", @@ -172,7 +172,7 @@ mod tests { assert_eq!(roster.items.len(), 4); assert_eq!( roster.items[0].jid, - Jid::from_str("romeo@example.net").unwrap() + BareJid::from_str("romeo@example.net").unwrap() ); assert_eq!(roster.items[0].name, Some(String::from("Romeo"))); assert_eq!(roster.items[0].subscription, Subscription::Both); @@ -184,7 +184,7 @@ mod tests { assert_eq!( roster.items[3].jid, - Jid::from_str("contact@example.org").unwrap() + BareJid::from_str("contact@example.org").unwrap() ); assert_eq!(roster.items[3].name, Some(String::from("MyContact"))); assert_eq!(roster.items[3].subscription, Subscription::None); @@ -213,7 +213,7 @@ mod tests { assert_eq!(roster.items.len(), 1); assert_eq!( roster.items[0].jid, - Jid::from_str("test@example.org").unwrap() + BareJid::from_str("test@example.org").unwrap() ); assert_eq!(roster.items[0].name, None); assert_eq!(roster.items[0].groups.len(), 2); @@ -248,7 +248,7 @@ mod tests { assert_eq!(roster.items.len(), 1); assert_eq!( roster.items[0].jid, - Jid::from_str("nurse@example.com").unwrap() + BareJid::from_str("nurse@example.com").unwrap() ); assert_eq!(roster.items[0].name, Some(String::from("Nurse"))); assert_eq!(roster.items[0].groups.len(), 1); @@ -270,7 +270,7 @@ mod tests { assert_eq!(roster.items.len(), 1); assert_eq!( roster.items[0].jid, - Jid::from_str("nurse@example.com").unwrap() + BareJid::from_str("nurse@example.com").unwrap() ); assert!(roster.items[0].name.is_none()); assert!(roster.items[0].groups.is_empty()); diff --git a/src/stream.rs b/src/stream.rs index 1d4715f2d1c164cfc158500f12348bccfd808ab1..021b64251c897d6b68bc135a0a5d827f309b8025 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -4,17 +4,17 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use jid::Jid; +use jid::BareJid; generate_element!( /// The stream opening for client-server communications. Stream, "stream", STREAM, attributes: [ /// The JID of the entity opening this stream. - from: Option = "from", + from: Option = "from", /// The JID of the entity receiving this stream opening. - to: Option = "to", + to: Option = "to", /// The id of the stream, used for authentication challenges. id: Option = "id", @@ -30,7 +30,7 @@ generate_element!( impl Stream { /// Creates a simple client→server `` element. - pub fn new(to: Jid) -> Stream { + pub fn new(to: BareJid) -> Stream { Stream { from: None, to: Some(to), @@ -42,7 +42,7 @@ impl Stream { /// Sets the [@from](#structfield.from) attribute on this `` /// element. - pub fn with_from(mut self, from: Jid) -> Stream { + pub fn with_from(mut self, from: BareJid) -> Stream { self.from = Some(from); self } @@ -92,7 +92,7 @@ mod tests { fn test_simple() { let elem: Element = "".parse().unwrap(); let stream = Stream::try_from(elem).unwrap(); - assert_eq!(stream.from, Some(Jid::domain("some-server.example"))); + assert_eq!(stream.from, Some(BareJid::domain("some-server.example"))); assert_eq!(stream.to, None); assert_eq!(stream.id, Some(String::from("abc"))); assert_eq!(stream.version, Some(String::from("1.0"))); diff --git a/src/websocket.rs b/src/websocket.rs index b8c02e2b44f736f76c9447e97d25ebcef6e16bcb..dc2670f74ed100744eec3b3dc76d6255be7c8a0d 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -4,17 +4,17 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use jid::Jid; +use jid::BareJid; generate_element!( /// The stream opening for WebSocket. Open, "open", WEBSOCKET, attributes: [ /// The JID of the entity opening this stream. - from: Option = "from", + from: Option = "from", /// The JID of the entity receiving this stream opening. - to: Option = "to", + to: Option = "to", /// The id of the stream, used for authentication challenges. id: Option = "id", @@ -30,7 +30,7 @@ generate_element!( impl Open { /// Creates a simple client→server `` element. - pub fn new(to: Jid) -> Open { + pub fn new(to: BareJid) -> Open { Open { from: None, to: Some(to), @@ -42,7 +42,7 @@ impl Open { /// Sets the [@from](#structfield.from) attribute on this `` /// element. - pub fn with_from(mut self, from: Jid) -> Open { + pub fn with_from(mut self, from: BareJid) -> Open { self.from = Some(from); self } From 3178aaa1cbb2177b94513b2cef65db899d8d4229 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Mon, 22 Apr 2019 11:24:11 +0100 Subject: [PATCH 628/698] Update test_size tests 64bit archs with jid-rs change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- src/bookmarks.rs | 2 +- src/delay.rs | 2 +- src/disco.rs | 2 +- src/forwarding.rs | 2 +- src/iq.rs | 4 ++-- src/jingle.rs | 2 +- src/jingle_s5b.rs | 2 +- src/mam.rs | 2 +- src/message.rs | 2 +- src/presence.rs | 2 +- src/roster.rs | 2 +- src/stanza_error.rs | 2 +- src/stanza_id.rs | 2 +- src/stream.rs | 2 +- src/websocket.rs | 2 +- 15 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/bookmarks.rs b/src/bookmarks.rs index 1424686b0fcbf7a75402e3ff51b348a4d74f29cd..b40482c6aa4b3c683069ec32cd56e150b6a99c07 100644 --- a/src/bookmarks.rs +++ b/src/bookmarks.rs @@ -85,7 +85,7 @@ mod tests { #[cfg(target_pointer_width = "64")] #[test] fn test_size() { - assert_size!(Conference, 152); + assert_size!(Conference, 128); assert_size!(Url, 48); assert_size!(Storage, 48); } diff --git a/src/delay.rs b/src/delay.rs index 97a32d2598933c069a0215d152e42ae5ae4b93c4..892578c2e8619b5aa35d596f0b6e3bf21eb620c0 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -46,7 +46,7 @@ mod tests { #[cfg(target_pointer_width = "64")] #[test] fn test_size() { - assert_size!(Delay, 112); + assert_size!(Delay, 120); } #[test] diff --git a/src/disco.rs b/src/disco.rs index 9d8781dcdb95d99ae78f7178b68e6620c5e66fba..2cacc2a752510d559e7378ef621c3a4d36026d1a 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -256,7 +256,7 @@ mod tests { assert_size!(DiscoInfoQuery, 24); assert_size!(DiscoInfoResult, 96); - assert_size!(Item, 120); + assert_size!(Item, 128); assert_size!(DiscoItemsQuery, 24); assert_size!(DiscoItemsResult, 48); } diff --git a/src/forwarding.rs b/src/forwarding.rs index 4adb47964422e79070a494dcb8b1264c567e7ced..906f016e9ea95c3c0ac1e4ba70aadb4de473e866 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -39,7 +39,7 @@ mod tests { #[cfg(target_pointer_width = "64")] #[test] fn test_size() { - assert_size!(Forwarded, 392); + assert_size!(Forwarded, 408); } #[test] diff --git a/src/iq.rs b/src/iq.rs index c5a195d404b68001960cb65b2edc20791a79c1fe..68d585d087a393311608094a4dcb5aadbf742f8f 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -232,8 +232,8 @@ mod tests { #[cfg(target_pointer_width = "64")] #[test] fn test_size() { - assert_size!(IqType, 216); - assert_size!(Iq, 384); + assert_size!(IqType, 224); + assert_size!(Iq, 408); } #[test] diff --git a/src/jingle.rs b/src/jingle.rs index ed4fe4a8609e920d87f99e4b28c0a92b610986d1..14574fdb47d84d7f0bdf0454140f7d8eef247447 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -579,7 +579,7 @@ mod tests { assert_size!(Reason, 1); assert_size!(ReasonElement, 32); assert_size!(SessionId, 24); - assert_size!(Jingle, 256); + assert_size!(Jingle, 272); } #[test] diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 0aa797211481d754a269176c9a86019754516659..a34b5e319b43a6553a90f0a4a5ed449ae27a92d5 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -297,7 +297,7 @@ mod tests { assert_size!(Mode, 1); assert_size!(CandidateId, 24); assert_size!(StreamId, 24); - assert_size!(Candidate, 128); + assert_size!(Candidate, 136); assert_size!(TransportPayload, 32); assert_size!(Transport, 88); } diff --git a/src/mam.rs b/src/mam.rs index bd97ed9902943262f05fc1ef94eabae70ac7cdb9..f0bd0448b30ee1423dba5a240e4149185c75a594 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -211,7 +211,7 @@ mod tests { fn test_size() { assert_size!(QueryId, 24); assert_size!(Query, 232); - assert_size!(Result_, 440); + assert_size!(Result_, 456); assert_size!(Complete, 1); assert_size!(Fin, 88); assert_size!(DefaultPrefs, 1); diff --git a/src/message.rs b/src/message.rs index 582c7f1a70e7763d53c2748302b35b20e544e418..fb11e91d98ef614476e2c3ac49c69fd813ef0a2c 100644 --- a/src/message.rs +++ b/src/message.rs @@ -274,7 +274,7 @@ mod tests { assert_size!(Body, 24); assert_size!(Subject, 24); assert_size!(Thread, 24); - assert_size!(Message, 272); + assert_size!(Message, 288); } #[test] diff --git a/src/presence.rs b/src/presence.rs index de2b77c37dd54423c79cceb436c5c24334d32d09..b4e1a1e046b2f36761a560ce175c6edad26a116a 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -376,7 +376,7 @@ mod tests { fn test_size() { assert_size!(Show, 1); assert_size!(Type, 1); - assert_size!(Presence, 224); + assert_size!(Presence, 240); } #[test] diff --git a/src/roster.rs b/src/roster.rs index dc615c786f3a6174d15145b8421f14c6766bc291..073ecfa7e770462e92ec9f519a306d87c04fa05d 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -114,7 +114,7 @@ mod tests { assert_size!(Group, 24); assert_size!(Subscription, 1); assert_size!(Ask, 1); - assert_size!(Item, 128); + assert_size!(Item, 104); assert_size!(Roster, 48); } diff --git a/src/stanza_error.rs b/src/stanza_error.rs index f2ae500e2592ed59662542e9e20d839bd2c14e8c..18087bafa385e5265c0f0cc2647bcc44a392b461 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -324,7 +324,7 @@ mod tests { fn test_size() { assert_size!(ErrorType, 1); assert_size!(DefinedCondition, 1); - assert_size!(StanzaError, 208); + assert_size!(StanzaError, 216); } #[test] diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 2178d7a94014034f91b14f3d8c7d6859ed31265d..eac29085f0a828818ef448f1b0a3f9cef6958111 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -52,7 +52,7 @@ mod tests { #[cfg(target_pointer_width = "64")] #[test] fn test_size() { - assert_size!(StanzaId, 96); + assert_size!(StanzaId, 104); assert_size!(OriginId, 24); } diff --git a/src/stream.rs b/src/stream.rs index 021b64251c897d6b68bc135a0a5d827f309b8025..112f1b94ac4572f9176009eb6802c9fd3b445bdf 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -85,7 +85,7 @@ mod tests { #[cfg(target_pointer_width = "64")] #[test] fn test_size() { - assert_size!(Stream, 216); + assert_size!(Stream, 168); } #[test] diff --git a/src/websocket.rs b/src/websocket.rs index dc2670f74ed100744eec3b3dc76d6255be7c8a0d..d94d81fd1f89c4321ca1be9effa36f891dba788d 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -84,7 +84,7 @@ mod tests { #[cfg(target_pointer_width = "64")] #[test] fn test_size() { - assert_size!(Open, 216); + assert_size!(Open, 168); } #[test] From bc480f8e7c966a4c9bdc1a97896268c5e78771fd Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 22 Apr 2019 14:34:25 +0200 Subject: [PATCH 629/698] Update test_size tests for 32-bit architectures --- src/bookmarks.rs | 2 +- src/delay.rs | 2 +- src/disco.rs | 2 +- src/forwarding.rs | 2 +- src/iq.rs | 4 ++-- src/jingle.rs | 2 +- src/jingle_s5b.rs | 2 +- src/mam.rs | 2 +- src/message.rs | 2 +- src/presence.rs | 2 +- src/roster.rs | 2 +- src/stanza_error.rs | 2 +- src/stanza_id.rs | 2 +- src/stream.rs | 2 +- src/websocket.rs | 2 +- 15 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/bookmarks.rs b/src/bookmarks.rs index b40482c6aa4b3c683069ec32cd56e150b6a99c07..c00195a526bd6b8568d94fe241e5660916a8d854 100644 --- a/src/bookmarks.rs +++ b/src/bookmarks.rs @@ -77,7 +77,7 @@ mod tests { #[cfg(target_pointer_width = "32")] #[test] fn test_size() { - assert_size!(Conference, 76); + assert_size!(Conference, 64); assert_size!(Url, 24); assert_size!(Storage, 24); } diff --git a/src/delay.rs b/src/delay.rs index 892578c2e8619b5aa35d596f0b6e3bf21eb620c0..e987a4f0e013352c8221ce586f4c67fc49d74df9 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -40,7 +40,7 @@ mod tests { #[cfg(target_pointer_width = "32")] #[test] fn test_size() { - assert_size!(Delay, 64); + assert_size!(Delay, 68); } #[cfg(target_pointer_width = "64")] diff --git a/src/disco.rs b/src/disco.rs index 2cacc2a752510d559e7378ef621c3a4d36026d1a..abf0e238f152afcdab91ca8eb270d9cc32e4f7aa 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -243,7 +243,7 @@ mod tests { assert_size!(DiscoInfoQuery, 12); assert_size!(DiscoInfoResult, 48); - assert_size!(Item, 60); + assert_size!(Item, 64); assert_size!(DiscoItemsQuery, 12); assert_size!(DiscoItemsResult, 24); } diff --git a/src/forwarding.rs b/src/forwarding.rs index 906f016e9ea95c3c0ac1e4ba70aadb4de473e866..5286323cfe1bcb21228b886c230acd69f8ca7436 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -33,7 +33,7 @@ mod tests { #[cfg(target_pointer_width = "32")] #[test] fn test_size() { - assert_size!(Forwarded, 204); + assert_size!(Forwarded, 212); } #[cfg(target_pointer_width = "64")] diff --git a/src/iq.rs b/src/iq.rs index 68d585d087a393311608094a4dcb5aadbf742f8f..c8d62257fb70e882b3b39f372b963ddb2025a277 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -225,8 +225,8 @@ mod tests { #[cfg(target_pointer_width = "32")] #[test] fn test_size() { - assert_size!(IqType, 108); - assert_size!(Iq, 192); + assert_size!(IqType, 112); + assert_size!(Iq, 204); } #[cfg(target_pointer_width = "64")] diff --git a/src/jingle.rs b/src/jingle.rs index 14574fdb47d84d7f0bdf0454140f7d8eef247447..98e5fcbb46db4620ed82b2efae8a6a4026ba5892 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -564,7 +564,7 @@ mod tests { assert_size!(Reason, 1); assert_size!(ReasonElement, 16); assert_size!(SessionId, 12); - assert_size!(Jingle, 128); + assert_size!(Jingle, 136); } #[cfg(target_pointer_width = "64")] diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index a34b5e319b43a6553a90f0a4a5ed449ae27a92d5..969b83f30ed562ef354660cecea560ab88786a5b 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -285,7 +285,7 @@ mod tests { assert_size!(Mode, 1); assert_size!(CandidateId, 12); assert_size!(StreamId, 12); - assert_size!(Candidate, 80); + assert_size!(Candidate, 84); assert_size!(TransportPayload, 16); assert_size!(Transport, 44); } diff --git a/src/mam.rs b/src/mam.rs index f0bd0448b30ee1423dba5a240e4149185c75a594..6889f2edf85cd5d1139748b7cf2aa893c15cc570 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -199,7 +199,7 @@ mod tests { fn test_size() { assert_size!(QueryId, 12); assert_size!(Query, 116); - assert_size!(Result_, 228); + assert_size!(Result_, 236); assert_size!(Complete, 1); assert_size!(Fin, 44); assert_size!(DefaultPrefs, 1); diff --git a/src/message.rs b/src/message.rs index fb11e91d98ef614476e2c3ac49c69fd813ef0a2c..f9bae2b16ee3351555b469534481f18f79324a14 100644 --- a/src/message.rs +++ b/src/message.rs @@ -264,7 +264,7 @@ mod tests { assert_size!(Body, 12); assert_size!(Subject, 12); assert_size!(Thread, 12); - assert_size!(Message, 136); + assert_size!(Message, 144); } #[cfg(target_pointer_width = "64")] diff --git a/src/presence.rs b/src/presence.rs index b4e1a1e046b2f36761a560ce175c6edad26a116a..e501f8839a4a5149470132450da4b70ecd554326 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -368,7 +368,7 @@ mod tests { fn test_size() { assert_size!(Show, 1); assert_size!(Type, 1); - assert_size!(Presence, 112); + assert_size!(Presence, 120); } #[cfg(target_pointer_width = "64")] diff --git a/src/roster.rs b/src/roster.rs index 073ecfa7e770462e92ec9f519a306d87c04fa05d..6c871ba651d84cfaf9f22668143c68ed732e3219 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -104,7 +104,7 @@ mod tests { assert_size!(Group, 12); assert_size!(Subscription, 1); assert_size!(Ask, 1); - assert_size!(Item, 64); + assert_size!(Item, 52); assert_size!(Roster, 24); } diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 18087bafa385e5265c0f0cc2647bcc44a392b461..db9230ebbddee84e8035c673291404c3365fa886 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -316,7 +316,7 @@ mod tests { fn test_size() { assert_size!(ErrorType, 1); assert_size!(DefinedCondition, 1); - assert_size!(StanzaError, 104); + assert_size!(StanzaError, 108); } #[cfg(target_pointer_width = "64")] diff --git a/src/stanza_id.rs b/src/stanza_id.rs index eac29085f0a828818ef448f1b0a3f9cef6958111..bbbfb48f2c5bfbfef5f0f4baba3946c3b1d522e0 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -45,7 +45,7 @@ mod tests { #[cfg(target_pointer_width = "32")] #[test] fn test_size() { - assert_size!(StanzaId, 48); + assert_size!(StanzaId, 52); assert_size!(OriginId, 12); } diff --git a/src/stream.rs b/src/stream.rs index 112f1b94ac4572f9176009eb6802c9fd3b445bdf..e6f5be009b0dfce49565f359c4b193f4dd43c589 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -79,7 +79,7 @@ mod tests { #[cfg(target_pointer_width = "32")] #[test] fn test_size() { - assert_size!(Stream, 108); + assert_size!(Stream, 84); } #[cfg(target_pointer_width = "64")] diff --git a/src/websocket.rs b/src/websocket.rs index d94d81fd1f89c4321ca1be9effa36f891dba788d..1092423a10cddb9612823af7865ff81d62774b97 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -78,7 +78,7 @@ mod tests { #[cfg(target_pointer_width = "32")] #[test] fn test_size() { - assert_size!(Open, 108); + assert_size!(Open, 84); } #[cfg(target_pointer_width = "64")] From 868164700aee90d812c38bafebbeb71555153447 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Wed, 26 Jun 2019 02:06:38 +0200 Subject: [PATCH 630/698] Fix pep email address in copyright once and for all MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- src/iq.rs | 2 +- src/muc/mod.rs | 2 +- src/muc/muc.rs | 2 +- src/muc/user.rs | 2 +- src/ns.rs | 2 +- src/ping.rs | 2 +- src/presence.rs | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index c8d62257fb70e882b3b39f372b963ddb2025a277..f3aa7f5d17a8f2f477b4b8ed79bdbdc53d6d876b 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -1,5 +1,5 @@ // Copyright (c) 2017 Emmanuel Gil Peyrot -// Copyright (c) 2017 Maxime “pep” Buquet +// Copyright (c) 2017 Maxime “pep” Buquet // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/src/muc/mod.rs b/src/muc/mod.rs index a12bd565f8ee2fd77201a77325d71a298df84747..5875e3bab750c79f8dc9e48e8e4c6f8de4ef79f0 100644 --- a/src/muc/mod.rs +++ b/src/muc/mod.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Maxime “pep” Buquet +// Copyright (c) 2017 Maxime “pep” Buquet // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 5428a60278e7cd220d10275ba89882f211ac3468..a4d13f8a90a082740069319f6d1e92a041253c52 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Maxime “pep” Buquet +// Copyright (c) 2017 Maxime “pep” Buquet // Copyright (c) 2017 Emmanuel Gil Peyrot // // This Source Code Form is subject to the terms of the Mozilla Public diff --git a/src/muc/user.rs b/src/muc/user.rs index 3621450ab89263a396d5a77a0c417d40a286aa18..44af3a9c734ff689a01f8f63e6846c8bd919b88a 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Maxime “pep” Buquet +// Copyright (c) 2017 Maxime “pep” Buquet // Copyright (c) 2017 Emmanuel Gil Peyrot // // This Source Code Form is subject to the terms of the Mozilla Public diff --git a/src/ns.rs b/src/ns.rs index 9d59435d2b48db9b92965bcf537b097f71e76292..e89ad67a0ad5379bcc2c223f2fb030201c57c3cc 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -1,5 +1,5 @@ // Copyright (c) 2017-2018 Emmanuel Gil Peyrot -// Copyright (c) 2017 Maxime “pep” Buquet +// Copyright (c) 2017 Maxime “pep” Buquet // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/src/ping.rs b/src/ping.rs index f110088b33edf9a717437ba4b80bcdf2c522b226..be8ac48c2be8f2ef4625bc683fd89428135dda7e 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -1,5 +1,5 @@ // Copyright (c) 2017 Emmanuel Gil Peyrot -// Copyright (c) 2017 Maxime “pep” Buquet +// Copyright (c) 2017 Maxime “pep” Buquet // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/src/presence.rs b/src/presence.rs index e501f8839a4a5149470132450da4b70ecd554326..b53b6714811edfd859daf234cf8bd485c8fd53e6 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -1,5 +1,5 @@ // Copyright (c) 2017 Emmanuel Gil Peyrot -// Copyright (c) 2017 Maxime “pep” Buquet +// Copyright (c) 2017 Maxime “pep” Buquet // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this From 03a1d915a4e33d7be62a867f0e0e1a4a5ac0c7c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Wed, 26 Jun 2019 01:50:52 +0200 Subject: [PATCH 631/698] presence: Remove Show::None and make presence.show Option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should make it easier to upgrade to minidom 0.11, to avoid having to implement an Iterator for Show, and just implement Into. It also makes a bit more sense to me semantically. Signed-off-by: Maxime “pep” Buquet --- src/presence.rs | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index b53b6714811edfd859daf234cf8bd485c8fd53e6..ffb734c596be6d0b7575f85e2079b51693108cf2 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -19,9 +19,6 @@ pub trait PresencePayload: TryFrom + Into {} /// Specifies the availability of an entity or resource. #[derive(Debug, Clone, PartialEq)] pub enum Show { - /// Not an actual show value, but an indication there is no show set. - None, - /// The entity or resource is temporarily away. Away, @@ -36,12 +33,6 @@ pub enum Show { Xa, } -impl Default for Show { - fn default() -> Show { - Show::None - } -} - impl FromStr for Show { type Err = Error; @@ -59,13 +50,9 @@ impl FromStr for Show { impl IntoElements for Show { fn into_elements(self, emitter: &mut ElementEmitter) { - if self == Show::None { - return; - } emitter.append_child( Element::builder("show") .append(match self { - Show::None => unreachable!(), Show::Away => Some("away"), Show::Chat => Some("chat"), Show::Dnd => Some("dnd"), @@ -177,7 +164,7 @@ pub struct Presence { pub type_: Type, /// The availability of the sender of this presence. - pub show: Show, + pub show: Option, /// A localised list of statuses defined in this presence. pub statuses: BTreeMap, @@ -198,7 +185,7 @@ impl Presence { to: None, id: None, type_, - show: Show::None, + show: None, statuses: BTreeMap::new(), priority: 0i8, payloads: vec![], @@ -228,7 +215,7 @@ impl Presence { /// Set the availability information of this presence. pub fn with_show(mut self, show: Show) -> Presence { - self.show = show; + self.show = Some(show); self } @@ -270,7 +257,7 @@ impl TryFrom for Presence { to: get_attr!(root, "to", Option), id: get_attr!(root, "id", Option), type_: get_attr!(root, "type", Default), - show: Show::None, + show: None, statuses: BTreeMap::new(), priority: 0i8, payloads: vec![], @@ -307,9 +294,7 @@ impl TryFrom for Presence { presence.payloads.push(elem.clone()); } } - if let Some(show) = show { - presence.show = show; - } + presence.show = show; if let Some(priority) = priority { presence.priority = priority; } @@ -423,7 +408,21 @@ mod tests { .unwrap(); let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); - assert_eq!(presence.show, Show::Chat); + assert_eq!(presence.show, Some(Show::Chat)); + } + + #[test] + fn test_empty_show_value() { + #[cfg(not(feature = "component"))] + let elem: Element = "" + .parse() + .unwrap(); + #[cfg(feature = "component")] + let elem: Element = "" + .parse() + .unwrap(); + let presence = Presence::try_from(elem).unwrap(); + assert_eq!(presence.show, None); } #[test] From fb71acd8d13db734d8a742e0b8b9604b171333c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sat, 13 Jul 2019 17:57:17 +0200 Subject: [PATCH 632/698] Cargo.toml, ChangeLog: Release version 0.14.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- Cargo.toml | 2 +- ChangeLog | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b1e84c36487e3eaff92260380c7c84b53db56a6c..fba43cb47e76b689fe177309fbca26f4e52d0241 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.13.1" +version = "0.14.0" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", diff --git a/ChangeLog b/ChangeLog index 82253779eb3c2f54efcbd543c328a72cbd5a8632..5637b0b24397442d0c79a51ea60a3d9d1a58e439 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,15 @@ -Version TODO: -TODO Emmanuel Gil Peyrot +Version 0.14.0: +2019-07-13 Emmanuel Gil Peyrot , Maxime “pep” Buquet * New parsers/serialisers: - Entity Time (XEP-0202). + * Improvements: + - Microblog NS (XEP-0227). + - Update jid-rs dependency with jid split change (Jid, FullJid, + BareJid) and reexport them. + - Fix rustdoc options in Cargo.toml for docs.rs + * Breaking changes: + - Presence's show attribute is now Option and Show::None is no + more. Version 0.13.1: 2019-04-12 Emmanuel Gil Peyrot From 9d6a43f6209863840c60308e679e742a077f7dd9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 17 Jul 2019 20:27:20 +0200 Subject: [PATCH 633/698] Add a DOAP file. --- doap.xml | 560 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 560 insertions(+) create mode 100644 doap.xml diff --git a/doap.xml b/doap.xml new file mode 100644 index 0000000000000000000000000000000000000000..d4d449fcb218859c5adb093911c03af097caf4da --- /dev/null +++ b/doap.xml @@ -0,0 +1,560 @@ + + + + + xmpp-parsers + + 2017-04-18 + + Collection of parsers and serialisers for XMPP extensions + Collection de parseurs et de sérialiseurs pour extensions XMPP + + TODO + TODO + + + + + + + + + + + + + + + + Rust + + + + + + + + + Link Mauve + + aaa4dac2b31c1be4ee8f8e2ab986d34fb261974f + + + + + pep. + + TODO + + + + + + + + + + + + + + + + + + + + complete + 0.1.0 + + + + + + complete + 0.1.0 + + + + + + complete + 0.10.0 + + + + + + partial + 2.9 + 0.1.0 + + + + + + complete + 2.5rc3 + 0.1.0 + + + + + + complete + 1.32.0 + 0.5.0 + + + + + + complete + 2.0 + 0.1.0 + + + + + + complete + 1.1 + 0.10.0 + + + + + + complete + 1.0 + 0.1.0 + + + + + + partial + 1.15.8 + 0.5.0 + + + + + + complete + 2.4 + 0.6.0 + + + + + + complete + 1.1 + 0.9.0 + + + + + + complete + 1.1.2 + 0.13.0 + + + + + + complete + 2.1 + 0.1.0 + + + + + + complete + 1.1 + 0.8.0 + + + + + + complete + 1.2.1 + 0.9.0 + + + + + + complete + 1.6 + 0.10.0 + + + + + + complete + 1.5.1 + 0.4.0 + + + + + + complete + 1.0.1 + 0.13.0 + + + + + + complete + 1.1.2 + 0.1.0 + + + + + + complete + 1.1.1 + 0.13.0 + + + + + + complete + 1.1 + 0.10.0 + + + + + + complete + 1.0 + 0.13.0 + + + + + + complete + 1.3.0 + 0.1.0 + + + + + + complete + 1.3 + 0.9.0 + + + + + + complete + 1.6 + 0.10.0 + + + + + + complete + 2.0.1 + 0.1.0 + + + + + + complete + 2.0 + 0.14.0 + + + + + + complete + 2.0 + 0.1.0 + + + + + + complete + 1.0 + 0.1.0 + + + + + + complete + 1.0 + 0.1.0 + + + + + + complete + 0.19.1 + 0.1.0 + + + + + + complete + 1.0.3 + 0.2.0 + + + + + + complete + 1.0 + 0.1.0 + + + + + + partial + 0.6.3 + 0.14.0 + only the namespace is included for now + + + + + + complete + 1.0 + 0.1.0 + + + + + + complete + 0.6.0 + 0.1.0 + + + + + + complete + 1.1.0 + 0.1.0 + + + + + + complete + 0.6.3 + 0.1.0 + + + + + + complete + 1.0.2 + 0.3.0 + + + + + + complete + 0.3.1 + 0.13.0 + + + + + + complete + 0.3 + 0.7.0 + + + + + + complete + 0.6.0 + 0.1.0 + + + + + + complete + 0.2.0 + 0.1.0 + + + + + + complete + 0.3.0 + 0.1.0 + + + + + + + + 0.14.0 + 2019-07-13 + + + + + + + 0.13.1 + 2019-04-12 + + + + + + 0.13.0 + 2019-03-20 + + + + + + 0.12.2 + 2019-01-16 + + + + + + 0.12.1 + 2019-01-16 + + + + + + 0.12.0 + 2019-01-16 + + + + + + 0.11.1 + 2018-09-20 + + + + + + 0.11.0 + 2018-08-02 + + + + + + 0.10.0 + 2018-07-31 + + + + + + 0.9.0 + 2017-12-27 + + + + + + 0.8.0 + 2017-11-30 + + + + + + 0.7.1 + 2017-11-30 + + + + + + 0.7.0 + 2017-11-30 + + + + + + 0.6.0 + 2017-11-30 + + + + + + 0.5.0 + 2017-11-30 + + + + + + 0.4.0 + 2017-11-30 + + + + + + 0.3.0 + 2017-11-30 + + + + + + 0.2.0 + 2017-11-30 + + + + + + 0.1.0 + 2017-11-30 + + + + + From 329afabb66cf1913ddc5461877e1ee3b57d47371 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 17 Jul 2019 21:55:16 +0200 Subject: [PATCH 634/698] Implement Message Carbons. --- doap.xml | 8 ++++ src/carbons.rs | 127 +++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 ++ src/ns.rs | 3 ++ 4 files changed, 141 insertions(+) create mode 100644 src/carbons.rs diff --git a/doap.xml b/doap.xml index d4d449fcb218859c5adb093911c03af097caf4da..241965cdf9e007ff897d57c4c5af1ef0676ec88f 100644 --- a/doap.xml +++ b/doap.xml @@ -339,6 +339,14 @@ only the namespace is included for now + + + + complete + 0.13.0 + NEXT + + diff --git a/src/carbons.rs b/src/carbons.rs new file mode 100644 index 0000000000000000000000000000000000000000..b40c1e2e7563c8b1f02e4c3311079c7567cb619e --- /dev/null +++ b/src/carbons.rs @@ -0,0 +1,127 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use crate::forwarding::Forwarded; +use crate::iq::IqSetPayload; +use crate::message::MessagePayload; + +generate_empty_element!( + /// Enable carbons for this session. + Enable, + "enable", + CARBONS +); + +impl IqSetPayload for Enable {} + +generate_empty_element!( + /// Disable a previously-enabled carbons. + Disable, + "disable", + CARBONS +); + +impl IqSetPayload for Disable {} + +generate_empty_element!( + /// Request the enclosing message to not be copied to other carbons-enabled + /// resources of the user. + Private, + "private", + CARBONS +); + +impl MessagePayload for Private {} + +generate_element!( + /// Wrapper for a message received on another resource. + Received, "received", CARBONS, + + children: [ + /// Wrapper for the enclosed message. + forwarded: Required = ("forwarded", FORWARD) => Forwarded + ] +); + +impl MessagePayload for Received {} + +generate_element!( + /// Wrapper for a message sent from another resource. + Sent, "sent", CARBONS, + + children: [ + /// Wrapper for the enclosed message. + forwarded: Required = ("forwarded", FORWARD) => Forwarded + ] +); + +impl MessagePayload for Sent {} + +#[cfg(test)] +mod tests { + use super::*; + use minidom::Element; + use std::convert::TryFrom; + + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Enable, 0); + assert_size!(Disable, 0); + assert_size!(Private, 0); + assert_size!(Received, 212); + assert_size!(Sent, 212); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(Enable, 0); + assert_size!(Disable, 0); + assert_size!(Private, 0); + assert_size!(Received, 408); + assert_size!(Sent, 408); + } + + #[test] + fn empty_elements() { + let elem: Element = "".parse().unwrap(); + Enable::try_from(elem).unwrap(); + + let elem: Element = "".parse().unwrap(); + Disable::try_from(elem).unwrap(); + + let elem: Element = "".parse().unwrap(); + Private::try_from(elem).unwrap(); + } + + #[test] + fn forwarded_elements() { + let elem: Element = " + + + +" + .parse() + .unwrap(); + let received = Received::try_from(elem).unwrap(); + assert!(received.forwarded.stanza.is_some()); + + let elem: Element = " + + + +" + .parse() + .unwrap(); + let sent = Sent::try_from(elem).unwrap(); + assert!(sent.forwarded.stanza.is_some()); + } +} diff --git a/src/lib.rs b/src/lib.rs index 6863afec8de05478a5f207251cf55c3cb75b37ac..607ea1bea6872e03cb2569d33148e2132378e856 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -149,6 +149,9 @@ pub mod jingle_s5b; /// XEP-0261: Jingle In-Band Bytestreams Transport Method pub mod jingle_ibb; +/// XEP-0280: Message Carbons +pub mod carbons; + /// XEP-0297: Stanza Forwarding pub mod forwarding; diff --git a/src/ns.rs b/src/ns.rs index e89ad67a0ad5379bcc2c223f2fb030201c57c3cc..c1a5e96c21d60a999918ff5fa8f15de2c816c64f 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -138,6 +138,9 @@ pub const JINGLE_IBB: &str = "urn:xmpp:jingle:transports:ibb:1"; /// XEP-0277: Microblogging over XMPP pub const MICROBLOG: &str = "urn:xmpp:microblog:0"; +/// XEP-0280: Message Carbons +pub const CARBONS: &str = "urn:xmpp:carbons:2"; + /// XEP-0297: Stanza Forwarding pub const FORWARD: &str = "urn:xmpp:forward:0"; From a7dbaee3094a6e0fef3e256086745db70d4c5015 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 17 Jul 2019 21:58:20 +0200 Subject: [PATCH 635/698] ChangeLog: Start the next entry. --- ChangeLog | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ChangeLog b/ChangeLog index 5637b0b24397442d0c79a51ea60a3d9d1a58e439..7a2b344409bfbefb425d3b49c649de86502d2dcd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +Version NEXT: +DATE Emmanuel Gil Peyrot + * New parsers/serialisers: + - Message Carbons (XEP-0280) + * Improvements: + - New DOAP file for a machine-readable description of the features. + Version 0.14.0: 2019-07-13 Emmanuel Gil Peyrot , Maxime “pep” Buquet * New parsers/serialisers: From 0aa5f5f60fa75fd9f88679a2eddee6b22aed4d00 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 17 Jul 2019 22:26:41 +0200 Subject: [PATCH 636/698] lib: Stop reexporting TryFrom and TryInto. They are available in std::convert nowadays, and should be imported from there. --- ChangeLog | 3 +++ src/lib.rs | 6 ++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7a2b344409bfbefb425d3b49c649de86502d2dcd..a3b472e34d551a71561ff2276df24e44919cb9bb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,9 @@ Version NEXT: DATE Emmanuel Gil Peyrot * New parsers/serialisers: - Message Carbons (XEP-0280) + * Breaking changes: + - Stop reexporting TryFrom and TryInto, they are available in + std::convert nowadays. * Improvements: - New DOAP file for a machine-readable description of the features. diff --git a/src/lib.rs b/src/lib.rs index 607ea1bea6872e03cb2569d33148e2132378e856..634cfd4fe29a1d96578150e7c52c8a4782c0db61 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ //! A crate parsing common XMPP elements into Rust structures. //! -//! Each module implements the [`TryFrom`] trait, which takes a +//! Each module implements the `TryFrom` trait, which takes a //! minidom [`Element`] and returns a `Result` whose value is `Ok` if the //! element parsed correctly, `Err(error::Error)` otherwise. //! @@ -12,7 +12,6 @@ //! [`Element`], using either `From` or `Into`, which give you what //! you want to be sending on the wire. //! -//! [`TryFrom`]: ../try_from/trait.TryFrom.html //! [`Element`]: ../minidom/element/struct.Element.html // Copyright (c) 2017-2019 Emmanuel Gil Peyrot @@ -25,8 +24,7 @@ #![deny(missing_docs)] pub use minidom::Element; -pub use jid::{Jid, BareJid, FullJid, JidParseError}; -pub use std::convert::{TryFrom, TryInto}; +pub use jid::{BareJid, FullJid, Jid, JidParseError}; pub use crate::util::error::Error; /// XML namespace definitions used through XMPP. From 2234bb76f21540062f3cbc4bce24da7f58e91e94 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 17 Jul 2019 22:30:49 +0200 Subject: [PATCH 637/698] macros, pubsub: Fix build failure introduced in the previous commit. --- src/pubsub/mod.rs | 2 +- src/util/macros.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pubsub/mod.rs b/src/pubsub/mod.rs index cde7e77529d65e99dfd0b4f96bc7f198449cd8ee..4b998b9febddc61c341e82d28edf88a1ebde497d 100644 --- a/src/pubsub/mod.rs +++ b/src/pubsub/mod.rs @@ -73,4 +73,4 @@ impl Item { } /// This trait should be implemented on any element which can be included as a PubSub payload. -pub trait PubSubPayload: crate::TryFrom + Into {} +pub trait PubSubPayload: ::std::convert::TryFrom + Into {} diff --git a/src/util/macros.rs b/src/util/macros.rs index 010cde52bb48675b7ba8a43cbb2b45365c734923..5b54cf65d57d97cf38778f18cc70bc616776b410 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -661,7 +661,7 @@ macro_rules! assert_size ( // TODO: move that to src/pubsub/mod.rs, once we figure out how to use macros from there. macro_rules! impl_pubsub_item { ($item:ident, $ns:ident) => { - impl crate::TryFrom for $item { + impl ::std::convert::TryFrom for $item { type Error = Error; fn try_from(elem: crate::Element) -> Result<$item, Error> { From 72ebd217673f6bd31a830c1551aa902ca7b108cb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 25 Jul 2019 17:51:05 +0200 Subject: [PATCH 638/698] hashes: Add base64, hex and colon-separated hex formatters on Hash. --- src/hashes.rs | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/src/hashes.rs b/src/hashes.rs index f4b19faad26f62324286715e220194fcd66af5f5..8e96d517777d28613a70000addc4c1e911ab2af1 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -121,6 +121,29 @@ impl Hash { pub fn from_base64(algo: Algo, hash: &str) -> Result { Ok(Hash::new(algo, base64::decode(hash)?)) } + + /// Formats this hash into base64. + pub fn to_base64(&self) -> String { + base64::encode(&self.hash[..]) + } + + /// Formats this hash into hexadecimal. + pub fn to_hex(&self) -> String { + let mut bytes = vec![]; + for byte in self.hash.iter() { + bytes.push(format!("{:02x}", byte)); + } + bytes.join("") + } + + /// Formats this hash into colon-separated hexadecimal. + pub fn to_colon_hex(&self) -> String { + let mut bytes = vec![]; + for byte in self.hash.iter() { + bytes.push(format!("{:02x}", byte)); + } + bytes.join(":") + } } /// Helper for parsing and serialising a SHA-1 attribute. @@ -142,11 +165,7 @@ impl FromStr for Sha1HexAttribute { impl IntoAttributeValue for Sha1HexAttribute { fn into_attribute_value(self) -> Option { - let mut bytes = vec![]; - for byte in self.0.hash { - bytes.push(format!("{:02x}", byte)); - } - Some(bytes.join("")) + Some(self.to_hex()) } } @@ -195,6 +214,15 @@ mod tests { ); } + #[test] + fn value_serialisation() { + let elem: Element = "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=".parse().unwrap(); + let hash = Hash::try_from(elem).unwrap(); + assert_eq!(hash.to_base64(), "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU="); + assert_eq!(hash.to_hex(), "d976ab9b04e53710c0324bf29a5a17dd2e7e55bca536b26dfe5e50c8f6be6285"); + assert_eq!(hash.to_colon_hex(), "d9:76:ab:9b:04:e5:37:10:c0:32:4b:f2:9a:5a:17:dd:2e:7e:55:bc:a5:36:b2:6d:fe:5e:50:c8:f6:be:62:85"); + } + #[test] fn test_unknown() { let elem: Element = "" From f167e8b591ede98ea334a4f708aa33fb1bd4a9d4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 26 Jul 2019 01:54:26 +0200 Subject: [PATCH 639/698] date: Follow clippy and remove a harmful reference. --- src/date.rs | 4 ++-- src/time.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/date.rs b/src/date.rs index 73dd3bd7e9dbb7777a376afd2ae8d2c4328663f2..e5fd67e89dc1de926760f39aa1a107d2440ca011 100644 --- a/src/date.rs +++ b/src/date.rs @@ -22,8 +22,8 @@ impl DateTime { } /// Returns a new `DateTime` with a different timezone. - pub fn with_timezone(&self, tz: &FixedOffset) -> DateTime { - DateTime(self.0.with_timezone(tz)) + pub fn with_timezone(&self, tz: FixedOffset) -> DateTime { + DateTime(self.0.with_timezone(&tz)) } /// Formats this `DateTime` with the specified format string. diff --git a/src/time.rs b/src/time.rs index 91866660acaf01c034bd3e0e3169d53b6cba77d0..cbedb9883ed1fae397356c9536cbb58c40c7298b 100644 --- a/src/time.rs +++ b/src/time.rs @@ -67,7 +67,7 @@ impl TryFrom for TimeResult { let tzo = tzo.ok_or(Error::ParseError("Missing tzo child in time element."))?; let utc = utc.ok_or(Error::ParseError("Missing utc child in time element."))?; - let date = utc.with_timezone(&tzo); + let date = utc.with_timezone(tzo); Ok(TimeResult(date)) } @@ -81,7 +81,7 @@ impl From for Element { .append(format!("{}", time.0.timezone())) .build()) .append(Element::builder("utc") - .append(time.0.with_timezone(&FixedOffset::east(0)).format("%FT%TZ")) + .append(time.0.with_timezone(FixedOffset::east(0)).format("%FT%TZ")) .build()) .build() } From 1ded40b61476dea8e0580dfc65df9b4de0741b2f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 27 Jul 2019 15:52:41 +0200 Subject: [PATCH 640/698] doap: Update to the latest specification The xmpp-doap extension has be simplified to only expose the SupportedXep class and its children properties, as well as categories, and reuses DOAP to the maximum. --- doap.xml | 719 ++++++++++++++++++++++++++----------------------------- 1 file changed, 345 insertions(+), 374 deletions(-) diff --git a/doap.xml b/doap.xml index 241965cdf9e007ff897d57c4c5af1ef0676ec88f..c28d0f932eabe7e4dc43569db1d103ac387b1576 100644 --- a/doap.xml +++ b/doap.xml @@ -28,9 +28,6 @@ Rust - - - @@ -58,377 +55,351 @@ - - - - - - - complete - 0.1.0 - - - - - - complete - 0.1.0 - - - - - - complete - 0.10.0 - - - - - - partial - 2.9 - 0.1.0 - - - - - - complete - 2.5rc3 - 0.1.0 - - - - - - complete - 1.32.0 - 0.5.0 - - - - - - complete - 2.0 - 0.1.0 - - - - - - complete - 1.1 - 0.10.0 - - - - - - complete - 1.0 - 0.1.0 - - - - - - partial - 1.15.8 - 0.5.0 - - - - - - complete - 2.4 - 0.6.0 - - - - - - complete - 1.1 - 0.9.0 - - - - - - complete - 1.1.2 - 0.13.0 - - - - - - complete - 2.1 - 0.1.0 - - - - - - complete - 1.1 - 0.8.0 - - - - - - complete - 1.2.1 - 0.9.0 - - - - - - complete - 1.6 - 0.10.0 - - - - - - complete - 1.5.1 - 0.4.0 - - - - - - complete - 1.0.1 - 0.13.0 - - - - - - complete - 1.1.2 - 0.1.0 - - - - - - complete - 1.1.1 - 0.13.0 - - - - - - complete - 1.1 - 0.10.0 - - - - - - complete - 1.0 - 0.13.0 - - - - - - complete - 1.3.0 - 0.1.0 - - - - - - complete - 1.3 - 0.9.0 - - - - - - complete - 1.6 - 0.10.0 - - - - - - complete - 2.0.1 - 0.1.0 - - - - - - complete - 2.0 - 0.14.0 - - - - - - complete - 2.0 - 0.1.0 - - - - - - complete - 1.0 - 0.1.0 - - - - - - complete - 1.0 - 0.1.0 - - - - - - complete - 0.19.1 - 0.1.0 - - - - - - complete - 1.0.3 - 0.2.0 - - - - - - complete - 1.0 - 0.1.0 - - - - - - partial - 0.6.3 - 0.14.0 - only the namespace is included for now - - - - - - complete - 0.13.0 - NEXT - - - - - - complete - 1.0 - 0.1.0 - - - - - - complete - 0.6.0 - 0.1.0 - - - - - - complete - 1.1.0 - 0.1.0 - - - - - - complete - 0.6.3 - 0.1.0 - - - - - - complete - 1.0.2 - 0.3.0 - - - - - - complete - 0.3.1 - 0.13.0 - - - - - - complete - 0.3 - 0.7.0 - - - - - - complete - 0.6.0 - 0.1.0 - - - - - - complete - 0.2.0 - 0.1.0 - - - - - - complete - 0.3.0 - 0.1.0 - - - - + + + + partial + 2.9 + 0.1.0 + + + + + + complete + 2.5rc3 + 0.1.0 + + + + + + complete + 1.32.0 + 0.5.0 + + + + + + complete + 2.0 + 0.1.0 + + + + + + complete + 1.1 + 0.10.0 + + + + + + complete + 1.0 + 0.1.0 + + + + + + partial + 1.15.8 + 0.5.0 + + + + + + complete + 2.4 + 0.6.0 + + + + + + complete + 1.1 + 0.9.0 + + + + + + complete + 1.1.2 + 0.13.0 + + + + + + complete + 2.1 + 0.1.0 + + + + + + complete + 1.1 + 0.8.0 + + + + + + complete + 1.2.1 + 0.9.0 + + + + + + complete + 1.6 + 0.10.0 + + + + + + complete + 1.5.1 + 0.4.0 + + + + + + complete + 1.0.1 + 0.13.0 + + + + + + complete + 1.1.2 + 0.1.0 + + + + + + complete + 1.1.1 + 0.13.0 + + + + + + complete + 1.1 + 0.10.0 + + + + + + complete + 1.0 + 0.13.0 + + + + + + complete + 1.3.0 + 0.1.0 + + + + + + complete + 1.3 + 0.9.0 + + + + + + complete + 1.6 + 0.10.0 + + + + + + complete + 2.0.1 + 0.1.0 + + + + + + complete + 2.0 + 0.14.0 + + + + + + complete + 2.0 + 0.1.0 + + + + + + complete + 1.0 + 0.1.0 + + + + + + complete + 1.0 + 0.1.0 + + + + + + complete + 0.19.1 + 0.1.0 + + + + + + complete + 1.0.3 + 0.2.0 + + + + + + complete + 1.0 + 0.1.0 + + + + + + partial + 0.6.3 + 0.14.0 + only the namespace is included for now + + + + + + complete + 0.13.0 + NEXT + + + + + + complete + 1.0 + 0.1.0 + + + + + + complete + 0.6.0 + 0.1.0 + + + + + + complete + 1.1.0 + 0.1.0 + + + + + + complete + 0.6.3 + 0.1.0 + + + + + + complete + 1.0.2 + 0.3.0 + + + + + + complete + 0.3.1 + 0.13.0 + + + + + + complete + 0.3 + 0.7.0 + + + + + + complete + 0.6.0 + 0.1.0 + + + + + + complete + 0.2.0 + 0.1.0 + + + + + + complete + 0.3.0 + 0.1.0 + + From ecee3e9ee8a55179a8895eab3778a7926977e37e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 30 Jul 2019 21:25:27 +0200 Subject: [PATCH 641/698] bind: Split Bind into request/response. --- src/bind.rs | 131 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 91 insertions(+), 40 deletions(-) diff --git a/src/bind.rs b/src/bind.rs index 408c08bb56ce4e2b3612d07b394561646d4e7b38..35d2204533134bbf31ce9343f25d3883eaa10fe5 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -17,76 +17,111 @@ use std::convert::TryFrom; /// /// See https://xmpp.org/rfcs/rfc6120.html#bind #[derive(Debug, Clone, PartialEq)] -pub enum Bind { - /// Requests no particular resource, a random one will be affected by the - /// server. - None, - +pub struct BindRequest { /// Requests this resource, the server may associate another one though. - Resource(String), - - /// The full JID returned by the server for this client. - Jid(FullJid), + /// + /// If this is None, we request no particular resource, and a random one + /// will be affected by the server. + resource: Option, } -impl Bind { +impl BindRequest { /// Creates a resource binding request. - pub fn new(resource: Option) -> Bind { - match resource { - None => Bind::None, - Some(resource) => Bind::Resource(resource), - } + pub fn new(resource: Option) -> BindRequest { + BindRequest { resource } } } -impl IqSetPayload for Bind {} -impl IqResultPayload for Bind {} +impl IqSetPayload for BindRequest {} -impl TryFrom for Bind { +impl TryFrom for BindRequest { type Error = Error; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { check_self!(elem, "bind", BIND); check_no_attributes!(elem, "bind"); - let mut bind = Bind::None; + let mut resource = None; for child in elem.children() { - if bind != Bind::None { + if resource.is_some() { return Err(Error::ParseError("Bind can only have one child.")); } if child.is("resource", ns::BIND) { check_no_attributes!(child, "resource"); check_no_children!(child, "resource"); - bind = Bind::Resource(child.text()); - } else if child.is("jid", ns::BIND) { - check_no_attributes!(child, "jid"); - check_no_children!(child, "jid"); - bind = Bind::Jid(FullJid::from_str(&child.text())?); + resource = Some(child.text()); } else { - return Err(Error::ParseError("Unknown element in bind.")); + return Err(Error::ParseError("Unknown element in bind request.")); } } - Ok(bind) + Ok(BindRequest { resource }) } } -impl From for Element { - fn from(bind: Bind) -> Element { +impl From for Element { + fn from(bind: BindRequest) -> Element { Element::builder("bind") .ns(ns::BIND) - .append(match bind { - Bind::None => vec![], - Bind::Resource(resource) => vec![Element::builder("resource") + .append(match bind.resource { + None => vec![], + Some(resource) => vec![Element::builder("resource") .ns(ns::BIND) .append(resource) .build()], - Bind::Jid(jid) => vec![Element::builder("jid").ns(ns::BIND).append(jid).build()], }) .build() } } +/// The response for resource binding, containing the client’s full JID. +/// +/// See https://xmpp.org/rfcs/rfc6120.html#bind +#[derive(Debug, Clone, PartialEq)] +pub struct BindResponse { + /// The full JID returned by the server for this client. + jid: FullJid, +} + +impl IqResultPayload for BindResponse {} + +impl TryFrom for BindResponse { + type Error = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "bind", BIND); + check_no_attributes!(elem, "bind"); + + let mut jid = None; + for child in elem.children() { + if jid.is_some() { + return Err(Error::ParseError("Bind can only have one child.")); + } + if child.is("jid", ns::BIND) { + check_no_attributes!(child, "jid"); + check_no_children!(child, "jid"); + jid = Some(FullJid::from_str(&child.text())?); + } else { + return Err(Error::ParseError("Unknown element in bind response.")); + } + } + + Ok(BindResponse { jid: match jid { + None => return Err(Error::ParseError("Bind response must contain a jid element.")), + Some(jid) => jid, + } }) + } +} + +impl From for Element { + fn from(bind: BindResponse) -> Element { + Element::builder("bind") + .ns(ns::BIND) + .append(Element::builder("jid").ns(ns::BIND).append(bind.jid).build()) + .build() + } +} + #[cfg(test)] mod tests { use super::*; @@ -94,13 +129,15 @@ mod tests { #[cfg(target_pointer_width = "32")] #[test] fn test_size() { - assert_size!(Bind, 40); + assert_size!(BindRequest, 12); + assert_size!(BindResponse, 36); } #[cfg(target_pointer_width = "64")] #[test] fn test_size() { - assert_size!(Bind, 80); + assert_size!(BindRequest, 24); + assert_size!(BindResponse, 72); } #[test] @@ -108,8 +145,22 @@ mod tests { let elem: Element = "" .parse() .unwrap(); - let bind = Bind::try_from(elem).unwrap(); - assert_eq!(bind, Bind::None); + let bind = BindRequest::try_from(elem).unwrap(); + assert_eq!(bind.resource, None); + + let elem: Element = "Hello™" + .parse() + .unwrap(); + let bind = BindRequest::try_from(elem).unwrap(); + // FIXME: “™” should be resourceprep’d into “TM” here… + //assert_eq!(bind.resource.unwrap(), "HelloTM"); + assert_eq!(bind.resource.unwrap(), "Hello™"); + + let elem: Element = "coucou@linkmauve.fr/HelloTM" + .parse() + .unwrap(); + let bind = BindResponse::try_from(elem).unwrap(); + assert_eq!(bind.jid, FullJid::new("coucou", "linkmauve.fr", "HelloTM")); } #[cfg(not(feature = "disable-validation"))] @@ -118,7 +169,7 @@ mod tests { let elem: Element = "resource" .parse() .unwrap(); - let error = Bind::try_from(elem).unwrap_err(); + let error = BindRequest::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -128,7 +179,7 @@ mod tests { let elem: Element = "resource" .parse() .unwrap(); - let error = Bind::try_from(elem).unwrap_err(); + let error = BindRequest::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), From a8628a7870f072428e6bc880886c111e90d83749 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 30 Jul 2019 21:30:34 +0200 Subject: [PATCH 642/698] ibr: Update the size tests. Hashbrown has been stabilised, so HashMap is now two pointers bigger, this reflects in this test. --- src/ibr.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/ibr.rs b/src/ibr.rs index 2d41a6fa2eb1deb86b70c50a778f460155d4d621..8ddca59dc4ebe34b3c1868f2d4d60f1dfbca9ce5 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -120,23 +120,16 @@ mod tests { use super::*; use crate::util::compare_elements::NamespaceAwareCompare; - // TODO: These size tests are sensible to the size of HashMap, which recently grew of two - // pointers and is thus different on stable and nightly. Let’s wait for this issue before - // attempting a fix: - // https://github.com/rust-lang/hashbrown/issues/69 - #[cfg(target_pointer_width = "32")] #[test] - #[ignore] fn test_size() { - assert_size!(Query, 88); + assert_size!(Query, 96); } #[cfg(target_pointer_width = "64")] #[test] - #[ignore] fn test_size() { - assert_size!(Query, 152); + assert_size!(Query, 168); } #[test] From 1eb8c781ab2c2076660f7a22b19da480179b3ac9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 30 Jul 2019 22:14:06 +0200 Subject: [PATCH 643/698] hashes: Add a from_hex constructor, and use it in Sha1HexAttribute. --- src/hashes.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/hashes.rs b/src/hashes.rs index 8e96d517777d28613a70000addc4c1e911ab2af1..e3f73ca83c51dfbf6187b90d78b4911915826e30 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -122,6 +122,16 @@ impl Hash { Ok(Hash::new(algo, base64::decode(hash)?)) } + /// Like [new](#method.new) but takes hex-encoded data before decoding it. + pub fn from_hex(algo: Algo, hex: &str) -> Result { + let mut bytes = vec![]; + for i in 0..hex.len() / 2 { + let byte = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16)?; + bytes.push(byte); + } + Ok(Hash::new(algo, bytes)) + } + /// Formats this hash into base64. pub fn to_base64(&self) -> String { base64::encode(&self.hash[..]) @@ -154,12 +164,8 @@ impl FromStr for Sha1HexAttribute { type Err = ParseIntError; fn from_str(hex: &str) -> Result { - let mut bytes = vec![]; - for i in 0..hex.len() / 2 { - let byte = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16)?; - bytes.push(byte); - } - Ok(Sha1HexAttribute(Hash::new(Algo::Sha_1, bytes))) + let hash = Hash::from_hex(Algo::Sha_1, hex)?; + Ok(Sha1HexAttribute(hash)) } } From 08fa36d1860ecfe5b725b00a6596c5a0db475c16 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 31 Jul 2019 13:51:18 +0200 Subject: [PATCH 644/698] Implement Bits of Binary. --- ChangeLog | 1 + doap.xml | 8 +++ src/bob.rs | 168 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 + src/ns.rs | 3 + 5 files changed, 183 insertions(+) create mode 100644 src/bob.rs diff --git a/ChangeLog b/ChangeLog index a3b472e34d551a71561ff2276df24e44919cb9bb..a855cb7495582789de67dba2342836834c072740 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,7 @@ Version NEXT: DATE Emmanuel Gil Peyrot * New parsers/serialisers: - Message Carbons (XEP-0280) + - Bits of Binary (XEP-0231) * Breaking changes: - Stop reexporting TryFrom and TryInto, they are available in std::convert nowadays. diff --git a/doap.xml b/doap.xml index c28d0f932eabe7e4dc43569db1d103ac387b1576..b68a71673bd5df4f062dea4ec6eff41d32839712 100644 --- a/doap.xml +++ b/doap.xml @@ -279,6 +279,14 @@ 0.1.0 + + + + complete + 1.0 + NEXT + + diff --git a/src/bob.rs b/src/bob.rs new file mode 100644 index 0000000000000000000000000000000000000000..704ed0c94ef7c692f1cea8478f353306c9affdd8 --- /dev/null +++ b/src/bob.rs @@ -0,0 +1,168 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use crate::hashes::{Hash, Algo}; +use crate::util::helpers::Base64; +use crate::util::error::Error; +use minidom::IntoAttributeValue; +use std::str::FromStr; + +/// A Content-ID, as defined in RFC2111. +/// +/// The text value SHOULD be of the form algo+hash@bob.xmpp.org, this struct +/// enforces that format. +#[derive(Clone, Debug)] +pub struct ContentId { + hash: Hash, +} + +impl FromStr for ContentId { + type Err = Error; + + fn from_str(s: &str) -> Result { + let temp: Vec<_> = s.splitn(2, '@').collect(); + let temp: Vec<_> = match temp[..] { + [lhs, rhs] => { + if rhs != "bob.xmpp.org" { + return Err(Error::ParseError("Wrong domain for cid URI.")) + } + lhs.splitn(2, '+').collect() + }, + _ => return Err(Error::ParseError("Missing @ in cid URI.")) + }; + let (algo, hex) = match temp[..] { + [lhs, rhs] => { + let algo = match lhs { + "sha1" => Algo::Sha_1, + "sha256" => Algo::Sha_256, + _ => unimplemented!(), + }; + (algo, rhs) + }, + _ => return Err(Error::ParseError("Missing + in cid URI.")) + }; + let hash = Hash::from_hex(algo, hex)?; + Ok(ContentId { hash }) + } +} + +impl IntoAttributeValue for ContentId { + fn into_attribute_value(self) -> Option { + let algo = match self.hash.algo { + Algo::Sha_1 => "sha1", + Algo::Sha_256 => "sha256", + _ => unimplemented!(), + }; + Some(format!("{}+{}@bob.xmpp.org", algo, self.hash.to_hex())) + } +} + +generate_element!( + /// Request for an uncached cid file. + Data, "data", BOB, + attributes: [ + /// The cid in question. + cid: Required = "cid", + + /// How long to cache it (in seconds). + max_age: Option = "max-age", + + /// The MIME type of the data being transmitted. + /// + /// See the [IANA MIME Media Types Registry][1] for a list of + /// registered types, but unregistered or yet-to-be-registered are + /// accepted too. + /// + /// [1]: https://www.iana.org/assignments/media-types/media-types.xhtml + type_: Option = "type" + ], + text: ( + /// The actual data. + data: Base64> + ) +); + +#[cfg(test)] +mod tests { + use super::*; + use minidom::Element; + use std::convert::TryFrom; + use std::error::Error as StdError; + + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(ContentId, 24); + assert_size!(Data, 24); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(ContentId, 56); + assert_size!(Data, 120); + } + + #[test] + fn test_simple() { + let cid: ContentId = "sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org".parse().unwrap(); + assert_eq!(cid.hash.algo, Algo::Sha_1); + assert_eq!(cid.hash.hash, b"\x8f\x35\xfe\xf1\x10\xff\xc5\xdf\x08\xd5\x79\xa5\x00\x83\xff\x93\x08\xfb\x62\x42"); + assert_eq!(cid.into_attribute_value().unwrap(), "sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org"); + + let elem: Element = "".parse().unwrap(); + let data = Data::try_from(elem).unwrap(); + assert_eq!(data.cid.hash.algo, Algo::Sha_1); + assert_eq!(data.cid.hash.hash, b"\x8f\x35\xfe\xf1\x10\xff\xc5\xdf\x08\xd5\x79\xa5\x00\x83\xff\x93\x08\xfb\x62\x42"); + assert!(data.max_age.is_none()); + assert!(data.type_.is_none()); + assert!(data.data.is_empty()); + } + + #[test] + fn invalid_cid() { + let error = "Hello world!".parse::().unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Missing @ in cid URI."); + + let error = "Hello world@bob.xmpp.org".parse::().unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Missing + in cid URI."); + + let error = "sha1+1234@coucou.linkmauve.fr".parse::().unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Wrong domain for cid URI."); + + let error = "sha1+invalid@bob.xmpp.org".parse::().unwrap_err(); + let message = match error { + Error::ParseIntError(error) => error, + _ => panic!(), + }; + assert_eq!(message.description(), "invalid digit found in string"); + } + + #[test] + fn unknown_child() { + let elem: Element = "" + .parse() + .unwrap(); + let error = Data::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in data element."); + } +} diff --git a/src/lib.rs b/src/lib.rs index 634cfd4fe29a1d96578150e7c52c8a4782c0db61..3a4a5dc3366d9b35b3ba39a25e1c2febd3658e89 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -138,6 +138,9 @@ pub mod media_element; /// XEP-0224: Attention pub mod attention; +/// XEP-0231: Bits of Binary +pub mod bob; + /// XEP-0234: Jingle File Transfer pub mod jingle_ft; diff --git a/src/ns.rs b/src/ns.rs index c1a5e96c21d60a999918ff5fa8f15de2c816c64f..f5775d53843572200aacbb73f7d95789729ece7b 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -124,6 +124,9 @@ pub const MEDIA_ELEMENT: &str = "urn:xmpp:media-element"; /// XEP-0224: Attention pub const ATTENTION: &str = "urn:xmpp:attention:0"; +/// XEP-0231: Bits of Binary +pub const BOB: &str = "urn:xmpp:bob"; + /// XEP-0234: Jingle File Transfer pub const JINGLE_FT: &str = "urn:xmpp:jingle:apps:file-transfer:5"; /// XEP-0234: Jingle File Transfer From 08c3cb8c6fc9e79d4b22540ba18586e88d311775 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 31 Jul 2019 13:52:08 +0200 Subject: [PATCH 645/698] bind: Document the split in ChangeLog. --- ChangeLog | 1 + src/bind.rs | 32 ++++++++++++++++---------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/ChangeLog b/ChangeLog index a855cb7495582789de67dba2342836834c072740..ea8f73c7f8cf0dd65f93ef06f3dc89ea2b4f6fb0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,6 +6,7 @@ DATE Emmanuel Gil Peyrot * Breaking changes: - Stop reexporting TryFrom and TryInto, they are available in std::convert nowadays. + - Bind has been split into BindQuery and BindResponse. * Improvements: - New DOAP file for a machine-readable description of the features. diff --git a/src/bind.rs b/src/bind.rs index 35d2204533134bbf31ce9343f25d3883eaa10fe5..357a7326b4448352494b2923d7456294ce1d8395 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -17,7 +17,7 @@ use std::convert::TryFrom; /// /// See https://xmpp.org/rfcs/rfc6120.html#bind #[derive(Debug, Clone, PartialEq)] -pub struct BindRequest { +pub struct BindQuery { /// Requests this resource, the server may associate another one though. /// /// If this is None, we request no particular resource, and a random one @@ -25,19 +25,19 @@ pub struct BindRequest { resource: Option, } -impl BindRequest { +impl BindQuery { /// Creates a resource binding request. - pub fn new(resource: Option) -> BindRequest { - BindRequest { resource } + pub fn new(resource: Option) -> BindQuery { + BindQuery { resource } } } -impl IqSetPayload for BindRequest {} +impl IqSetPayload for BindQuery {} -impl TryFrom for BindRequest { +impl TryFrom for BindQuery { type Error = Error; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { check_self!(elem, "bind", BIND); check_no_attributes!(elem, "bind"); @@ -55,12 +55,12 @@ impl TryFrom for BindRequest { } } - Ok(BindRequest { resource }) + Ok(BindQuery { resource }) } } -impl From for Element { - fn from(bind: BindRequest) -> Element { +impl From for Element { + fn from(bind: BindQuery) -> Element { Element::builder("bind") .ns(ns::BIND) .append(match bind.resource { @@ -129,14 +129,14 @@ mod tests { #[cfg(target_pointer_width = "32")] #[test] fn test_size() { - assert_size!(BindRequest, 12); + assert_size!(BindQuery, 12); assert_size!(BindResponse, 36); } #[cfg(target_pointer_width = "64")] #[test] fn test_size() { - assert_size!(BindRequest, 24); + assert_size!(BindQuery, 24); assert_size!(BindResponse, 72); } @@ -145,13 +145,13 @@ mod tests { let elem: Element = "" .parse() .unwrap(); - let bind = BindRequest::try_from(elem).unwrap(); + let bind = BindQuery::try_from(elem).unwrap(); assert_eq!(bind.resource, None); let elem: Element = "Hello™" .parse() .unwrap(); - let bind = BindRequest::try_from(elem).unwrap(); + let bind = BindQuery::try_from(elem).unwrap(); // FIXME: “™” should be resourceprep’d into “TM” here… //assert_eq!(bind.resource.unwrap(), "HelloTM"); assert_eq!(bind.resource.unwrap(), "Hello™"); @@ -169,7 +169,7 @@ mod tests { let elem: Element = "resource" .parse() .unwrap(); - let error = BindRequest::try_from(elem).unwrap_err(); + let error = BindQuery::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -179,7 +179,7 @@ mod tests { let elem: Element = "resource" .parse() .unwrap(); - let error = BindRequest::try_from(elem).unwrap_err(); + let error = BindQuery::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), From 7ebfe3e88100fd91df72d6d25003f0a290a63b65 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 25 Aug 2019 19:01:51 +0200 Subject: [PATCH 646/698] New XHTML-IM parser (XEP-0071). --- ChangeLog | 3 +- doap.xml | 8 + src/lib.rs | 3 + src/ns.rs | 5 + src/xhtml.rs | 472 +++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 490 insertions(+), 1 deletion(-) create mode 100644 src/xhtml.rs diff --git a/ChangeLog b/ChangeLog index ea8f73c7f8cf0dd65f93ef06f3dc89ea2b4f6fb0..eec3bbb8cb21ca2b08b42a528f354d846e8f2aa6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,8 +1,9 @@ Version NEXT: DATE Emmanuel Gil Peyrot * New parsers/serialisers: - - Message Carbons (XEP-0280) + - XHTML-IM (XEP-0071) - Bits of Binary (XEP-0231) + - Message Carbons (XEP-0280) * Breaking changes: - Stop reexporting TryFrom and TryInto, they are available in std::convert nowadays. diff --git a/doap.xml b/doap.xml index b68a71673bd5df4f062dea4ec6eff41d32839712..a3165dc0b94df64facbfe2696f0579e0efc62bab 100644 --- a/doap.xml +++ b/doap.xml @@ -111,6 +111,14 @@ 0.5.0 + + + + complete + 1.5.4 + NEXT + + diff --git a/src/lib.rs b/src/lib.rs index 3a4a5dc3366d9b35b3ba39a25e1c2febd3658e89..af2fc129566d64a4dc6ca7121fd95c0e56bbc6da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,6 +75,9 @@ pub mod rsm; /// XEP-0060: Publish-Subscribe pub mod pubsub; +/// XEP-0071: XHTML-IM +pub mod xhtml; + /// XEP-0077: In-Band Registration pub mod ibr; diff --git a/src/ns.rs b/src/ns.rs index f5775d53843572200aacbb73f7d95789729ece7b..5c59ec03cccbf36169f5bf355ca27e10ea377528 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -53,6 +53,11 @@ pub const PUBSUB_EVENT: &str = "http://jabber.org/protocol/pubsub#event"; /// XEP-0060: Publish-Subscribe pub const PUBSUB_OWNER: &str = "http://jabber.org/protocol/pubsub#owner"; +/// XEP-0071: XHTML-IM +pub const XHTML_IM: &str = "http://jabber.org/protocol/xhtml-im"; +/// XEP-0071: XHTML-IM +pub const XHTML: &str = "http://www.w3.org/1999/xhtml"; + /// XEP-0077: In-Band Registration pub const REGISTER: &str = "jabber:iq:register"; diff --git a/src/xhtml.rs b/src/xhtml.rs new file mode 100644 index 0000000000000000000000000000000000000000..c4fc0c05685448160a05fe6bc98f882676099492 --- /dev/null +++ b/src/xhtml.rs @@ -0,0 +1,472 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use crate::util::error::Error; +use crate::message::MessagePayload; +use crate::ns; +use minidom::{Element, Node}; +use std::convert::TryFrom; +use std::collections::HashMap; + +// TODO: Use a proper lang type. +type Lang = String; + +/// Container for formatted text. +#[derive(Debug, Clone)] +pub struct XhtmlIm { + /// Map of language to body element. + bodies: HashMap, +} + +impl XhtmlIm { + /// Serialise formatted text to HTML. + pub fn to_html(self) -> String { + let mut html = Vec::new(); + // TODO: use the best language instead. + for (lang, body) in self.bodies { + if let Tag::Body { style: _, xml_lang, children } = body { + if lang.is_empty() { + assert!(xml_lang.is_none()); + } else { + assert_eq!(Some(lang), xml_lang); + } + for tag in children { + html.push(tag.to_html()); + } + break; + } else { + unreachable!(); + } + } + html.concat() + } +} + +impl MessagePayload for XhtmlIm {} + +impl TryFrom for XhtmlIm { + type Error = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "html", XHTML_IM); + check_no_attributes!(elem, "html"); + + let mut bodies = HashMap::new(); + for child in elem.children() { + if child.is("body", ns::XHTML) { + let child = child.clone(); + let lang = match child.attr("xml:lang") { + Some(lang) => lang, + None => "", + }.to_string(); + let body = Tag::try_from(child)?; + match bodies.insert(lang, body) { + None => (), + Some(_) => return Err(Error::ParseError("Two identical language bodies found in XHTML-IM.")) + } + } else { + return Err(Error::ParseError("Unknown element in XHTML-IM.")); + } + } + + Ok(XhtmlIm { bodies }) + } +} + +impl From for Element { + fn from(wrapper: XhtmlIm) -> Element { + Element::builder("html") + .ns(ns::XHTML_IM) + .append(wrapper.bodies.into_iter().map(|(ref lang, ref body)| { + if let Tag::Body { style, xml_lang, children } = body { + assert_eq!(Some(lang), xml_lang.as_ref()); + Element::builder("body") + .ns(ns::XHTML_IM) + .attr("style", get_style_string(style.clone())) + .attr("xml:lang", xml_lang.clone()) + .append(children_to_nodes(children.clone())) + } else { + unreachable!(); + } + }).collect::>()) + .build() + } +} + +#[derive(Debug, Clone)] +enum Child { + Tag(Tag), + Text(String), +} + +impl Child { + fn to_html(self) -> String { + match self { + Child::Tag(tag) => tag.to_html(), + Child::Text(text) => text, + } + } +} + +#[derive(Debug, Clone)] +struct Property { + key: String, + value: String, +} + +type Css = Vec; + +fn get_style_string(style: Css) -> Option { + let mut result = vec![]; + for Property { key, value } in style { + result.push(format!("{}: {}", key, value)); + } + if result.is_empty() { + return None; + } + Some(result.join("; ")) +} + +#[derive(Debug, Clone)] +enum Tag { + A { href: Option, style: Css, type_: Option, children: Vec }, + Blockquote { style: Css, children: Vec }, + Body { style: Css, xml_lang: Option, children: Vec }, + Br, + Cite { style: Css, children: Vec }, + Em { children: Vec }, + Img { src: Option, alt: Option }, // TODO: height, width, style + Li { style: Css, children: Vec }, + Ol { style: Css, children: Vec }, + P { style: Css, children: Vec }, + Span { style: Css, children: Vec }, + Strong { children: Vec }, + Ul { style: Css, children: Vec }, + Unknown(Vec), +} + +impl Tag { + fn to_html(self) -> String { + match self { + Tag::A { href, style, type_, children } => { + let href = write_attr(href, "href"); + let style = write_attr(get_style_string(style), "style"); + let type_ = write_attr(type_, "type"); + format!("{}", href, style, type_, children_to_html(children)) + }, + Tag::Blockquote { style, children } => { + let style = write_attr(get_style_string(style), "style"); + format!("{}", style, children_to_html(children)) + }, + Tag::Body { style, xml_lang: _, children } => { + let style = write_attr(get_style_string(style), "style"); + format!("{}", style, children_to_html(children)) + }, + Tag::Br => String::from("
"), + Tag::Cite { style, children } => { + let style = write_attr(get_style_string(style), "style"); + format!("{}", style, children_to_html(children)) + }, + Tag::Em { children } => format!("{}", children_to_html(children)), + Tag::Img { src, alt } => { + let src = write_attr(src, "src"); + let alt = write_attr(alt, "alt"); + format!("", src, alt) + } + Tag::Li { style, children } => { + let style = write_attr(get_style_string(style), "style"); + format!("{}", style, children_to_html(children)) + } + Tag::Ol { style, children } => { + let style = write_attr(get_style_string(style), "style"); + format!("{}", style, children_to_html(children)) + } + Tag::P { style, children } => { + let style = write_attr(get_style_string(style), "style"); + format!("{}

", style, children_to_html(children)) + } + Tag::Span { style, children } => { + let style = write_attr(get_style_string(style), "style"); + format!("{}", style, children_to_html(children)) + } + Tag::Strong { children } => format!("{}", children.into_iter().map(|child| child.to_html()).collect::>().join("")), + Tag::Ul { style, children } => { + let style = write_attr(get_style_string(style), "style"); + format!("{}", style, children_to_html(children)) + } + Tag::Unknown(children) => children_to_html(children), + } + } +} + +impl TryFrom for Tag { + type Error = Error; + + fn try_from(elem: Element) -> Result { + let mut children = vec![]; + for child in elem.nodes() { + match child { + Node::Element(child) => children.push(Child::Tag(Tag::try_from(child.clone())?)), + Node::Text(text) => children.push(Child::Text(text.clone())), + Node::Comment(_) => unimplemented!() // XXX: remove! + } + } + + Ok(match elem.name() { + "a" => Tag::A { href: elem.attr("href").map(|href| href.to_string()), style: parse_css(elem.attr("style")), type_: elem.attr("type").map(|type_| type_.to_string()), children }, + "blockquote" => Tag::Blockquote { style: parse_css(elem.attr("style")), children }, + "body" => Tag::Body { style: parse_css(elem.attr("style")), xml_lang: elem.attr("xml:lang").map(|xml_lang| xml_lang.to_string()), children }, + "br" => Tag::Br, + "cite" => Tag::Cite { style: parse_css(elem.attr("style")), children }, + "em" => Tag::Em { children }, + "img" => Tag::Img { src: elem.attr("src").map(|src| src.to_string()), alt: elem.attr("alt").map(|alt| alt.to_string()) }, + "li" => Tag::Li { style: parse_css(elem.attr("style")), children }, + "ol" => Tag::Ol { style: parse_css(elem.attr("style")), children }, + "p" => Tag::P { style: parse_css(elem.attr("style")), children }, + "span" => Tag::Span { style: parse_css(elem.attr("style")), children }, + "strong" => Tag::Strong { children }, + "ul" => Tag::Ul { style: parse_css(elem.attr("style")), children }, + _ => Tag::Unknown(children), + }) + } +} + +impl From for Element { + fn from(tag: Tag) -> Element { + let (name, attrs, children) = match tag { + Tag::A { href, style, type_, children } => ("a", { + let mut attrs = vec![]; + if let Some(href) = href { + attrs.push(("href", href)); + } + if let Some(style) = get_style_string(style) { + attrs.push(("style", style)); + } + if let Some(type_) = type_ { + attrs.push(("type", type_)); + } + attrs + }, children), + Tag::Blockquote { style, children } => ("blockquote", match get_style_string(style) { + Some(style) => vec![("style", style)], + None => vec![], + }, children), + Tag::Body { style, xml_lang, children } => ("body", { + let mut attrs = vec![]; + if let Some(style) = get_style_string(style) { + attrs.push(("style", style)); + } + if let Some(xml_lang) = xml_lang { + attrs.push(("xml:lang", xml_lang)); + } + attrs + }, children), + Tag::Br => ("br", vec![], vec![]), + Tag::Cite { style, children } => ("cite", match get_style_string(style) { + Some(style) => vec![("style", style)], + None => vec![], + }, children), + Tag::Em { children } => ("em", vec![], children), + Tag::Img { src, alt } => { + let mut attrs = vec![]; + if let Some(src) = src { + attrs.push(("src", src)); + } + if let Some(alt) = alt { + attrs.push(("alt", alt)); + } + ("img", attrs, vec![]) + }, + Tag::Li { style, children } => ("li", match get_style_string(style) { + Some(style) => vec![("style", style)], + None => vec![], + }, children), + Tag::Ol { style, children } => ("ol", match get_style_string(style) { + Some(style) => vec![("style", style)], + None => vec![], + }, children), + Tag::P { style, children } => ("p", match get_style_string(style) { + Some(style) => vec![("style", style)], + None => vec![], + }, children), + Tag::Span { style, children } => ("span", match get_style_string(style) { + Some(style) => vec![("style", style)], + None => vec![], + }, children), + Tag::Strong { children } => ("strong", vec![], children), + Tag::Ul { style, children } => ("ul", match get_style_string(style) { + Some(style) => vec![("style", style)], + None => vec![], + }, children), + Tag::Unknown(children) => return Element::builder("unknown").ns(ns::XHTML).append(children_to_nodes(children)).build(), + }; + let mut builder = Element::builder(name) + .ns(ns::XHTML) + .append(children_to_nodes(children)); + for (key, value) in attrs { + builder = builder.attr(key, value); + } + builder.build() + } +} + +fn children_to_nodes(children: Vec) -> Vec { + children.into_iter().map(|child| match child { + Child::Tag(tag) => Node::Element(Element::from(tag)), + Child::Text(text) => Node::Text(text), + }).collect::>() +} + +fn children_to_html(children: Vec) -> String { + children.into_iter().map(|child| child.to_html()).collect::>().concat() +} + +fn write_attr(attr: Option, name: &str) -> String { + match attr { + Some(attr) => format!(" {}='{}'", name, attr), + None => String::new(), + } +} + +fn parse_css(style: Option<&str>) -> Css { + let mut properties = vec![]; + if let Some(style) = style { + // TODO: make that parser a bit more resilient to things. + for part in style.split(";") { + let mut part = part.splitn(2, ":").map(|a| a.to_string()).collect::>(); + let key = part.pop().unwrap(); + let value = part.pop().unwrap(); + properties.push(Property { key, value }); + } + } + properties +} + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg(target_pointer_width = "32")] + #[test] + #[ignore] + fn test_size() { + assert_size!(XhtmlIm, 0); + assert_size!(Child, 0); + assert_size!(Tag, 0); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(XhtmlIm, 56); + assert_size!(Child, 112); + assert_size!(Tag, 104); + } + + #[test] + fn test_empty() { + let elem: Element = "" + .parse() + .unwrap(); + let xhtml = XhtmlIm::try_from(elem).unwrap(); + assert_eq!(xhtml.bodies.len(), 0); + + let elem: Element = "" + .parse() + .unwrap(); + let xhtml = XhtmlIm::try_from(elem).unwrap(); + assert_eq!(xhtml.bodies.len(), 1); + + let elem: Element = "" + .parse() + .unwrap(); + let xhtml = XhtmlIm::try_from(elem).unwrap(); + assert_eq!(xhtml.bodies.len(), 2); + } + + #[test] + fn invalid_two_same_langs() { + let elem: Element = "" + .parse() + .unwrap(); + let error = XhtmlIm::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Two identical language bodies found in XHTML-IM."); + } + + #[test] + fn test_tag() { + let elem: Element = "" + .parse() + .unwrap(); + let body = Tag::try_from(elem).unwrap(); + match body { + Tag::Body { style: _, xml_lang: _, children } => assert_eq!(children.len(), 0), + _ => panic!(), + } + + let elem: Element = "

Hello world!

" + .parse() + .unwrap(); + let body = Tag::try_from(elem).unwrap(); + let mut children = match body { + Tag::Body { style, xml_lang, children } => { + assert_eq!(style.len(), 0); + assert_eq!(xml_lang, None); + assert_eq!(children.len(), 1); + children + }, + _ => panic!(), + }; + let p = match children.pop() { + Some(Child::Tag(tag)) => tag, + _ => panic!(), + }; + let mut children = match p { + Tag::P { style, children } => { + assert_eq!(style.len(), 0); + assert_eq!(children.len(), 1); + children + }, + _ => panic!(), + }; + let text = match children.pop() { + Some(Child::Text(text)) => text, + _ => panic!(), + }; + assert_eq!(text, "Hello world!"); + } + + #[test] + fn test_unknown_element() { + let elem: Element = "Hello world!" + .parse() + .unwrap(); + let xhtml_im = XhtmlIm::try_from(elem).unwrap(); + let html = xhtml_im.to_html(); + assert_eq!(html, "Hello world!"); + } + + #[test] + fn test_generate_html() { + let elem: Element = "

Hello world!

" + .parse() + .unwrap(); + let xhtml_im = XhtmlIm::try_from(elem).unwrap(); + let html = xhtml_im.to_html(); + assert_eq!(html, "

Hello world!

"); + + let elem: Element = "

Hello world!

" + .parse() + .unwrap(); + let xhtml_im = XhtmlIm::try_from(elem).unwrap(); + let html = xhtml_im.to_html(); + assert_eq!(html, "

Hello world!

"); + } +} From 2f45d586b5b2783699090f7a1700a7d1f072d3d8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 25 Aug 2019 20:02:06 +0200 Subject: [PATCH 647/698] xhtml: Add a tree generation example. --- src/xhtml.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/xhtml.rs b/src/xhtml.rs index c4fc0c05685448160a05fe6bc98f882676099492..43ea9e611fe87f602039729dfe8b661d814bcdab 100644 --- a/src/xhtml.rs +++ b/src/xhtml.rs @@ -469,4 +469,19 @@ mod tests { let html = xhtml_im.to_html(); assert_eq!(html, "

Hello world!

"); } + + #[test] + fn generate_tree() { + let world = "world".to_string(); + + Body { style: vec![], xml_lang: Some("en".to_string()), children: vec![ + Child::Tag(Tag::P { style: vec![], children: vec![ + Child::Text("Hello ".to_string()), + Child::Tag(Tag::Strong { children: vec![ + Child::Text(world), + ] }), + Child::Text("!".to_string()), + ] }), + ] }; + } } From 63d0265284e75859747878f63a09231cc479e9e1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 25 Aug 2019 20:02:33 +0200 Subject: [PATCH 648/698] xhtml: Move Body out of Tag, because it is the only top-level element. --- src/xhtml.rs | 135 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 80 insertions(+), 55 deletions(-) diff --git a/src/xhtml.rs b/src/xhtml.rs index 43ea9e611fe87f602039729dfe8b661d814bcdab..766109361e070980e1acc83f81dcf443a1a6cf53 100644 --- a/src/xhtml.rs +++ b/src/xhtml.rs @@ -18,7 +18,7 @@ type Lang = String; #[derive(Debug, Clone)] pub struct XhtmlIm { /// Map of language to body element. - bodies: HashMap, + bodies: HashMap, } impl XhtmlIm { @@ -27,22 +27,41 @@ impl XhtmlIm { let mut html = Vec::new(); // TODO: use the best language instead. for (lang, body) in self.bodies { - if let Tag::Body { style: _, xml_lang, children } = body { - if lang.is_empty() { - assert!(xml_lang.is_none()); - } else { - assert_eq!(Some(lang), xml_lang); - } - for tag in children { - html.push(tag.to_html()); - } - break; + if lang.is_empty() { + assert!(body.xml_lang.is_none()); } else { - unreachable!(); + assert_eq!(Some(lang), body.xml_lang); } + for tag in body.children { + html.push(tag.to_html()); + } + break; } html.concat() } + + /// Removes all unknown elements. + pub fn flatten(self) -> XhtmlIm { + let mut bodies = HashMap::new(); + for (lang, body) in self.bodies { + let children = body.children.into_iter().fold(vec![], |mut acc, child| { + match child { + Child::Tag(Tag::Unknown(children)) => acc.extend(children), + any => acc.push(any), + } + acc + }); + let body = Body { + style: body.style, + xml_lang: body.xml_lang, + children, + }; + bodies.insert(lang, body); + } + XhtmlIm { + bodies, + } + } } impl MessagePayload for XhtmlIm {} @@ -62,7 +81,7 @@ impl TryFrom for XhtmlIm { Some(lang) => lang, None => "", }.to_string(); - let body = Tag::try_from(child)?; + let body = Body::try_from(child)?; match bodies.insert(lang, body) { None => (), Some(_) => return Err(Error::ParseError("Two identical language bodies found in XHTML-IM.")) @@ -81,16 +100,12 @@ impl From for Element { Element::builder("html") .ns(ns::XHTML_IM) .append(wrapper.bodies.into_iter().map(|(ref lang, ref body)| { - if let Tag::Body { style, xml_lang, children } = body { - assert_eq!(Some(lang), xml_lang.as_ref()); - Element::builder("body") - .ns(ns::XHTML_IM) - .attr("style", get_style_string(style.clone())) - .attr("xml:lang", xml_lang.clone()) - .append(children_to_nodes(children.clone())) - } else { - unreachable!(); - } + assert_eq!(Some(lang), body.xml_lang.as_ref()); + Element::builder("body") + .ns(ns::XHTML_IM) + .attr("style", get_style_string(body.style.clone())) + .attr("xml:lang", body.xml_lang.clone()) + .append(children_to_nodes(body.children.clone())) }).collect::>()) .build() } @@ -130,11 +145,45 @@ fn get_style_string(style: Css) -> Option { Some(result.join("; ")) } +#[derive(Debug, Clone)] +struct Body { + style: Css, + xml_lang: Option, + children: Vec, +} + +impl TryFrom for Body { + type Error = Error; + + fn try_from(elem: Element) -> Result { + let mut children = vec![]; + for child in elem.nodes() { + match child { + Node::Element(child) => children.push(Child::Tag(Tag::try_from(child.clone())?)), + Node::Text(text) => children.push(Child::Text(text.clone())), + Node::Comment(_) => unimplemented!() // XXX: remove! + } + } + + Ok(Body { style: parse_css(elem.attr("style")), xml_lang: elem.attr("xml:lang").map(|xml_lang| xml_lang.to_string()), children }) + } +} + +impl From for Element { + fn from(body: Body) -> Element { + Element::builder("body") + .ns(ns::XHTML) + .attr("style", get_style_string(body.style)) + .attr("xml:lang", body.xml_lang) + .append(children_to_nodes(body.children)) + .build() + } +} + #[derive(Debug, Clone)] enum Tag { A { href: Option, style: Css, type_: Option, children: Vec }, Blockquote { style: Css, children: Vec }, - Body { style: Css, xml_lang: Option, children: Vec }, Br, Cite { style: Css, children: Vec }, Em { children: Vec }, @@ -161,10 +210,6 @@ impl Tag { let style = write_attr(get_style_string(style), "style"); format!("{}", style, children_to_html(children)) }, - Tag::Body { style, xml_lang: _, children } => { - let style = write_attr(get_style_string(style), "style"); - format!("{}", style, children_to_html(children)) - }, Tag::Br => String::from("
"), Tag::Cite { style, children } => { let style = write_attr(get_style_string(style), "style"); @@ -218,7 +263,6 @@ impl TryFrom for Tag { Ok(match elem.name() { "a" => Tag::A { href: elem.attr("href").map(|href| href.to_string()), style: parse_css(elem.attr("style")), type_: elem.attr("type").map(|type_| type_.to_string()), children }, "blockquote" => Tag::Blockquote { style: parse_css(elem.attr("style")), children }, - "body" => Tag::Body { style: parse_css(elem.attr("style")), xml_lang: elem.attr("xml:lang").map(|xml_lang| xml_lang.to_string()), children }, "br" => Tag::Br, "cite" => Tag::Cite { style: parse_css(elem.attr("style")), children }, "em" => Tag::Em { children }, @@ -254,16 +298,6 @@ impl From for Element { Some(style) => vec![("style", style)], None => vec![], }, children), - Tag::Body { style, xml_lang, children } => ("body", { - let mut attrs = vec![]; - if let Some(style) = get_style_string(style) { - attrs.push(("style", style)); - } - if let Some(xml_lang) = xml_lang { - attrs.push(("xml:lang", xml_lang)); - } - attrs - }, children), Tag::Br => ("br", vec![], vec![]), Tag::Cite { style, children } => ("cite", match get_style_string(style) { Some(style) => vec![("style", style)], @@ -405,26 +439,17 @@ mod tests { let elem: Element = "" .parse() .unwrap(); - let body = Tag::try_from(elem).unwrap(); - match body { - Tag::Body { style: _, xml_lang: _, children } => assert_eq!(children.len(), 0), - _ => panic!(), - } + let body = Body::try_from(elem).unwrap(); + assert_eq!(body.children.len(), 0); let elem: Element = "

Hello world!

" .parse() .unwrap(); - let body = Tag::try_from(elem).unwrap(); - let mut children = match body { - Tag::Body { style, xml_lang, children } => { - assert_eq!(style.len(), 0); - assert_eq!(xml_lang, None); - assert_eq!(children.len(), 1); - children - }, - _ => panic!(), - }; - let p = match children.pop() { + let mut body = Body::try_from(elem).unwrap(); + assert_eq!(body.style.len(), 0); + assert_eq!(body.xml_lang, None); + assert_eq!(body.children.len(), 1); + let p = match body.children.pop() { Some(Child::Tag(tag)) => tag, _ => panic!(), }; From f528a45568af1a476026191d51143f58091ca377 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 4 Sep 2019 18:14:39 +0200 Subject: [PATCH 649/698] xhtml: Automatically flatten on parsing. --- src/xhtml.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/xhtml.rs b/src/xhtml.rs index 766109361e070980e1acc83f81dcf443a1a6cf53..8b9635e6a121974b7363ecbe0554a152c163dac9 100644 --- a/src/xhtml.rs +++ b/src/xhtml.rs @@ -41,7 +41,7 @@ impl XhtmlIm { } /// Removes all unknown elements. - pub fn flatten(self) -> XhtmlIm { + fn flatten(self) -> XhtmlIm { let mut bodies = HashMap::new(); for (lang, body) in self.bodies { let children = body.children.into_iter().fold(vec![], |mut acc, child| { @@ -91,7 +91,7 @@ impl TryFrom for XhtmlIm { } } - Ok(XhtmlIm { bodies }) + Ok(XhtmlIm { bodies }.flatten()) } } @@ -237,7 +237,7 @@ impl Tag { let style = write_attr(get_style_string(style), "style"); format!("{}", style, children_to_html(children)) } - Tag::Strong { children } => format!("{}", children.into_iter().map(|child| child.to_html()).collect::>().join("")), + Tag::Strong { children } => format!("{}", children_to_html(children)), Tag::Ul { style, children } => { let style = write_attr(get_style_string(style), "style"); format!("{}", style, children_to_html(children)) From 24e862e35290e88e3b25591fe431c36610e1af47 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 5 Sep 2019 11:51:05 +0200 Subject: [PATCH 650/698] xhtml: Fix namespace on Body serialisation. --- src/xhtml.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/xhtml.rs b/src/xhtml.rs index 8b9635e6a121974b7363ecbe0554a152c163dac9..224e0bb54447a75dbccf50e9684e8d16f9432ce1 100644 --- a/src/xhtml.rs +++ b/src/xhtml.rs @@ -52,9 +52,8 @@ impl XhtmlIm { acc }); let body = Body { - style: body.style, - xml_lang: body.xml_lang, children, + ..body }; bodies.insert(lang, body); } @@ -100,12 +99,12 @@ impl From for Element { Element::builder("html") .ns(ns::XHTML_IM) .append(wrapper.bodies.into_iter().map(|(ref lang, ref body)| { - assert_eq!(Some(lang), body.xml_lang.as_ref()); - Element::builder("body") - .ns(ns::XHTML_IM) - .attr("style", get_style_string(body.style.clone())) - .attr("xml:lang", body.xml_lang.clone()) - .append(children_to_nodes(body.children.clone())) + if lang.is_empty() { + assert!(body.xml_lang.is_none()); + } else { + assert_eq!(Some(lang), body.xml_lang.as_ref()); + } + Element::from(body.clone()) }).collect::>()) .build() } @@ -473,9 +472,13 @@ mod tests { let elem: Element = "Hello world!" .parse() .unwrap(); - let xhtml_im = XhtmlIm::try_from(elem).unwrap(); - let html = xhtml_im.to_html(); + let parsed = XhtmlIm::try_from(elem).unwrap(); + let parsed2 = parsed.clone(); + let html = parsed.to_html(); assert_eq!(html, "Hello world!"); + + let elem = Element::from(parsed2); + assert_eq!(String::from(&elem), "Hello world!"); } #[test] From cde011aa5e6b32f6236b133439d2432a1baa9fef Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 5 Sep 2019 11:58:48 +0200 Subject: [PATCH 651/698] xhtml: Panic on unknown elements still present after parsing. --- src/xhtml.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/xhtml.rs b/src/xhtml.rs index 224e0bb54447a75dbccf50e9684e8d16f9432ce1..1ec6a0478f08ec7cd2421ceb3d168f1671a6d371 100644 --- a/src/xhtml.rs +++ b/src/xhtml.rs @@ -241,7 +241,7 @@ impl Tag { let style = write_attr(get_style_string(style), "style"); format!("{}", style, children_to_html(children)) } - Tag::Unknown(children) => children_to_html(children), + Tag::Unknown(children) => panic!("No unknown element should be present in XHTML-IM after parsing."), } } } @@ -334,7 +334,7 @@ impl From for Element { Some(style) => vec![("style", style)], None => vec![], }, children), - Tag::Unknown(children) => return Element::builder("unknown").ns(ns::XHTML).append(children_to_nodes(children)).build(), + Tag::Unknown(children) => panic!("No unknown element should be present in XHTML-IM after parsing."), }; let mut builder = Element::builder(name) .ns(ns::XHTML) From f25d4c79b7cb454db08916e510cf2c053519a9bb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 5 Sep 2019 14:13:37 +0200 Subject: [PATCH 652/698] xhtml: Use _ for children of Unknown. --- src/xhtml.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/xhtml.rs b/src/xhtml.rs index 1ec6a0478f08ec7cd2421ceb3d168f1671a6d371..9c43181bf06fc38e88568b101b6971a6ae466562 100644 --- a/src/xhtml.rs +++ b/src/xhtml.rs @@ -241,7 +241,7 @@ impl Tag { let style = write_attr(get_style_string(style), "style"); format!("{}", style, children_to_html(children)) } - Tag::Unknown(children) => panic!("No unknown element should be present in XHTML-IM after parsing."), + Tag::Unknown(_) => panic!("No unknown element should be present in XHTML-IM after parsing."), } } } @@ -334,7 +334,7 @@ impl From for Element { Some(style) => vec![("style", style)], None => vec![], }, children), - Tag::Unknown(children) => panic!("No unknown element should be present in XHTML-IM after parsing."), + Tag::Unknown(_) => panic!("No unknown element should be present in XHTML-IM after parsing."), }; let mut builder = Element::builder(name) .ns(ns::XHTML) From c77221e43748aed5336de836fb4076c844b38fb3 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 5 Sep 2019 15:34:21 +0200 Subject: [PATCH 653/698] macros: Allow non-String in generate_elem_id!(). --- src/mood.rs | 4 ++++ src/muc/user.rs | 4 ++++ src/util/macros.rs | 13 ++++++++----- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/mood.rs b/src/mood.rs index 8d27c4f0629db50ff50675003532f7923535e4be..f4b529c592ba6e7719e261138c03a735c1d2ff04 100644 --- a/src/mood.rs +++ b/src/mood.rs @@ -302,7 +302,11 @@ mod tests { let elem: Element = "Yay!" .parse() .unwrap(); + let elem2 = elem.clone(); let text = Text::try_from(elem).unwrap(); assert_eq!(text.0, String::from("Yay!")); + + let elem3 = text.into(); + assert_eq!(elem2, elem3); } } diff --git a/src/muc/user.rs b/src/muc/user.rs index 44af3a9c734ff689a01f8f63e6846c8bd919b88a..d785d2356cd208adc2a4a22d0a363fb1a66f5128 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -511,8 +511,12 @@ mod tests { Reason" .parse() .unwrap(); + let elem2 = elem.clone(); let reason = Reason::try_from(elem).unwrap(); assert_eq!(reason.0, "Reason".to_owned()); + + let elem3 = reason.into(); + assert_eq!(elem2, elem3); } #[cfg(not(feature = "disable-validation"))] diff --git a/src/util/macros.rs b/src/util/macros.rs index 5b54cf65d57d97cf38778f18cc70bc616776b410..c4b5c4e0b85d75ac68624821b57bbe55ebd808ef 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -410,9 +410,7 @@ macro_rules! generate_id { macro_rules! generate_elem_id { ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident) => ( - $(#[$meta])* - #[derive(Debug, Clone, PartialEq, Eq, Hash)] - pub struct $elem(pub String); + generate_elem_id!($(#[$meta])* $elem, $name, $ns, String); impl ::std::str::FromStr for $elem { type Err = crate::util::error::Error; fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> { @@ -420,6 +418,11 @@ macro_rules! generate_elem_id { Ok($elem(String::from(s))) } } + ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, $type:ty) => ( + $(#[$meta])* + #[derive(Debug, Clone, PartialEq, Eq, Hash)] + pub struct $elem(pub $type); impl ::std::convert::TryFrom<::minidom::Element> for $elem { type Error = crate::util::error::Error; fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { @@ -427,14 +430,14 @@ macro_rules! generate_elem_id { check_no_children!(elem, $name); check_no_attributes!(elem, $name); // TODO: add a way to parse that differently when needed. - Ok($elem(elem.text())) + Ok($elem(elem.text().parse()?)) } } impl From<$elem> for ::minidom::Element { fn from(elem: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) .ns(crate::ns::$ns) - .append(elem.0) + .append(elem.0.to_string()) .build() } } From 77920e5f466662e3a7cea03baf4f6aa4e0a68937 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 5 Sep 2019 15:37:34 +0200 Subject: [PATCH 654/698] New User Tune parser (XEP-0118). --- ChangeLog | 1 + doap.xml | 8 ++ src/lib.rs | 3 + src/ns.rs | 3 + src/tune.rs | 214 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 229 insertions(+) create mode 100644 src/tune.rs diff --git a/ChangeLog b/ChangeLog index eec3bbb8cb21ca2b08b42a528f354d846e8f2aa6..5ecea28ef465af043d041b95750d673ead917e6a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,7 @@ Version NEXT: DATE Emmanuel Gil Peyrot * New parsers/serialisers: - XHTML-IM (XEP-0071) + - User Tune (XEP-0118) - Bits of Binary (XEP-0231) - Message Carbons (XEP-0280) * Breaking changes: diff --git a/doap.xml b/doap.xml index a3165dc0b94df64facbfe2696f0579e0efc62bab..561f49dd133eef7655c5c2fbdbce8fda19175db2 100644 --- a/doap.xml +++ b/doap.xml @@ -183,6 +183,14 @@ 0.4.0 + + + + complete + 1.2 + NEXT + + diff --git a/src/lib.rs b/src/lib.rs index af2fc129566d64a4dc6ca7121fd95c0e56bbc6da..d989812348b2a9dd141974da0b672eec97e19ead 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -102,6 +102,9 @@ pub mod component; /// XEP-0115: Entity Capabilities pub mod caps; +/// XEP-0118: User Tune +pub mod tune; + /// XEP-0157: Contact Addresses for XMPP Services pub mod server_info; diff --git a/src/ns.rs b/src/ns.rs index 5c59ec03cccbf36169f5bf355ca27e10ea377528..2c932c3ced7c9e8e844aa7a362f1541f2f021008 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -84,6 +84,9 @@ pub const COMPONENT: &str = "jabber:component:accept"; /// XEP-0115: Entity Capabilities pub const CAPS: &str = "http://jabber.org/protocol/caps"; +/// XEP-0118: User Tune +pub const TUNE: &str = "http://jabber.org/protocol/tune"; + /// XEP-0157: Contact Addresses for XMPP Services pub const SERVER_INFO: &str = "http://jabber.org/network/serverinfo"; diff --git a/src/tune.rs b/src/tune.rs new file mode 100644 index 0000000000000000000000000000000000000000..7a0b9f3a74d6dc5b097ff0cf790273831a1a7192 --- /dev/null +++ b/src/tune.rs @@ -0,0 +1,214 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use crate::util::error::Error; +use crate::pubsub::PubSubPayload; +use crate::ns; +use minidom::Element; +use std::convert::TryFrom; + +generate_elem_id!( + /// The artist or performer of the song or piece. + Artist, "artist", TUNE +); + +generate_elem_id!( + /// The duration of the song or piece in seconds. + Length, "length", TUNE, + u16 +); + +generate_elem_id!( + /// The user's rating of the song or piece, from 1 (lowest) to 10 (highest). + Rating, "rating", TUNE, + u8 +); + +generate_elem_id!( + /// The collection (e.g., album) or other source (e.g., a band website that hosts streams or + /// audio files). + Source, "source", TUNE +); + +generate_elem_id!( + /// The title of the song or piece. + Title, "title", TUNE +); + +generate_elem_id!( + /// A unique identifier for the tune; e.g., the track number within a collection or the + /// specific URI for the object (e.g., a stream or audio file). + Track, "track", TUNE +); + +generate_elem_id!( + /// A URI or URL pointing to information about the song, collection, or artist. + Uri, "uri", TUNE +); + +/// Container for formatted text. +#[derive(Debug, Clone)] +pub struct Tune { + /// The artist or performer of the song or piece. + artist: Option, + + /// The duration of the song or piece in seconds. + length: Option, + + /// The user's rating of the song or piece, from 1 (lowest) to 10 (highest). + rating: Option, + + /// The collection (e.g., album) or other source (e.g., a band website that hosts streams or + /// audio files). + source: Option, + + /// The title of the song or piece. + title: Option, + + /// A unique identifier for the tune; e.g., the track number within a collection or the + /// specific URI for the object (e.g., a stream or audio file). + track: Option<Track>, + + /// A URI or URL pointing to information about the song, collection, or artist. + uri: Option<Uri>, +} + +impl PubSubPayload for Tune {} + +impl Tune { + fn new() -> Tune { + Tune { + artist: None, + length: None, + rating: None, + source: None, + title: None, + track: None, + uri: None, + } + } +} + +impl TryFrom<Element> for Tune { + type Error = Error; + + fn try_from(elem: Element) -> Result<Tune, Error> { + check_self!(elem, "tune", TUNE); + check_no_attributes!(elem, "tune"); + + let mut tune = Tune::new(); + for child in elem.children() { + if child.is("artist", ns::TUNE) { + if tune.artist.is_some() { + return Err(Error::ParseError("Tune can’t have more than one artist.")); + } + tune.artist = Some(Artist::try_from(child.clone())?); + } else if child.is("length", ns::TUNE) { + if tune.length.is_some() { + return Err(Error::ParseError("Tune can’t have more than one length.")); + } + tune.length = Some(Length::try_from(child.clone())?); + } else if child.is("rating", ns::TUNE) { + if tune.rating.is_some() { + return Err(Error::ParseError("Tune can’t have more than one rating.")); + } + tune.rating = Some(Rating::try_from(child.clone())?); + } else if child.is("source", ns::TUNE) { + if tune.source.is_some() { + return Err(Error::ParseError("Tune can’t have more than one source.")); + } + tune.source = Some(Source::try_from(child.clone())?); + } else if child.is("title", ns::TUNE) { + if tune.title.is_some() { + return Err(Error::ParseError("Tune can’t have more than one title.")); + } + tune.title = Some(Title::try_from(child.clone())?); + } else if child.is("track", ns::TUNE) { + if tune.track.is_some() { + return Err(Error::ParseError("Tune can’t have more than one track.")); + } + tune.track = Some(Track::try_from(child.clone())?); + } else if child.is("uri", ns::TUNE) { + if tune.uri.is_some() { + return Err(Error::ParseError("Tune can’t have more than one uri.")); + } + tune.uri = Some(Uri::try_from(child.clone())?); + } else { + return Err(Error::ParseError("Unknown element in User Tune.")); + } + } + + Ok(tune) + } +} + +impl From<Tune> for Element { + fn from(tune: Tune) -> Element { + Element::builder("tune") + .ns(ns::TUNE) + .append(tune.artist) + .append(tune.length) + .append(tune.rating) + .append(tune.source) + .append(tune.title) + .append(tune.track) + .append(tune.uri) + .build() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::str::FromStr; + + #[cfg(target_pointer_width = "32")] + #[test] + #[ignore] + fn test_size() { + assert_size!(Tune, 0); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(Tune, 128); + } + + #[test] + fn empty() { + let elem: Element = "<tune xmlns='http://jabber.org/protocol/tune'/>" + .parse() + .unwrap(); + let elem2 = elem.clone(); + let tune = Tune::try_from(elem).unwrap(); + assert!(tune.artist.is_none()); + assert!(tune.length.is_none()); + assert!(tune.rating.is_none()); + assert!(tune.source.is_none()); + assert!(tune.title.is_none()); + assert!(tune.track.is_none()); + assert!(tune.uri.is_none()); + + let elem3 = tune.into(); + assert_eq!(elem2, elem3); + } + + #[test] + fn full() { + let elem: Element = "<tune xmlns='http://jabber.org/protocol/tune'><artist>Yes</artist><length>686</length><rating>8</rating><source>Yessongs</source><title>Heart of the Sunrise3http://www.yesworld.com/lyrics/Fragile.html#9" + .parse() + .unwrap(); + let tune = Tune::try_from(elem).unwrap(); + assert_eq!(tune.artist, Some(Artist::from_str("Yes").unwrap())); + assert_eq!(tune.length, Some(Length(686))); + assert_eq!(tune.rating, Some(Rating(8))); + assert_eq!(tune.source, Some(Source::from_str("Yessongs").unwrap())); + assert_eq!(tune.title, Some(Title::from_str("Heart of the Sunrise").unwrap())); + assert_eq!(tune.track, Some(Track::from_str("3").unwrap())); + assert_eq!(tune.uri, Some(Uri::from_str("http://www.yesworld.com/lyrics/Fragile.html#9").unwrap())); + } +} From 0ef4ba7e55f87ee5f6a439130bd7757f88a9ac4b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 5 Sep 2019 17:58:45 +0200 Subject: [PATCH 655/698] tune: Add size checks for all elements. --- src/tune.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/tune.rs b/src/tune.rs index 7a0b9f3a74d6dc5b097ff0cf790273831a1a7192..1bbb436a618f6c8c9c45fb5393f363fd47863bac 100644 --- a/src/tune.rs +++ b/src/tune.rs @@ -169,13 +169,27 @@ mod tests { #[test] #[ignore] fn test_size() { - assert_size!(Tune, 0); + assert_size!(Tune, 64); + assert_size!(Artist, 12); + assert_size!(Length, 2); + assert_size!(Rating, 1); + assert_size!(Source, 12); + assert_size!(Title, 12); + assert_size!(Track, 12); + assert_size!(Uri, 12); } #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Tune, 128); + assert_size!(Artist, 24); + assert_size!(Length, 2); + assert_size!(Rating, 1); + assert_size!(Source, 24); + assert_size!(Title, 24); + assert_size!(Track, 24); + assert_size!(Uri, 24); } #[test] From a1ae45add88e424710073711935a3605cafe9ef9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Thu, 25 Jul 2019 00:20:38 +0200 Subject: [PATCH 656/698] Update minidom dependency to 0.11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- Cargo.toml | 4 +- src/bind.rs | 4 +- src/blocking.rs | 4 +- src/data_forms.rs | 23 +++++----- src/date.rs | 8 ++-- src/disco.rs | 8 ++-- src/ibr.rs | 15 +++---- src/jingle.rs | 10 ++--- src/jingle_ft.rs | 58 +++++++++++++++---------- src/jingle_s5b.rs | 23 ++++++---- src/mam.rs | 29 ++++++++----- src/message.rs | 8 ++-- src/presence.rs | 36 ++++++++------- src/pubsub/event.rs | 11 +++-- src/pubsub/pubsub.rs | 8 ++-- src/rsm.rs | 16 +++---- src/sasl.rs | 3 +- src/stanza_error.rs | 9 ++-- src/tune.rs | 14 +++--- src/util/helpers.rs | 4 +- src/util/macros.rs | 101 ++++++++++++++++++++++++++++++++----------- src/xhtml.rs | 8 ++-- 22 files changed, 236 insertions(+), 168 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fba43cb47e76b689fe177309fbca26f4e52d0241..6690ccf66ba81a972eb767e3cb620e3e0d61707f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,8 +14,8 @@ license = "MPL-2.0" edition = "2018" [dependencies] -minidom = "0.10.0" -jid = { version = "0.6.0", features = ["minidom"] } +minidom = "0.11.0" +jid = { version = "0.7.0", features = ["minidom"] } base64 = "0.10" digest = "0.8" sha-1 = "0.8" diff --git a/src/bind.rs b/src/bind.rs index 357a7326b4448352494b2923d7456294ce1d8395..17e1e1240ac5ac9895bbd08250f6ae92a4c85e69 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -63,13 +63,13 @@ impl From for Element { fn from(bind: BindQuery) -> Element { Element::builder("bind") .ns(ns::BIND) - .append(match bind.resource { + .append_all((match bind.resource { None => vec![], Some(resource) => vec![Element::builder("resource") .ns(ns::BIND) .append(resource) .build()], - }) + }).into_iter()) .build() } } diff --git a/src/blocking.rs b/src/blocking.rs index f218b21d4985dd4894d56d1c3e1e9dcc34c16902..f9e6a821bc3769891a80da0404e1d6bf5ef86ceb 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -51,12 +51,12 @@ macro_rules! generate_blocking_element { fn from(elem: $elem) -> Element { Element::builder($name) .ns(ns::BLOCKING) - .append(elem.items.into_iter().map(|jid| { + .append_all(elem.items.into_iter().map(|jid| { Element::builder("item") .ns(ns::BLOCKING) .attr("jid", jid) .build() - }).collect::>()) + })) .build() } } diff --git a/src/data_forms.rs b/src/data_forms.rs index 323e91ed091b7411dc1a2eb82e40b28fc4357853..f252bb714f5b82efb9ee1417c426d5f3a8b63771 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -7,7 +7,7 @@ use crate::util::error::Error; use crate::media_element::MediaElement; use crate::ns; -use minidom::Element; +use minidom::{Element, Node}; use std::convert::TryFrom; generate_element!( @@ -152,13 +152,13 @@ impl From for Element { .attr("var", field.var) .attr("type", field.type_) .attr("label", field.label) - .append(if field.required { + .append_all((if field.required { Some(Element::builder("required").ns(ns::DATA_FORMS).build()) } else { None - }) - .append(field.options) - .append( + }).into_iter()) + .append_all(field.options.iter().cloned().map(Element::from).map(Node::Element).into_iter()) + .append_all( field .values .into_iter() @@ -168,9 +168,8 @@ impl From for Element { .append(value) .build() }) - .collect::>(), ) - .append(field.media) + .append_all(field.media.iter().cloned().map(Element::from).map(Node::Element)) .build() } } @@ -275,21 +274,21 @@ impl From for Element { Element::builder("x") .ns(ns::DATA_FORMS) .attr("type", form.type_) - .append( + .append_all( form.title .map(|title| Element::builder("title").ns(ns::DATA_FORMS).append(title)), ) - .append(form.instructions.map(|text| { + .append_all(form.instructions.map(|text| { Element::builder("instructions") .ns(ns::DATA_FORMS) .append(text) })) - .append(if let Some(form_type) = form.form_type { + .append_all((if let Some(form_type) = form.form_type { vec![Element::builder("field").ns(ns::DATA_FORMS).attr("var", "FORM_TYPE").attr("type", "hidden").append(Element::builder("value").ns(ns::DATA_FORMS).append(form_type).build()).build()] } else { vec![] - }) - .append(form.fields) + }).into_iter()) + .append_all(form.fields.iter().cloned().map(Element::from).map(Node::Element)) .build() } } diff --git a/src/date.rs b/src/date.rs index e5fd67e89dc1de926760f39aa1a107d2440ca011..bfcf4e166de8cf1097a5d4f6010c87ae48536b7e 100644 --- a/src/date.rs +++ b/src/date.rs @@ -6,7 +6,7 @@ use crate::util::error::Error; use chrono::{DateTime as ChronoDateTime, FixedOffset}; -use minidom::{ElementEmitter, IntoAttributeValue, IntoElements}; +use minidom::{IntoAttributeValue, Node}; use std::str::FromStr; /// Implements the DateTime profile of XEP-0082, which represents a @@ -46,9 +46,9 @@ impl IntoAttributeValue for DateTime { } } -impl IntoElements for DateTime { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_text_node(self.0.to_rfc3339()) +impl Into for DateTime { + fn into(self) -> Node { + Node::Text(self.0.to_rfc3339()) } } diff --git a/src/disco.rs b/src/disco.rs index abf0e238f152afcdab91ca8eb270d9cc32e4f7aa..3c2cde13835203e97c070c52d00c67ed8e43a87b 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -9,7 +9,7 @@ use crate::util::error::Error; use crate::iq::{IqGetPayload, IqResultPayload}; use crate::ns; use jid::Jid; -use minidom::Element; +use minidom::{Element, Node}; use std::convert::TryFrom; generate_element!( @@ -178,9 +178,9 @@ impl From for Element { Element::builder("query") .ns(ns::DISCO_INFO) .attr("node", disco.node) - .append(disco.identities) - .append(disco.features) - .append(disco.extensions) + .append_all(disco.identities.into_iter()) + .append_all(disco.features.into_iter()) + .append_all(disco.extensions.iter().cloned().map(Element::from).map(Node::Element)) .build() } } diff --git a/src/ibr.rs b/src/ibr.rs index 8ddca59dc4ebe34b3c1868f2d4d60f1dfbca9ce5..d539f2adce83fa4cdf8fe13ba650ba0be5f277af 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -8,7 +8,7 @@ use crate::data_forms::DataForm; use crate::util::error::Error; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; -use minidom::Element; +use minidom::{Element, Node}; use std::collections::HashMap; use std::convert::TryFrom; @@ -93,24 +93,23 @@ impl From for Element { fn from(query: Query) -> Element { Element::builder("query") .ns(ns::REGISTER) - .append(if query.registered { + .append_all((if query.registered { Some(Element::builder("registered").ns(ns::REGISTER)) } else { None - }) - .append( + }).into_iter()) + .append_all( query .fields .into_iter() .map(|(name, value)| Element::builder(name).ns(ns::REGISTER).append(value)) - .collect::>(), ) - .append(if query.remove { + .append_all((if query.remove { Some(Element::builder("remove").ns(ns::REGISTER)) } else { None - }) - .append(query.form) + }).into_iter()) + .append_all(query.form.map(Element::from).map(Node::Element).into_iter()) .build() } } diff --git a/src/jingle.rs b/src/jingle.rs index 98e5fcbb46db4620ed82b2efae8a6a4026ba5892..55a493ef407fa7785be7d94125cf2d4fcb55ea80 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -8,7 +8,7 @@ use crate::util::error::Error; use crate::iq::IqSetPayload; use crate::ns; use jid::Jid; -use minidom::Element; +use minidom::{Element, Node}; use std::collections::BTreeMap; use std::str::FromStr; use std::convert::TryFrom; @@ -413,14 +413,14 @@ impl From for Element { Element::builder("reason") .ns(ns::JINGLE) .append(Element::from(reason.reason)) - .append( + .append_all( reason.texts.into_iter().map(|(lang, text)| { Element::builder("text") .ns(ns::JINGLE) .attr("xml:lang", lang) .append(text) .build() - }).collect::>()) + })) .build() } } @@ -542,8 +542,8 @@ impl From for Element { .attr("initiator", jingle.initiator) .attr("responder", jingle.responder) .attr("sid", jingle.sid) - .append(jingle.contents) - .append(jingle.reason) + .append_all(jingle.contents.into_iter()) + .append_all(jingle.reason.map(Element::from).map(Node::Element).into_iter()) .build() } } diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index f0b7cf32461fe8e1982bc9a309f67f3fa3db5f74..b3da6f8b9661b027d46e5fa0002a5f28672f98db 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -9,7 +9,7 @@ use crate::util::error::Error; use crate::hashes::Hash; use crate::jingle::{ContentId, Creator}; use crate::ns; -use minidom::Element; +use minidom::{Element, Node}; use std::collections::BTreeMap; use std::str::FromStr; use std::convert::TryFrom; @@ -196,43 +196,53 @@ impl From for Element { let mut root = Element::builder("file").ns(ns::JINGLE_FT); if let Some(date) = file.date { root = root.append( - Element::builder("date") - .ns(ns::JINGLE_FT) - .append(date) - .build(), + Node::Element( + Element::builder("date") + .ns(ns::JINGLE_FT) + .append(date) + .build() + ) ); } if let Some(media_type) = file.media_type { root = root.append( - Element::builder("media-type") - .ns(ns::JINGLE_FT) - .append(media_type) - .build(), + Node::Element( + Element::builder("media-type") + .ns(ns::JINGLE_FT) + .append(media_type) + .build() + ) ); } if let Some(name) = file.name { root = root.append( - Element::builder("name") - .ns(ns::JINGLE_FT) - .append(name) - .build(), + Node::Element( + Element::builder("name") + .ns(ns::JINGLE_FT) + .append(name) + .build() + ) ); } for (lang, desc) in file.descs.into_iter() { root = root.append( - Element::builder("desc") - .ns(ns::JINGLE_FT) - .attr("xml:lang", lang) - .append(desc.0) - .build(), + Node::Element( + Element::builder("desc") + .ns(ns::JINGLE_FT) + .attr("xml:lang", lang) + .append(desc.0) + .build() + ) ); } if let Some(size) = file.size { root = root.append( - Element::builder("size") - .ns(ns::JINGLE_FT) - .append(format!("{}", size)) - .build(), + Node::Element( + Element::builder("size") + .ns(ns::JINGLE_FT) + .append(format!("{}", size)) + .build() + ) ); } if let Some(range) = file.range { @@ -282,7 +292,7 @@ impl From for Element { fn from(description: Description) -> Element { Element::builder("description") .ns(ns::JINGLE_FT) - .append(description.file) + .append(Node::Element(description.file.into())) .build() } } @@ -334,7 +344,7 @@ impl From for Element { .ns(ns::JINGLE_FT) .attr("name", checksum.name) .attr("creator", checksum.creator) - .append(checksum.file) + .append(Node::Element(checksum.file.into())) .build() } } diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 969b83f30ed562ef354660cecea560ab88786a5b..0f246691c40c1c065c252ffe473113f3bd01b659 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -247,26 +247,31 @@ impl From for Element { .attr("sid", transport.sid) .attr("dstaddr", transport.dstaddr) .attr("mode", transport.mode) - .append(match transport.payload { + .append_all(match transport.payload { TransportPayload::Candidates(candidates) => candidates .into_iter() .map(Element::from) - .collect::>(), + .collect::>() + .into_iter(), TransportPayload::Activated(cid) => vec![Element::builder("activated") .ns(ns::JINGLE_S5B) .attr("cid", cid) - .build()], + .build()] + .into_iter(), TransportPayload::CandidateError => vec![Element::builder("candidate-error") .ns(ns::JINGLE_S5B) - .build()], + .build()] + .into_iter(), TransportPayload::CandidateUsed(cid) => vec![Element::builder("candidate-used") .ns(ns::JINGLE_S5B) .attr("cid", cid) - .build()], - TransportPayload::ProxyError => { - vec![Element::builder("proxy-error").ns(ns::JINGLE_S5B).build()] - } - TransportPayload::None => vec![], + .build()] + .into_iter(), + TransportPayload::ProxyError => vec![Element::builder("proxy-error") + .ns(ns::JINGLE_S5B) + .build()] + .into_iter(), + TransportPayload::None => vec![].into_iter(), }) .build() } diff --git a/src/mam.rs b/src/mam.rs index 6889f2edf85cd5d1139748b7cf2aa893c15cc570..edc0116c4269dcdf57655c761e306d814496e39e 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -13,7 +13,7 @@ use crate::ns; use crate::pubsub::NodeName; use crate::rsm::{SetQuery, SetResult}; use jid::Jid; -use minidom::Element; +use minidom::{Element, Node}; use std::convert::TryFrom; generate_id!( @@ -161,20 +161,29 @@ impl TryFrom for Prefs { } } -fn serialise_jid_list(name: &str, jids: Vec) -> Option { +fn serialise_jid_list(name: &str, jids: Vec) -> ::std::option::IntoIter { if jids.is_empty() { - None + None.into_iter() } else { Some( Element::builder(name) .ns(ns::MAM) - .append( + .append_all( jids.into_iter() - .map(|jid| Element::builder("jid").ns(ns::MAM).append(jid).build()) - .collect::>(), + .map(|jid| + Node::Element( + Element::builder("jid") + .ns(ns::MAM) + .append(Node::Text(String::from(jid))) + .build() + .into() + ) + ) + .into_iter(), ) - .build(), - ) + .build() + .into(), + ).into_iter() } } @@ -183,8 +192,8 @@ impl From for Element { Element::builder("prefs") .ns(ns::MAM) .attr("default", prefs.default_) - .append(serialise_jid_list("always", prefs.always)) - .append(serialise_jid_list("never", prefs.never)) + .append_all(serialise_jid_list("always", prefs.always)) + .append_all(serialise_jid_list("never", prefs.never)) .build() } } diff --git a/src/message.rs b/src/message.rs index f9bae2b16ee3351555b469534481f18f79324a14..4a78c6c0b7794e1cda6738af6739718031acd9e0 100644 --- a/src/message.rs +++ b/src/message.rs @@ -212,7 +212,7 @@ impl From for Element { .attr("to", message.to) .attr("id", message.id) .attr("type", message.type_) - .append( + .append_all( message .subjects .into_iter() @@ -227,9 +227,8 @@ impl From for Element { ); subject }) - .collect::>(), ) - .append( + .append_all( message .bodies .into_iter() @@ -244,9 +243,8 @@ impl From for Element { ); body }) - .collect::>(), ) - .append(message.payloads) + .append_all(message.payloads.into_iter()) .build() } } diff --git a/src/presence.rs b/src/presence.rs index ffb734c596be6d0b7575f85e2079b51693108cf2..4aad1622941265e2d93a7994e83aaf1f99c6da5c 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -8,7 +8,7 @@ use crate::util::error::Error; use crate::ns; use jid::Jid; -use minidom::{Element, ElementEmitter, IntoAttributeValue, IntoElements}; +use minidom::{Element, IntoAttributeValue, Node}; use std::collections::BTreeMap; use std::str::FromStr; use std::convert::TryFrom; @@ -48,18 +48,17 @@ impl FromStr for Show { } } -impl IntoElements for Show { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child( - Element::builder("show") - .append(match self { - Show::Away => Some("away"), - Show::Chat => Some("chat"), - Show::Dnd => Some("dnd"), - Show::Xa => Some("xa"), - }) - .build(), - ) +impl Into for Show { + fn into(self) -> Node { + Element::builder("show") + .append(match self { + Show::Away => "away", + Show::Chat => "chat", + Show::Dnd => "dnd", + Show::Xa => "xa", + }) + .build() + .into() } } @@ -310,8 +309,8 @@ impl From for Element { .attr("to", presence.to) .attr("id", presence.id) .attr("type", presence.type_) - .append(presence.show) - .append( + .append_all(presence.show.into_iter()) + .append_all( presence .statuses .into_iter() @@ -327,9 +326,8 @@ impl From for Element { .append(status) .build() }) - .collect::>(), ) - .append(if presence.priority == 0 { + .append_all((if presence.priority == 0 { None } else { Some( @@ -337,8 +335,8 @@ impl From for Element { .append(format!("{}", presence.priority)) .build() ) - }) - .append(presence.payloads) + }).into_iter()) + .append_all(presence.payloads.into_iter()) .build() } } diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 5d65eb783d9411505b26065f8aee07c25e476605..9327c50bc7d2d8c57adfdea09e68ed382e3aa1c6 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -10,7 +10,7 @@ use crate::util::error::Error; use crate::ns; use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId, Item as PubSubItem}; use jid::Jid; -use minidom::Element; +use minidom::{Element, Node}; use std::convert::TryFrom; /// Event wrapper for a PubSub ``. @@ -198,12 +198,12 @@ impl From for Element { PubSubEvent::Configuration { node, form } => Element::builder("configuration") .ns(ns::PUBSUB_EVENT) .attr("node", node) - .append(form) + .append_all(form.map(Element::from).map(Node::Element).into_iter()) .build(), PubSubEvent::Delete { node, redirect } => Element::builder("purge") .ns(ns::PUBSUB_EVENT) .attr("node", node) - .append(redirect.map(|redirect| { + .append_all(redirect.map(|redirect| { Element::builder("redirect") .ns(ns::PUBSUB_EVENT) .attr("uri", redirect) @@ -213,12 +213,12 @@ impl From for Element { PubSubEvent::PublishedItems { node, items } => Element::builder("items") .ns(ns::PUBSUB_EVENT) .attr("node", node) - .append(items) + .append_all(items.into_iter()) .build(), PubSubEvent::RetractedItems { node, items } => Element::builder("items") .ns(ns::PUBSUB_EVENT) .attr("node", node) - .append( + .append_all( items .into_iter() .map(|id| { @@ -227,7 +227,6 @@ impl From for Element { .attr("id", id) .build() }) - .collect::>(), ) .build(), PubSubEvent::Purge { node } => Element::builder("purge") diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index 0e166bd3ebcf86cd17e2327f864e5f53a0ba670f..c49d5fbfd19101e72a62305af9fffadac9557d29 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -219,11 +219,11 @@ impl From for Element { fn from(subscribe_options: SubscribeOptions) -> Element { Element::builder("subscribe-options") .ns(ns::PUBSUB) - .append(if subscribe_options.required { + .append_all((if subscribe_options.required { vec![Element::builder("required").ns(ns::PUBSUB).build()] } else { vec![] - }) + }).into_iter()) .build() } } @@ -473,7 +473,7 @@ impl From for Element { fn from(pubsub: PubSub) -> Element { Element::builder("pubsub") .ns(ns::PUBSUB) - .append(match pubsub { + .append_all((match pubsub { PubSub::Create { create, configure } => { let mut elems = vec![Element::from(create)]; if let Some(configure) = configure { @@ -498,7 +498,7 @@ impl From for Element { PubSub::Subscription(subscription) => vec![Element::from(subscription)], PubSub::Subscriptions(subscriptions) => vec![Element::from(subscriptions)], PubSub::Unsubscribe(unsubscribe) => vec![Element::from(unsubscribe)], - }) + }).into_iter()) .build() } } diff --git a/src/rsm.rs b/src/rsm.rs index a4f97365388da78a6e47e3ebc7555afd0a2623f2..691e5e9846d50095369a9f333fc1336d6edeb0ab 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -6,7 +6,7 @@ use crate::util::error::Error; use crate::ns; -use minidom::Element; +use minidom::{Element, Node}; use std::convert::TryFrom; /// Requests paging through a potentially big set of items (represented by an @@ -72,23 +72,23 @@ impl From for Element { fn from(set: SetQuery) -> Element { Element::builder("set") .ns(ns::RSM) - .append(set.max.map(|max| { + .append_all(set.max.map(|max| { Element::builder("max") .ns(ns::RSM) .append(format!("{}", max)) .build() })) - .append( + .append_all( set.after .map(|after| Element::builder("after").ns(ns::RSM).append(after).build()), ) - .append(set.before.map(|before| { + .append_all(set.before.map(|before| { Element::builder("before") .ns(ns::RSM) .append(before) .build() })) - .append(set.index.map(|index| { + .append_all(set.index.map(|index| { Element::builder("index") .ns(ns::RSM) .append(format!("{}", index)) @@ -162,12 +162,12 @@ impl From for Element { }); Element::builder("set") .ns(ns::RSM) - .append(first) - .append( + .append_all(first.map(Element::from).map(Node::Element).into_iter()) + .append_all( set.last .map(|last| Element::builder("last").ns(ns::RSM).append(last).build()), ) - .append(set.count.map(|count| { + .append_all(set.count.map(|count| { Element::builder("count") .ns(ns::RSM) .append(format!("{}", count)) diff --git a/src/sasl.rs b/src/sasl.rs index 3deed9e1aa6b0fface1dcd41319f8eafc96641c0..038e9e4f5a26e8cc159a65fee2c941ae14c3b292 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -203,7 +203,7 @@ impl From for Element { Element::builder("failure") .ns(ns::SASL) .append(failure.defined_condition) - .append( + .append_all( failure .texts .into_iter() @@ -214,7 +214,6 @@ impl From for Element { .append(text) .build() }) - .collect::>(), ) .build() } diff --git a/src/stanza_error.rs b/src/stanza_error.rs index db9230ebbddee84e8035c673291404c3365fa886..e871a12b79b459b2d0ffb925b9c0e4ece1c7acce 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -9,7 +9,7 @@ use crate::message::MessagePayload; use crate::ns; use crate::presence::PresencePayload; use jid::Jid; -use minidom::Element; +use minidom::{Element, Node}; use std::collections::BTreeMap; use std::convert::TryFrom; @@ -294,15 +294,16 @@ impl From for Element { .attr("type", err.type_) .attr("by", err.by) .append(err.defined_condition) - .append( + .append_all( err.texts.into_iter().map(|(lang, text)| { Element::builder("text") .ns(ns::XMPP_STANZAS) .attr("xml:lang", lang) .append(text) .build() - }).collect::>()) - .append(err.other) + }) + ) + .append_all(err.other.map(Element::from).map(Node::Element).into_iter()) .build() } } diff --git a/src/tune.rs b/src/tune.rs index 1bbb436a618f6c8c9c45fb5393f363fd47863bac..df9b6679865114fffb7c652eb93d28666c3f78b2 100644 --- a/src/tune.rs +++ b/src/tune.rs @@ -149,13 +149,13 @@ impl From for Element { fn from(tune: Tune) -> Element { Element::builder("tune") .ns(ns::TUNE) - .append(tune.artist) - .append(tune.length) - .append(tune.rating) - .append(tune.source) - .append(tune.title) - .append(tune.track) - .append(tune.uri) + .append_all(tune.artist) + .append_all(tune.length) + .append_all(tune.rating) + .append_all(tune.source) + .append_all(tune.title) + .append_all(tune.track) + .append_all(tune.uri) .build() } } diff --git a/src/util/helpers.rs b/src/util/helpers.rs index b7bdc8443cbaf4cbf1f5d3f3f6b055bd7ef16550..b51b18a9c8ee6259b450fb5169eeff90c2badf33 100644 --- a/src/util/helpers.rs +++ b/src/util/helpers.rs @@ -34,8 +34,8 @@ impl TrimmedPlainText { }) } - pub fn encode(string: &str) -> String { - string.to_owned() + pub fn encode(string: &str) -> Option { + Some(string.to_owned()) } } diff --git a/src/util/macros.rs b/src/util/macros.rs index c4b5c4e0b85d75ac68624821b57bbe55ebd808ef..6843369d12dfc49b9809108ce72ba3e6c3bc75b3 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -253,6 +253,11 @@ macro_rules! generate_element_enum { .build() } } + impl From<$elem> for ::minidom::Node { + fn from(elem: $elem) -> ::minidom::Node { + ::minidom::Node::Element(elem.into()) + } + } ); } @@ -291,6 +296,11 @@ macro_rules! generate_attribute_enum { .build() } } + impl From<$elem> for ::minidom::Node { + fn from(elem: $elem) -> ::minidom::Node { + ::minidom::Node::Element(elem.into()) + } + } ); } @@ -385,6 +395,12 @@ macro_rules! generate_empty_element { .build() } } + + impl From<$elem> for ::minidom::Node { + fn from(elem: $elem) -> ::minidom::Node { + ::minidom::Node::Element(elem.into()) + } + } ); } @@ -441,6 +457,12 @@ macro_rules! generate_elem_id { .build() } } + + impl From<$elem> for ::minidom::Node { + fn from(elem: $elem) -> ::minidom::Node { + ::minidom::Node::Element(elem.into()) + } + } ); } @@ -547,22 +569,38 @@ macro_rules! finish_parse_elem { } macro_rules! generate_serialiser { - ($parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => { - ::minidom::Element::builder($name) - .ns(crate::ns::$ns) - .append($parent.$elem) - .build() - }; - ($parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => { - $parent.$elem.map(|elem| { + ($builder:ident, $parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => { + $builder.append(::minidom::Node::Element( ::minidom::Element::builder($name) .ns(crate::ns::$ns) - .append(elem) + .append(::minidom::Node::Text($parent.$elem)) .build() - }) + ) + ) }; - ($parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => { - $parent.$elem + ($builder:ident, $parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => { + $builder.append_all($parent.$elem.map(|elem| { + ::minidom::Element::builder($name) + .ns(crate::ns::$ns) + .append(::minidom::Node::Text(elem)) + .build() + }).into_iter() + ) + }; + ($builder:ident, $parent:ident, $elem:ident, Option, $constructor:ident, ($name:tt, $ns:ident)) => { + $builder.append_all($parent.$elem.map(|elem| { + ::minidom::Element::builder($name) + .ns(crate::ns::$ns) + .append(::minidom::Node::Element(::minidom::Element::from(elem))) + .build() + }).into_iter() + ) + }; + ($builder:ident, $parent:ident, $elem:ident, Vec, $constructor:ident, ($name:tt, $ns:ident)) => { + $builder.append_all($parent.$elem.into_iter()) + }; + ($builder:ident, $parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => { + $builder.append(::minidom::Node::Element(::minidom::Element::from($parent.$elem))) }; } @@ -637,18 +675,25 @@ macro_rules! generate_element { impl From<$elem> for ::minidom::Element { fn from(elem: $elem) -> ::minidom::Element { - ::minidom::Element::builder($name) - .ns(crate::ns::$ns) - $( - .attr($attr_name, elem.$attr) - )* - $( - .append(generate_serialiser!(elem, $child_ident, $coucou, $child_constructor, ($child_name, $child_ns))) - )* - $( - .append($codec::encode(&elem.$text_ident)) - )* - .build() + let mut builder = ::minidom::Element::builder($name) + .ns(crate::ns::$ns); + $( + builder = builder.attr($attr_name, elem.$attr); + )* + $( + builder = generate_serialiser!(builder, elem, $child_ident, $coucou, $child_constructor, ($child_name, $child_ns)); + )* + $( + builder = builder.append_all($codec::encode(&elem.$text_ident).map(::minidom::Node::Text).into_iter()); + )* + + builder.build() + } + } + + impl From<$elem> for ::minidom::Node { + fn from(elem: $elem) -> ::minidom::Node { + ::minidom::Node::Element(elem.into()) } } ); @@ -691,11 +736,17 @@ macro_rules! impl_pubsub_item { .ns(ns::$ns) .attr("id", item.0.id) .attr("publisher", item.0.publisher) - .append(item.0.payload) + .append_all(item.0.payload.map(::minidom::Node::Element).into_iter()) .build() } } + impl From<$item> for ::minidom::Node { + fn from(item: $item) -> ::minidom::Node { + ::minidom::Node::Element(item.into()) + } + } + impl ::std::ops::Deref for $item { type Target = crate::pubsub::Item; diff --git a/src/xhtml.rs b/src/xhtml.rs index 9c43181bf06fc38e88568b101b6971a6ae466562..0216e7c9e72fe518af5b60e53b19c34dca278157 100644 --- a/src/xhtml.rs +++ b/src/xhtml.rs @@ -98,14 +98,14 @@ impl From for Element { fn from(wrapper: XhtmlIm) -> Element { Element::builder("html") .ns(ns::XHTML_IM) - .append(wrapper.bodies.into_iter().map(|(ref lang, ref body)| { + .append_all(wrapper.bodies.into_iter().map(|(ref lang, ref body)| { if lang.is_empty() { assert!(body.xml_lang.is_none()); } else { assert_eq!(Some(lang), body.xml_lang.as_ref()); } Element::from(body.clone()) - }).collect::>()) + })) .build() } } @@ -174,7 +174,7 @@ impl From for Element { .ns(ns::XHTML) .attr("style", get_style_string(body.style)) .attr("xml:lang", body.xml_lang) - .append(children_to_nodes(body.children)) + .append_all(children_to_nodes(body.children)) .build() } } @@ -338,7 +338,7 @@ impl From for Element { }; let mut builder = Element::builder(name) .ns(ns::XHTML) - .append(children_to_nodes(children)); + .append_all(children_to_nodes(children)); for (key, value) in attrs { builder = builder.attr(key, value); } From 6f1fc7b009ef22083790d1c786dbd633d8f41ab4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 6 Sep 2019 11:45:04 +0200 Subject: [PATCH 657/698] bind: Easily convert BindResponse into FullJid or Jid. --- src/bind.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/bind.rs b/src/bind.rs index 17e1e1240ac5ac9895bbd08250f6ae92a4c85e69..22beccd19b8e2c54a5e9e3b73cb7fb4e9e570bda 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -7,7 +7,7 @@ use crate::util::error::Error; use crate::iq::{IqResultPayload, IqSetPayload}; use crate::ns; -use jid::FullJid; +use jid::{FullJid, Jid}; use minidom::Element; use std::str::FromStr; use std::convert::TryFrom; @@ -85,6 +85,18 @@ pub struct BindResponse { impl IqResultPayload for BindResponse {} +impl From for FullJid { + fn from(bind: BindResponse) -> FullJid { + bind.jid + } +} + +impl From for Jid { + fn from(bind: BindResponse) -> Jid { + Jid::Full(bind.jid) + } +} + impl TryFrom for BindResponse { type Error = Error; From 188de32dacd263746de32ed65eceb9c8d0d75101 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 6 Sep 2019 11:45:46 +0200 Subject: [PATCH 658/698] Prepare for the 0.15.0 release. --- Cargo.toml | 2 +- ChangeLog | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6690ccf66ba81a972eb767e3cb620e3e0d61707f..91f5fccc380bd1158005d22307f52d812fd9ca07 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.14.0" +version = "0.15.0" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", diff --git a/ChangeLog b/ChangeLog index 5ecea28ef465af043d041b95750d673ead917e6a..05db5edadd5153f0d5d2abf4558786ec9fee03bd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,5 @@ -Version NEXT: -DATE Emmanuel Gil Peyrot +Version 0.15.0: +2019-09-06 Emmanuel Gil Peyrot * New parsers/serialisers: - XHTML-IM (XEP-0071) - User Tune (XEP-0118) @@ -11,6 +11,7 @@ DATE Emmanuel Gil Peyrot - Bind has been split into BindQuery and BindResponse. * Improvements: - New DOAP file for a machine-readable description of the features. + - Add various parser and formatter helpers on Hash. Version 0.14.0: 2019-07-13 Emmanuel Gil Peyrot , Maxime “pep” Buquet From a5011c59adc00150dd86553e710c468b98ac5ae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sat, 30 Mar 2019 18:47:07 +0100 Subject: [PATCH 659/698] Add structs for OpenPGP for XMPP (XEP-0373). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- ChangeLog | 5 +++ src/lib.rs | 3 ++ src/ns.rs | 5 +++ src/openpgp.rs | 104 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 117 insertions(+) create mode 100644 src/openpgp.rs diff --git a/ChangeLog b/ChangeLog index 05db5edadd5153f0d5d2abf4558786ec9fee03bd..ed5aae07e7aadf06c1a45153c68a9cf158fdd112 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Version NEXT: +DATE Emmanuel Gil Peyrot + * New parsers/serialisers: + - OpenPGP for XMPP (XEP-0373) + Version 0.15.0: 2019-09-06 Emmanuel Gil Peyrot * New parsers/serialisers: diff --git a/src/lib.rs b/src/lib.rs index d989812348b2a9dd141974da0b672eec97e19ead..ea09f2f0ea5e50e06f2a7059d98dbe7abb7263e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -183,6 +183,9 @@ pub mod jingle_message; /// XEP-0359: Unique and Stable Stanza IDs pub mod stanza_id; +/// XEP-0373: OpenPGP for XMPP +pub mod openpgp; + /// XEP-0380: Explicit Message Encryption pub mod eme; diff --git a/src/ns.rs b/src/ns.rs index 2c932c3ced7c9e8e844aa7a362f1541f2f021008..e5242b25667a70312c2e5b4f405c4efb6796a18a 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -188,6 +188,11 @@ pub const JINGLE_MESSAGE: &str = "urn:xmpp:jingle-message:0"; /// XEP-0359: Unique and Stable Stanza IDs pub const SID: &str = "urn:xmpp:sid:0"; +/// XEP-0373: OpenPGP for XMPP +pub const OX: &str = "urn:xmpp:openpgp:0"; +/// XEP-0373: OpenPGP for XMPP +pub const OX_PUBKEYS: &str = "urn:xmpp:openpgp:0:public-keys"; + /// XEP-0380: Explicit Message Encryption pub const EME: &str = "urn:xmpp:eme:0"; diff --git a/src/openpgp.rs b/src/openpgp.rs new file mode 100644 index 0000000000000000000000000000000000000000..86f4829a77fa8af1fae6448a3aa27b3635418e6b --- /dev/null +++ b/src/openpgp.rs @@ -0,0 +1,104 @@ +// Copyright (c) 2019 Maxime “pep” Buquet +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use crate::date::DateTime; +use crate::util::helpers::Base64; +use crate::pubsub::PubSubPayload; + +// TODO: Merge this container with the PubKey struct +generate_element!( + /// Data contained in the PubKey element + PubKeyData, "data", OX, + text: ( + /// Base64 data + data: Base64> + ) +); + +generate_element!( + /// Pubkey element to be used in PubSub publish payloads. + PubKey, "pubkey", OX, + attributes: [ + /// Last updated date + date: Option = "date" + ], + children: [ + /// Public key as base64 data + data: Required = ("data", OX) => PubKeyData + ] +); + +impl PubSubPayload for PubKey {} + +generate_element!( + /// Public key metadata + PubKeyMeta, "pubkey-metadata", OX, + attributes: [ + /// OpenPGP v4 fingerprint + v4fingerprint: Required = "v4-fingerprint", + /// Time the key was published or updated + date: Required = "date", + ] +); + +generate_element!( + /// List of public key metadata + PubKeysMeta, "public-key-list", OX, + children: [ + /// Public keys + pubkeys: Vec = ("pubkey-metadata", OX) => PubKeyMeta + ] +); + +impl PubSubPayload for PubKeysMeta {} + +#[cfg(test)] +mod tests { + use super::*; + use crate::ns; + use std::str::FromStr; + use crate::pubsub::{NodeName, Item, pubsub::{Item as PubSubItem, Publish}}; + + #[test] + fn pubsub_publish_pubkey_data() { + let pubkey = PubKey { + date: None, + data: PubKeyData { + data: (&"Foo").as_bytes().to_vec(), + } + }; + println!("Foo1: {:?}", pubkey); + + let pubsub = Publish { + node: NodeName(format!("{}:{}", ns::OX_PUBKEYS, "some-fingerprint")), + items: vec![ + PubSubItem(Item::new(None, None, Some(pubkey))), + ], + }; + println!("Foo2: {:?}", pubsub); + } + + #[test] + fn pubsub_publish_pubkey_meta() { + let pubkeymeta = PubKeysMeta { + pubkeys: vec![ + PubKeyMeta { + v4fingerprint: "some-fingerprint".to_owned(), + date: DateTime::from_str("2019-03-30T18:30:25Z").unwrap(), + }, + ], + }; + println!("Foo1: {:?}", pubkeymeta); + + let pubsub = Publish { + node: NodeName("foo".to_owned()), + items: vec![ + PubSubItem(Item::new(None, None, Some(pubkeymeta))), + ], + }; + println!("Foo2: {:?}", pubsub); + } +} From ecd9502f58430e220327a6ec138b6e730ef3eca6 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 6 Sep 2019 13:35:40 +0200 Subject: [PATCH 660/698] doap: Update for the latest release. --- doap.xml | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/doap.xml b/doap.xml index 561f49dd133eef7655c5c2fbdbce8fda19175db2..b8099544b4c1e24be4d768e91b6718133d7fd96c 100644 --- a/doap.xml +++ b/doap.xml @@ -116,7 +116,7 @@ complete 1.5.4 - NEXT + 0.15.0 @@ -188,7 +188,7 @@ complete 1.2 - NEXT + 0.15.0 @@ -300,7 +300,7 @@ complete 1.0 - NEXT + 0.15.0 @@ -341,7 +341,7 @@ complete 0.13.0 - NEXT + 0.15.0 @@ -425,6 +425,13 @@ + + + 0.15.0 + 2019-09-06 + + + 0.14.0 @@ -432,7 +439,6 @@ - 0.13.1 From d3157c77f00bfdb92416de735ed1168dbcf46be1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 6 Sep 2019 13:37:27 +0200 Subject: [PATCH 661/698] doap: Add XEP-0373 there. --- doap.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doap.xml b/doap.xml index b8099544b4c1e24be4d768e91b6718133d7fd96c..b05c4c42b3e6f2d14940f2e386d0037c014538c2 100644 --- a/doap.xml +++ b/doap.xml @@ -408,6 +408,14 @@ 0.1.0 + + + + partial + 0.4.0 + NEXT + + From cdf80c897993a1ce0e1f959a01c09a01aae21df6 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 6 Sep 2019 15:00:14 +0200 Subject: [PATCH 662/698] presence: Simplify constructors. --- ChangeLog | 2 ++ src/presence.rs | 40 ++++++++++++++++++++++++++++++++++------ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index ed5aae07e7aadf06c1a45153c68a9cf158fdd112..372ebddef25d66a6bec783f258542a143e58123f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,8 @@ Version NEXT: DATE Emmanuel Gil Peyrot * New parsers/serialisers: - OpenPGP for XMPP (XEP-0373) + * Breaking changes: + - Presence constructors now take Into and assume Some. Version 0.15.0: 2019-09-06 Emmanuel Gil Peyrot diff --git a/src/presence.rs b/src/presence.rs index 4aad1622941265e2d93a7994e83aaf1f99c6da5c..14ae559c492d23a5b24f3fe4b155cf42b6491d31 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -194,21 +194,21 @@ impl Presence { /// Set the emitter of this presence, this should only be useful for /// servers and components, as clients can only send presences from their /// own resource (which is implicit). - pub fn with_from(mut self, from: Option) -> Presence { - self.from = from; + pub fn with_from>(mut self, from: J) -> Presence { + self.from = Some(from.into()); self } /// Set the recipient of this presence, this is only useful for directed /// presences. - pub fn with_to(mut self, to: Option) -> Presence { - self.to = to; + pub fn with_to>(mut self, to: J) -> Presence { + self.to = Some(to.into()); self } /// Set the identifier for this presence. - pub fn with_id(mut self, id: Option) -> Presence { - self.id = id; + pub fn with_id(mut self, id: String) -> Presence { + self.id = Some(id); self } @@ -345,6 +345,7 @@ impl From for Element { mod tests { use super::*; use crate::util::compare_elements::NamespaceAwareCompare; + use jid::{BareJid, FullJid}; #[cfg(target_pointer_width = "32")] #[test] @@ -634,4 +635,31 @@ mod tests { assert!(priority.is("priority", ns::DEFAULT_NS)); assert_eq!(priority.text(), "42"); } + + #[test] + fn presence_with_to() { + let presence = Presence::new(Type::None); + let elem: Element = presence.into(); + assert_eq!(elem.attr("to"), None); + + let presence = Presence::new(Type::None) + .with_to(Jid::Bare(BareJid::domain("localhost"))); + let elem: Element = presence.into(); + assert_eq!(elem.attr("to"), Some("localhost")); + + let presence = Presence::new(Type::None) + .with_to(BareJid::domain("localhost")); + let elem: Element = presence.into(); + assert_eq!(elem.attr("to"), Some("localhost")); + + let presence = Presence::new(Type::None) + .with_to(Jid::Full(FullJid::new("test", "localhost", "coucou"))); + let elem: Element = presence.into(); + assert_eq!(elem.attr("to"), Some("test@localhost/coucou")); + + let presence = Presence::new(Type::None) + .with_to(FullJid::new("test", "localhost", "coucou")); + let elem: Element = presence.into(); + assert_eq!(elem.attr("to"), Some("test@localhost/coucou")); + } } From ff77f6141fa008b1890528a08d9c3209e162869d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 6 Sep 2019 16:03:58 +0200 Subject: [PATCH 663/698] Cleanup a1ae45add88e424710073711935a3605cafe9ef9 a bit. --- src/bind.rs | 11 +++--- src/blocking.rs | 1 - src/data_forms.rs | 29 +++++++++------- src/disco.rs | 4 +-- src/ibr.rs | 12 +++---- src/jingle.rs | 10 +++--- src/jingle_ft.rs | 80 ++++++++++++-------------------------------- src/jingle_s5b.rs | 17 ++++------ src/mam.rs | 14 ++------ src/presence.rs | 12 +++---- src/pubsub/event.rs | 22 ++++-------- src/pubsub/pubsub.rs | 12 +++---- src/rsm.rs | 13 +++---- src/sasl.rs | 1 - src/stanza_error.rs | 5 ++- src/time.rs | 6 ++-- src/util/macros.rs | 12 +++---- src/xhtml.rs | 10 +++--- 18 files changed, 97 insertions(+), 174 deletions(-) diff --git a/src/bind.rs b/src/bind.rs index 22beccd19b8e2c54a5e9e3b73cb7fb4e9e570bda..16fb487bc7ef00a931d3bcd16a736589c19ec50c 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -63,13 +63,10 @@ impl From for Element { fn from(bind: BindQuery) -> Element { Element::builder("bind") .ns(ns::BIND) - .append_all((match bind.resource { - None => vec![], - Some(resource) => vec![Element::builder("resource") + .append_all(bind.resource.map(|resource| + Element::builder("resource") .ns(ns::BIND) - .append(resource) - .build()], - }).into_iter()) + .append(resource))) .build() } } @@ -129,7 +126,7 @@ impl From for Element { fn from(bind: BindResponse) -> Element { Element::builder("bind") .ns(ns::BIND) - .append(Element::builder("jid").ns(ns::BIND).append(bind.jid).build()) + .append(Element::builder("jid").ns(ns::BIND).append(bind.jid)) .build() } } diff --git a/src/blocking.rs b/src/blocking.rs index f9e6a821bc3769891a80da0404e1d6bf5ef86ceb..741d43405412864166729d53f17e0d210ba5cfe6 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -55,7 +55,6 @@ macro_rules! generate_blocking_element { Element::builder("item") .ns(ns::BLOCKING) .attr("jid", jid) - .build() })) .build() } diff --git a/src/data_forms.rs b/src/data_forms.rs index f252bb714f5b82efb9ee1417c426d5f3a8b63771..80c9131b32da9798eba8ce64d57505ab9500b5db 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -7,7 +7,7 @@ use crate::util::error::Error; use crate::media_element::MediaElement; use crate::ns; -use minidom::{Element, Node}; +use minidom::Element; use std::convert::TryFrom; generate_element!( @@ -152,12 +152,12 @@ impl From for Element { .attr("var", field.var) .attr("type", field.type_) .attr("label", field.label) - .append_all((if field.required { - Some(Element::builder("required").ns(ns::DATA_FORMS).build()) + .append_all(if field.required { + Some(Element::builder("required").ns(ns::DATA_FORMS)) } else { None - }).into_iter()) - .append_all(field.options.iter().cloned().map(Element::from).map(Node::Element).into_iter()) + }) + .append_all(field.options.iter().cloned().map(Element::from)) .append_all( field .values @@ -166,10 +166,9 @@ impl From for Element { Element::builder("value") .ns(ns::DATA_FORMS) .append(value) - .build() }) ) - .append_all(field.media.iter().cloned().map(Element::from).map(Node::Element)) + .append_all(field.media.iter().cloned().map(Element::from)) .build() } } @@ -283,12 +282,16 @@ impl From for Element { .ns(ns::DATA_FORMS) .append(text) })) - .append_all((if let Some(form_type) = form.form_type { - vec![Element::builder("field").ns(ns::DATA_FORMS).attr("var", "FORM_TYPE").attr("type", "hidden").append(Element::builder("value").ns(ns::DATA_FORMS).append(form_type).build()).build()] - } else { - vec![] - }).into_iter()) - .append_all(form.fields.iter().cloned().map(Element::from).map(Node::Element)) + .append_all(form.form_type.map(|form_type| { + Element::builder("field") + .ns(ns::DATA_FORMS) + .attr("var", "FORM_TYPE") + .attr("type", "hidden") + .append(Element::builder("value") + .ns(ns::DATA_FORMS) + .append(form_type)) + })) + .append_all(form.fields.iter().cloned().map(Element::from)) .build() } } diff --git a/src/disco.rs b/src/disco.rs index 3c2cde13835203e97c070c52d00c67ed8e43a87b..857afaf8c758de96eac503d50249a572475375f5 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -9,7 +9,7 @@ use crate::util::error::Error; use crate::iq::{IqGetPayload, IqResultPayload}; use crate::ns; use jid::Jid; -use minidom::{Element, Node}; +use minidom::Element; use std::convert::TryFrom; generate_element!( @@ -180,7 +180,7 @@ impl From for Element { .attr("node", disco.node) .append_all(disco.identities.into_iter()) .append_all(disco.features.into_iter()) - .append_all(disco.extensions.iter().cloned().map(Element::from).map(Node::Element)) + .append_all(disco.extensions.iter().cloned().map(Element::from)) .build() } } diff --git a/src/ibr.rs b/src/ibr.rs index d539f2adce83fa4cdf8fe13ba650ba0be5f277af..8ced963f259e0c27375efbd4875b7e38d23b354a 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -8,7 +8,7 @@ use crate::data_forms::DataForm; use crate::util::error::Error; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; -use minidom::{Element, Node}; +use minidom::Element; use std::collections::HashMap; use std::convert::TryFrom; @@ -93,23 +93,23 @@ impl From for Element { fn from(query: Query) -> Element { Element::builder("query") .ns(ns::REGISTER) - .append_all((if query.registered { + .append_all(if query.registered { Some(Element::builder("registered").ns(ns::REGISTER)) } else { None - }).into_iter()) + }) .append_all( query .fields .into_iter() .map(|(name, value)| Element::builder(name).ns(ns::REGISTER).append(value)) ) - .append_all((if query.remove { + .append_all(if query.remove { Some(Element::builder("remove").ns(ns::REGISTER)) } else { None - }).into_iter()) - .append_all(query.form.map(Element::from).map(Node::Element).into_iter()) + }) + .append_all(query.form.map(Element::from)) .build() } } diff --git a/src/jingle.rs b/src/jingle.rs index 55a493ef407fa7785be7d94125cf2d4fcb55ea80..4a8e5ac357b577e88861aeb5db7535db0a3b67fe 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -8,7 +8,7 @@ use crate::util::error::Error; use crate::iq::IqSetPayload; use crate::ns; use jid::Jid; -use minidom::{Element, Node}; +use minidom::Element; use std::collections::BTreeMap; use std::str::FromStr; use std::convert::TryFrom; @@ -351,7 +351,8 @@ impl From for Element { Reason::UnsupportedApplications => "unsupported-applications", Reason::UnsupportedTransports => "unsupported-transports", }) - .build() + .ns(ns::JINGLE) + .build() } } @@ -419,7 +420,6 @@ impl From for Element { .ns(ns::JINGLE) .attr("xml:lang", lang) .append(text) - .build() })) .build() } @@ -542,8 +542,8 @@ impl From for Element { .attr("initiator", jingle.initiator) .attr("responder", jingle.responder) .attr("sid", jingle.sid) - .append_all(jingle.contents.into_iter()) - .append_all(jingle.reason.map(Element::from).map(Node::Element).into_iter()) + .append_all(jingle.contents) + .append_all(jingle.reason.map(Element::from)) .build() } } diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index b3da6f8b9661b027d46e5fa0002a5f28672f98db..9902fed773142f24231dc0f076ae4d9fde4aa821 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -193,65 +193,27 @@ impl TryFrom for File { impl From for Element { fn from(file: File) -> Element { - let mut root = Element::builder("file").ns(ns::JINGLE_FT); - if let Some(date) = file.date { - root = root.append( - Node::Element( - Element::builder("date") - .ns(ns::JINGLE_FT) - .append(date) - .build() - ) - ); - } - if let Some(media_type) = file.media_type { - root = root.append( - Node::Element( - Element::builder("media-type") - .ns(ns::JINGLE_FT) - .append(media_type) - .build() - ) - ); - } - if let Some(name) = file.name { - root = root.append( - Node::Element( - Element::builder("name") - .ns(ns::JINGLE_FT) - .append(name) - .build() - ) - ); - } - for (lang, desc) in file.descs.into_iter() { - root = root.append( - Node::Element( - Element::builder("desc") - .ns(ns::JINGLE_FT) - .attr("xml:lang", lang) - .append(desc.0) - .build() - ) - ); - } - if let Some(size) = file.size { - root = root.append( - Node::Element( - Element::builder("size") - .ns(ns::JINGLE_FT) - .append(format!("{}", size)) - .build() - ) - ); - } - if let Some(range) = file.range { - root = root.append(range); - } - for hash in file.hashes { - root = root.append(hash); - } - root.build() + Element::builder("file") + .ns(ns::JINGLE_FT) + .append_all(file.date.map(|date| + Element::builder("date") + .append(date))) + .append_all(file.media_type.map(|media_type| + Element::builder("media-type") + .append(media_type))) + .append_all(file.name.map(|name| + Element::builder("name") + .append(name))) + .append_all(file.descs.into_iter().map(|(lang, desc)| + Element::builder("desc") + .attr("xml:lang", lang) + .append(desc.0))) + .append_all(file.size.map(|size| + Element::builder("size") + .append(format!("{}", size)))) + .append_all(file.range) + .append_all(file.hashes) + .build() } } diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 0f246691c40c1c065c252ffe473113f3bd01b659..17c5d996fb3bb7070cf8f3bf5d442d310883deb4 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -251,27 +251,22 @@ impl From for Element { TransportPayload::Candidates(candidates) => candidates .into_iter() .map(Element::from) - .collect::>() - .into_iter(), + .collect::>(), TransportPayload::Activated(cid) => vec![Element::builder("activated") .ns(ns::JINGLE_S5B) .attr("cid", cid) - .build()] - .into_iter(), + .build()], TransportPayload::CandidateError => vec![Element::builder("candidate-error") .ns(ns::JINGLE_S5B) - .build()] - .into_iter(), + .build()], TransportPayload::CandidateUsed(cid) => vec![Element::builder("candidate-used") .ns(ns::JINGLE_S5B) .attr("cid", cid) - .build()] - .into_iter(), + .build()], TransportPayload::ProxyError => vec![Element::builder("proxy-error") .ns(ns::JINGLE_S5B) - .build()] - .into_iter(), - TransportPayload::None => vec![].into_iter(), + .build()], + TransportPayload::None => vec![], }) .build() } diff --git a/src/mam.rs b/src/mam.rs index edc0116c4269dcdf57655c761e306d814496e39e..28362bf917c7081d49c28757434af018a485c9b6 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -171,17 +171,9 @@ fn serialise_jid_list(name: &str, jids: Vec) -> ::std::option::IntoIter for Element { }, ) .append(status) - .build() }) ) - .append_all((if presence.priority == 0 { + .append_all(if presence.priority == 0 { None } else { - Some( - Element::builder("priority") - .append(format!("{}", presence.priority)) - .build() - ) - }).into_iter()) + Some(Element::builder("priority") + .append(format!("{}", presence.priority))) + }) .append_all(presence.payloads.into_iter()) .build() } diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 9327c50bc7d2d8c57adfdea09e68ed382e3aa1c6..30c118de688830d9b8924de15c7a97a42c7274b0 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -10,7 +10,7 @@ use crate::util::error::Error; use crate::ns; use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId, Item as PubSubItem}; use jid::Jid; -use minidom::{Element, Node}; +use minidom::Element; use std::convert::TryFrom; /// Event wrapper for a PubSub ``. @@ -198,8 +198,7 @@ impl From for Element { PubSubEvent::Configuration { node, form } => Element::builder("configuration") .ns(ns::PUBSUB_EVENT) .attr("node", node) - .append_all(form.map(Element::from).map(Node::Element).into_iter()) - .build(), + .append_all(form.map(Element::from)), PubSubEvent::Delete { node, redirect } => Element::builder("purge") .ns(ns::PUBSUB_EVENT) .attr("node", node) @@ -207,14 +206,11 @@ impl From for Element { Element::builder("redirect") .ns(ns::PUBSUB_EVENT) .attr("uri", redirect) - .build() - })) - .build(), + })), PubSubEvent::PublishedItems { node, items } => Element::builder("items") .ns(ns::PUBSUB_EVENT) .attr("node", node) - .append_all(items.into_iter()) - .build(), + .append_all(items.into_iter()), PubSubEvent::RetractedItems { node, items } => Element::builder("items") .ns(ns::PUBSUB_EVENT) .attr("node", node) @@ -225,14 +221,11 @@ impl From for Element { Element::builder("retract") .ns(ns::PUBSUB_EVENT) .attr("id", id) - .build() }) - ) - .build(), + ), PubSubEvent::Purge { node } => Element::builder("purge") .ns(ns::PUBSUB_EVENT) - .attr("node", node) - .build(), + .attr("node", node), PubSubEvent::Subscription { node, expiry, @@ -245,8 +238,7 @@ impl From for Element { .attr("expiry", expiry) .attr("jid", jid) .attr("subid", subid) - .attr("subscription", subscription) - .build(), + .attr("subscription", subscription), }; Element::builder("event") .ns(ns::PUBSUB_EVENT) diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index c49d5fbfd19101e72a62305af9fffadac9557d29..727ef2b882d5c86de46ec215e82127a317b029fc 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -219,11 +219,11 @@ impl From for Element { fn from(subscribe_options: SubscribeOptions) -> Element { Element::builder("subscribe-options") .ns(ns::PUBSUB) - .append_all((if subscribe_options.required { - vec![Element::builder("required").ns(ns::PUBSUB).build()] + .append_all(if subscribe_options.required { + Some(Element::builder("required").ns(ns::PUBSUB)) } else { - vec![] - }).into_iter()) + None + }) .build() } } @@ -473,7 +473,7 @@ impl From for Element { fn from(pubsub: PubSub) -> Element { Element::builder("pubsub") .ns(ns::PUBSUB) - .append_all((match pubsub { + .append_all(match pubsub { PubSub::Create { create, configure } => { let mut elems = vec![Element::from(create)]; if let Some(configure) = configure { @@ -498,7 +498,7 @@ impl From for Element { PubSub::Subscription(subscription) => vec![Element::from(subscription)], PubSub::Subscriptions(subscriptions) => vec![Element::from(subscriptions)], PubSub::Unsubscribe(unsubscribe) => vec![Element::from(unsubscribe)], - }).into_iter()) + }) .build() } } diff --git a/src/rsm.rs b/src/rsm.rs index 691e5e9846d50095369a9f333fc1336d6edeb0ab..8299c9b67132b27d50e43d6df1222d5cc20facf3 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -6,7 +6,7 @@ use crate::util::error::Error; use crate::ns; -use minidom::{Element, Node}; +use minidom::Element; use std::convert::TryFrom; /// Requests paging through a potentially big set of items (represented by an @@ -76,23 +76,20 @@ impl From for Element { Element::builder("max") .ns(ns::RSM) .append(format!("{}", max)) - .build() })) .append_all( set.after - .map(|after| Element::builder("after").ns(ns::RSM).append(after).build()), + .map(|after| Element::builder("after").ns(ns::RSM).append(after)) ) .append_all(set.before.map(|before| { Element::builder("before") .ns(ns::RSM) .append(before) - .build() })) .append_all(set.index.map(|index| { Element::builder("index") .ns(ns::RSM) .append(format!("{}", index)) - .build() })) .build() } @@ -158,20 +155,18 @@ impl From for Element { .ns(ns::RSM) .attr("index", set.first_index) .append(first) - .build() }); Element::builder("set") .ns(ns::RSM) - .append_all(first.map(Element::from).map(Node::Element).into_iter()) + .append_all(first) .append_all( set.last - .map(|last| Element::builder("last").ns(ns::RSM).append(last).build()), + .map(|last| Element::builder("last").ns(ns::RSM).append(last)), ) .append_all(set.count.map(|count| { Element::builder("count") .ns(ns::RSM) .append(format!("{}", count)) - .build() })) .build() } diff --git a/src/sasl.rs b/src/sasl.rs index 038e9e4f5a26e8cc159a65fee2c941ae14c3b292..e8f3aa6e041727102c112b3ea28ddd9f0130837b 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -212,7 +212,6 @@ impl From for Element { .ns(ns::SASL) .attr("xml:lang", lang) .append(text) - .build() }) ) .build() diff --git a/src/stanza_error.rs b/src/stanza_error.rs index e871a12b79b459b2d0ffb925b9c0e4ece1c7acce..1f4dbbe16de934e405af539040c7231763341bb5 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -9,7 +9,7 @@ use crate::message::MessagePayload; use crate::ns; use crate::presence::PresencePayload; use jid::Jid; -use minidom::{Element, Node}; +use minidom::Element; use std::collections::BTreeMap; use std::convert::TryFrom; @@ -300,10 +300,9 @@ impl From for Element { .ns(ns::XMPP_STANZAS) .attr("xml:lang", lang) .append(text) - .build() }) ) - .append_all(err.other.map(Element::from).map(Node::Element).into_iter()) + .append_all(err.other) .build() } } diff --git a/src/time.rs b/src/time.rs index cbedb9883ed1fae397356c9536cbb58c40c7298b..601b3a4cd0032a323c90664f9c554328f24e8746 100644 --- a/src/time.rs +++ b/src/time.rs @@ -78,11 +78,9 @@ impl From for Element { Element::builder("time") .ns(ns::TIME) .append(Element::builder("tzo") - .append(format!("{}", time.0.timezone())) - .build()) + .append(format!("{}", time.0.timezone()))) .append(Element::builder("utc") - .append(time.0.with_timezone(FixedOffset::east(0)).format("%FT%TZ")) - .build()) + .append(time.0.with_timezone(FixedOffset::east(0)).format("%FT%TZ"))) .build() } } diff --git a/src/util/macros.rs b/src/util/macros.rs index 6843369d12dfc49b9809108ce72ba3e6c3bc75b3..f5fda080cdd1fdd0c3d52f8fd3e83c66881e5474 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -570,12 +570,10 @@ macro_rules! finish_parse_elem { macro_rules! generate_serialiser { ($builder:ident, $parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => { - $builder.append(::minidom::Node::Element( + $builder.append( ::minidom::Element::builder($name) .ns(crate::ns::$ns) .append(::minidom::Node::Text($parent.$elem)) - .build() - ) ) }; ($builder:ident, $parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => { @@ -583,8 +581,7 @@ macro_rules! generate_serialiser { ::minidom::Element::builder($name) .ns(crate::ns::$ns) .append(::minidom::Node::Text(elem)) - .build() - }).into_iter() + }) ) }; ($builder:ident, $parent:ident, $elem:ident, Option, $constructor:ident, ($name:tt, $ns:ident)) => { @@ -592,8 +589,7 @@ macro_rules! generate_serialiser { ::minidom::Element::builder($name) .ns(crate::ns::$ns) .append(::minidom::Node::Element(::minidom::Element::from(elem))) - .build() - }).into_iter() + }) ) }; ($builder:ident, $parent:ident, $elem:ident, Vec, $constructor:ident, ($name:tt, $ns:ident)) => { @@ -736,7 +732,7 @@ macro_rules! impl_pubsub_item { .ns(ns::$ns) .attr("id", item.0.id) .attr("publisher", item.0.publisher) - .append_all(item.0.payload.map(::minidom::Node::Element).into_iter()) + .append_all(item.0.payload) .build() } } diff --git a/src/xhtml.rs b/src/xhtml.rs index 0216e7c9e72fe518af5b60e53b19c34dca278157..080fb4a2e4e0d604f4efed8342fe1a034d11df45 100644 --- a/src/xhtml.rs +++ b/src/xhtml.rs @@ -98,13 +98,13 @@ impl From for Element { fn from(wrapper: XhtmlIm) -> Element { Element::builder("html") .ns(ns::XHTML_IM) - .append_all(wrapper.bodies.into_iter().map(|(ref lang, ref body)| { + .append_all(wrapper.bodies.into_iter().map(|(lang, body)| { if lang.is_empty() { assert!(body.xml_lang.is_none()); } else { - assert_eq!(Some(lang), body.xml_lang.as_ref()); + assert_eq!(Some(lang), body.xml_lang); } - Element::from(body.clone()) + Element::from(body) })) .build() } @@ -346,11 +346,11 @@ impl From for Element { } } -fn children_to_nodes(children: Vec) -> Vec { +fn children_to_nodes(children: Vec) -> impl IntoIterator { children.into_iter().map(|child| match child { Child::Tag(tag) => Node::Element(Element::from(tag)), Child::Text(text) => Node::Text(text), - }).collect::>() + }) } fn children_to_html(children: Vec) -> String { From 79804e2b01d4cf3861bd6ba7504e71c4fbe4f467 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 7 Sep 2019 16:15:32 +0200 Subject: [PATCH 664/698] error: Use better error messages. --- src/util/error.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/util/error.rs b/src/util/error.rs index f2378a19b4d99314abc43445a86335a960d10085..408c649bf4c46101dc054ab593cd32de7e46d1b0 100644 --- a/src/util/error.rs +++ b/src/util/error.rs @@ -48,14 +48,14 @@ pub enum Error { impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::ParseError(s) => write!(fmt, "{}", s), - Error::Base64Error(ref e) => write!(fmt, "{}", e), - Error::ParseIntError(ref e) => write!(fmt, "{}", e), - Error::ParseStringError(ref e) => write!(fmt, "{}", e), - Error::ParseAddrError(ref e) => write!(fmt, "{}", e), - Error::JidParseError(_) => write!(fmt, "JID parse error"), - Error::ChronoParseError(ref e) => write!(fmt, "{}", e), + match self { + Error::ParseError(s) => write!(fmt, "parse error: {}", s), + Error::Base64Error(e) => write!(fmt, "base64 error: {}", e), + Error::ParseIntError(e) => write!(fmt, "integer parsing error: {}", e), + Error::ParseStringError(e) => write!(fmt, "string parsing error: {}", e), + Error::ParseAddrError(e) => write!(fmt, "IP address parsing error: {}", e), + Error::JidParseError(e) => write!(fmt, "JID parsing error: {}", e), + Error::ChronoParseError(e) => write!(fmt, "time parsing error: {}", e), } } } From f83e9fd9281d4c6613c4a453b194f6f30696b6da Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 7 Sep 2019 16:15:44 +0200 Subject: [PATCH 665/698] error: implement std::error::Error. --- src/util/error.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/util/error.rs b/src/util/error.rs index 408c649bf4c46101dc054ab593cd32de7e46d1b0..b103711a5a0930f0d32746fe6478875bac6a401e 100644 --- a/src/util/error.rs +++ b/src/util/error.rs @@ -8,6 +8,7 @@ use base64; use chrono; use jid; use std::convert::From; +use std::error::Error as StdError; use std::fmt; use std::net; use std::num; @@ -46,6 +47,20 @@ pub enum Error { ChronoParseError(chrono::ParseError), } +impl StdError for Error { + fn cause(&self) -> Option<&dyn StdError> { + match self { + Error::ParseError(_) => None, + Error::Base64Error(e) => Some(e), + Error::ParseIntError(e) => Some(e), + Error::ParseStringError(e) => Some(e), + Error::ParseAddrError(e) => Some(e), + Error::JidParseError(e) => Some(e), + Error::ChronoParseError(e) => Some(e), + } + } +} + impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match self { From 0328ec446a7a67eb9e20559863d9352adc2cc015 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 7 Sep 2019 16:18:25 +0200 Subject: [PATCH 666/698] error: Remove unused imports. --- src/util/error.rs | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/util/error.rs b/src/util/error.rs index b103711a5a0930f0d32746fe6478875bac6a401e..33b2a699a2dea2cf8cbde53cd6a66ec5fa388a83 100644 --- a/src/util/error.rs +++ b/src/util/error.rs @@ -4,15 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use base64; -use chrono; -use jid; -use std::convert::From; use std::error::Error as StdError; use std::fmt; -use std::net; -use std::num; -use std::string; /// Contains one of the potential errors triggered while parsing an /// [Element](../struct.Element.html) into a specialised struct. @@ -29,14 +22,14 @@ pub enum Error { Base64Error(base64::DecodeError), /// Generated when text which should be an integer fails to parse. - ParseIntError(num::ParseIntError), + ParseIntError(std::num::ParseIntError), /// Generated when text which should be a string fails to parse. - ParseStringError(string::ParseError), + ParseStringError(std::string::ParseError), /// Generated when text which should be an IP address (IPv4 or IPv6) fails /// to parse. - ParseAddrError(net::AddrParseError), + ParseAddrError(std::net::AddrParseError), /// Generated when text which should be a [JID](../../jid/struct.Jid.html) /// fails to parse. @@ -81,20 +74,20 @@ impl From for Error { } } -impl From for Error { - fn from(err: num::ParseIntError) -> Error { +impl From for Error { + fn from(err: std::num::ParseIntError) -> Error { Error::ParseIntError(err) } } -impl From for Error { - fn from(err: string::ParseError) -> Error { +impl From for Error { + fn from(err: std::string::ParseError) -> Error { Error::ParseStringError(err) } } -impl From for Error { - fn from(err: net::AddrParseError) -> Error { +impl From for Error { + fn from(err: std::net::AddrParseError) -> Error { Error::ParseAddrError(err) } } From b307652421cc5bf344f303ed41152786240349bc Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 7 Sep 2019 16:21:42 +0200 Subject: [PATCH 667/698] Remove unused base64 imports. --- src/caps.rs | 2 -- src/ecaps2.rs | 1 - src/hashes.rs | 1 - src/jingle_ft.rs | 1 - src/util/helpers.rs | 1 - 5 files changed, 6 deletions(-) diff --git a/src/caps.rs b/src/caps.rs index e5e998b1df7561085baa580473f576030cd1c0af..32f8f5735edd94fad2c3cb6e217bce745f264c50 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -10,7 +10,6 @@ use crate::util::error::Error; use crate::hashes::{Algo, Hash}; use crate::ns; use crate::presence::PresencePayload; -use base64; use blake2::VarBlake2b; use digest::{Digest, Input, VariableOutput}; use minidom::Element; @@ -218,7 +217,6 @@ pub fn query_caps(caps: Caps) -> DiscoInfoQuery { mod tests { use super::*; use crate::caps; - use base64; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/ecaps2.rs b/src/ecaps2.rs index d408b28de48f1ccc74c09ad074e0322b35b278b0..8722d065050bca5c65d33291c20db39da6c4c962 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -9,7 +9,6 @@ use crate::disco::{DiscoInfoQuery, DiscoInfoResult, Feature, Identity}; use crate::hashes::{Algo, Hash}; use crate::ns; use crate::presence::PresencePayload; -use base64; use blake2::VarBlake2b; use digest::{Digest, Input, VariableOutput}; use sha2::{Sha256, Sha512}; diff --git a/src/hashes.rs b/src/hashes.rs index e3f73ca83c51dfbf6187b90d78b4911915826e30..9987407a694fb736b4f783606b43e4a1fe6d2586 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -6,7 +6,6 @@ use crate::util::error::Error; use crate::util::helpers::Base64; -use base64; use minidom::IntoAttributeValue; use std::num::ParseIntError; use std::ops::{Deref, DerefMut}; diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 9902fed773142f24231dc0f076ae4d9fde4aa821..f3fc9a33fdad6ac7d5e221df9e7d2f31586e6434 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -327,7 +327,6 @@ generate_element!( mod tests { use super::*; use crate::hashes::Algo; - use base64; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/util/helpers.rs b/src/util/helpers.rs index b51b18a9c8ee6259b450fb5169eeff90c2badf33..a455a139f0eb26bd9e13f01f9e1a0a47f6cad8c1 100644 --- a/src/util/helpers.rs +++ b/src/util/helpers.rs @@ -5,7 +5,6 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::util::error::Error; -use base64; /// Codec for plain text content. pub struct PlainText; From d58321ebba16651bb693538d6936d9d34476cea8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 7 Sep 2019 16:32:03 +0200 Subject: [PATCH 668/698] Add a new CSI parser (XEP-0352). --- ChangeLog | 1 + src/csi.rs | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 +++ src/ns.rs | 3 +++ 4 files changed, 66 insertions(+) create mode 100644 src/csi.rs diff --git a/ChangeLog b/ChangeLog index 372ebddef25d66a6bec783f258542a143e58123f..825c7a4065d7861e6e96a61b6f7168c3122a8dce 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,7 @@ Version NEXT: DATE Emmanuel Gil Peyrot * New parsers/serialisers: + - Client State Indication (XEP-0352) - OpenPGP for XMPP (XEP-0373) * Breaking changes: - Presence constructors now take Into and assume Some. diff --git a/src/csi.rs b/src/csi.rs new file mode 100644 index 0000000000000000000000000000000000000000..0465f94b2014f3d98e050a069052414e3d8647ff --- /dev/null +++ b/src/csi.rs @@ -0,0 +1,59 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +generate_empty_element!( + /// Stream:feature sent by the server to advertise it supports CSI. + Feature, "csi", CSI +); + +generate_empty_element!( + /// Client indicates it is inactive. + Inactive, "inactive", CSI +); + +generate_empty_element!( + /// Client indicates it is active again. + Active, "active", CSI +); + +#[cfg(test)] +mod tests { + use super::*; + use minidom::Element; + use std::convert::TryFrom; + use crate::ns; + + #[test] + fn test_size() { + assert_size!(Feature, 0); + assert_size!(Inactive, 0); + assert_size!(Active, 0); + } + + #[test] + fn parsing() { + let elem: Element = "".parse().unwrap(); + Feature::try_from(elem).unwrap(); + + let elem: Element = "".parse().unwrap(); + Inactive::try_from(elem).unwrap(); + + let elem: Element = "".parse().unwrap(); + Active::try_from(elem).unwrap(); + } + + #[test] + fn serialising() { + let elem: Element = Feature.into(); + assert!(elem.is("csi", ns::CSI)); + + let elem: Element = Inactive.into(); + assert!(elem.is("inactive", ns::CSI)); + + let elem: Element = Active.into(); + assert!(elem.is("active", ns::CSI)); + } +} diff --git a/src/lib.rs b/src/lib.rs index ea09f2f0ea5e50e06f2a7059d98dbe7abb7263e3..da1af26c4cd99effc751de78b9bd465739a7619d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -177,6 +177,9 @@ pub mod idle; /// XEP-0320: Use of DTLS-SRTP in Jingle Sessions pub mod jingle_dtls_srtp; +/// XEP-0352: Client State Indication +pub mod csi; + /// XEP-0353: Jingle Message Initiation pub mod jingle_message; diff --git a/src/ns.rs b/src/ns.rs index e5242b25667a70312c2e5b4f405c4efb6796a18a..f5941258a8c7459370c215595a75c9567b236ae2 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -182,6 +182,9 @@ pub const IDLE: &str = "urn:xmpp:idle:1"; /// XEP-0320: Use of DTLS-SRTP in Jingle Sessions pub const JINGLE_DTLS: &str = "urn:xmpp:jingle:apps:dtls:0"; +/// XEP-0352: Client State Indication +pub const CSI: &str = "urn:xmpp:csi:0"; + /// XEP-0353: Jingle Message Initiation pub const JINGLE_MESSAGE: &str = "urn:xmpp:jingle-message:0"; From 05ab0cdc3844c6db261e2abf7ab91ade2e2cb2fa Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 7 Sep 2019 16:32:03 +0200 Subject: [PATCH 669/698] Add a new CSI parser (XEP-0352). --- ChangeLog | 1 + doap.xml | 8 ++++++++ src/csi.rs | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 +++ src/ns.rs | 3 +++ 5 files changed, 74 insertions(+) create mode 100644 src/csi.rs diff --git a/ChangeLog b/ChangeLog index 372ebddef25d66a6bec783f258542a143e58123f..825c7a4065d7861e6e96a61b6f7168c3122a8dce 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,7 @@ Version NEXT: DATE Emmanuel Gil Peyrot * New parsers/serialisers: + - Client State Indication (XEP-0352) - OpenPGP for XMPP (XEP-0373) * Breaking changes: - Presence constructors now take Into and assume Some. diff --git a/doap.xml b/doap.xml index b05c4c42b3e6f2d14940f2e386d0037c014538c2..713ca9f5e0cc815fab411f695a544d2a67c389b8 100644 --- a/doap.xml +++ b/doap.xml @@ -392,6 +392,14 @@ 0.13.0 + + + + complete + 0.3.0 + NEXT + + diff --git a/src/csi.rs b/src/csi.rs new file mode 100644 index 0000000000000000000000000000000000000000..0465f94b2014f3d98e050a069052414e3d8647ff --- /dev/null +++ b/src/csi.rs @@ -0,0 +1,59 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +generate_empty_element!( + /// Stream:feature sent by the server to advertise it supports CSI. + Feature, "csi", CSI +); + +generate_empty_element!( + /// Client indicates it is inactive. + Inactive, "inactive", CSI +); + +generate_empty_element!( + /// Client indicates it is active again. + Active, "active", CSI +); + +#[cfg(test)] +mod tests { + use super::*; + use minidom::Element; + use std::convert::TryFrom; + use crate::ns; + + #[test] + fn test_size() { + assert_size!(Feature, 0); + assert_size!(Inactive, 0); + assert_size!(Active, 0); + } + + #[test] + fn parsing() { + let elem: Element = "".parse().unwrap(); + Feature::try_from(elem).unwrap(); + + let elem: Element = "".parse().unwrap(); + Inactive::try_from(elem).unwrap(); + + let elem: Element = "".parse().unwrap(); + Active::try_from(elem).unwrap(); + } + + #[test] + fn serialising() { + let elem: Element = Feature.into(); + assert!(elem.is("csi", ns::CSI)); + + let elem: Element = Inactive.into(); + assert!(elem.is("inactive", ns::CSI)); + + let elem: Element = Active.into(); + assert!(elem.is("active", ns::CSI)); + } +} diff --git a/src/lib.rs b/src/lib.rs index ea09f2f0ea5e50e06f2a7059d98dbe7abb7263e3..da1af26c4cd99effc751de78b9bd465739a7619d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -177,6 +177,9 @@ pub mod idle; /// XEP-0320: Use of DTLS-SRTP in Jingle Sessions pub mod jingle_dtls_srtp; +/// XEP-0352: Client State Indication +pub mod csi; + /// XEP-0353: Jingle Message Initiation pub mod jingle_message; diff --git a/src/ns.rs b/src/ns.rs index e5242b25667a70312c2e5b4f405c4efb6796a18a..f5941258a8c7459370c215595a75c9567b236ae2 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -182,6 +182,9 @@ pub const IDLE: &str = "urn:xmpp:idle:1"; /// XEP-0320: Use of DTLS-SRTP in Jingle Sessions pub const JINGLE_DTLS: &str = "urn:xmpp:jingle:apps:dtls:0"; +/// XEP-0352: Client State Indication +pub const CSI: &str = "urn:xmpp:csi:0"; + /// XEP-0353: Jingle Message Initiation pub const JINGLE_MESSAGE: &str = "urn:xmpp:jingle-message:0"; From 943292a74942ec7c496d7431278ec098983d557b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 17 Jul 2019 17:30:52 +0200 Subject: [PATCH 670/698] Implement occupant-id (XEP-0421). --- src/lib.rs | 3 ++ src/ns.rs | 3 ++ src/occupant_id.rs | 89 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 src/occupant_id.rs diff --git a/src/lib.rs b/src/lib.rs index da1af26c4cd99effc751de78b9bd465739a7619d..06048c8e30634d8c8b4051047592105832d59e45 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -194,3 +194,6 @@ pub mod eme; /// XEP-0390: Entity Capabilities 2.0 pub mod ecaps2; + +/// XEP-0421: Anonymous unique occupant identifiers for MUCs +pub mod occupant_id; diff --git a/src/ns.rs b/src/ns.rs index f5941258a8c7459370c215595a75c9567b236ae2..ef785082e8a4809b24a9db5f130952d26be448b5 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -204,6 +204,9 @@ pub const ECAPS2: &str = "urn:xmpp:caps"; /// XEP-0390: Entity Capabilities 2.0 pub const ECAPS2_OPTIMIZE: &str = "urn:xmpp:caps:optimize"; +/// XEP-0421: Anonymous unique occupant identifiers for MUCs +pub const OID: &str = "urn:xmpp:occupant-id:0"; + /// Alias for the main namespace of the stream, that is "jabber:client" when /// the component feature isn’t enabled. #[cfg(not(feature = "component"))] diff --git a/src/occupant_id.rs b/src/occupant_id.rs new file mode 100644 index 0000000000000000000000000000000000000000..730652942d9073973f3e7d149796e5155a6b5a7d --- /dev/null +++ b/src/occupant_id.rs @@ -0,0 +1,89 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use crate::message::MessagePayload; +use crate::presence::PresencePayload; + +generate_element!( + /// Unique identifier given to a MUC participant. + /// + /// It allows clients to identify a MUC participant across reconnects and + /// renames. It thus prevents impersonification of anonymous users. + OccupantId, "occupant-id", OID, + + attributes: [ + /// The id associated to the sending user by the MUC service. + id: Required = "id", + ] +); + +impl MessagePayload for OccupantId {} +impl PresencePayload for OccupantId {} + +#[cfg(test)] +mod tests { + use super::*; + use crate::util::error::Error; + use minidom::Element; + use std::convert::TryFrom; + + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(OccupantId, 12); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(OccupantId, 24); + } + + #[test] + fn test_simple() { + let elem: Element = "" + .parse() + .unwrap(); + let origin_id = OccupantId::try_from(elem).unwrap(); + assert_eq!(origin_id.id, "coucou"); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "" + .parse() + .unwrap(); + let error = OccupantId::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in occupant-id element."); + } + + #[test] + fn test_invalid_id() { + let elem: Element = "".parse().unwrap(); + let error = OccupantId::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'id' missing."); + } + + #[test] + fn test_serialise() { + let elem: Element = "" + .parse() + .unwrap(); + let occupant_id = OccupantId { + id: String::from("coucou"), + }; + let elem2 = occupant_id.into(); + assert_eq!(elem, elem2); + } +} From d9a6aeea9981e0e2e013ef392130b1d9648f790e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 7 Sep 2019 16:43:05 +0200 Subject: [PATCH 671/698] doap, ChangeLog: Add support for occupand-id (XEP-0421). --- ChangeLog | 1 + doap.xml | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/ChangeLog b/ChangeLog index 825c7a4065d7861e6e96a61b6f7168c3122a8dce..275600e1314ce35b739d26fae0b97aa842b372d4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,7 @@ DATE Emmanuel Gil Peyrot * New parsers/serialisers: - Client State Indication (XEP-0352) - OpenPGP for XMPP (XEP-0373) + - Anonymous unique occupant identifiers for MUCs (XEP-0421) * Breaking changes: - Presence constructors now take Into and assume Some. diff --git a/doap.xml b/doap.xml index 713ca9f5e0cc815fab411f695a544d2a67c389b8..6b8bf5a135796468aea85186800636e92458d68b 100644 --- a/doap.xml +++ b/doap.xml @@ -440,6 +440,14 @@ 0.1.0 + + + + complete + 0.1.0 + NEXT + + From 46522ceb198b68ce392b1be8b531fb3f38e4fc27 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 8 Sep 2019 15:53:55 +0200 Subject: [PATCH 672/698] Add a new client certificate management parser (XEP-0257). --- ChangeLog | 1 + doap.xml | 8 ++ src/cert_management.rs | 191 +++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 + src/ns.rs | 3 + src/util/macros.rs | 24 ++++++ 6 files changed, 230 insertions(+) create mode 100644 src/cert_management.rs diff --git a/ChangeLog b/ChangeLog index 275600e1314ce35b739d26fae0b97aa842b372d4..f1f253dca3490fdb4ae7b171f6a52af7d028e4d1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,7 @@ Version NEXT: DATE Emmanuel Gil Peyrot * New parsers/serialisers: + - Client Certificate Management for SASL EXTERNAL (XEP-0257) - Client State Indication (XEP-0352) - OpenPGP for XMPP (XEP-0373) - Anonymous unique occupant identifiers for MUCs (XEP-0421) diff --git a/doap.xml b/doap.xml index 6b8bf5a135796468aea85186800636e92458d68b..b9fc9965d9b3c03739fade79adc7e193a4633ac1 100644 --- a/doap.xml +++ b/doap.xml @@ -311,6 +311,14 @@ 0.1.0 + + + + complete + 0.3 + NEXT + + diff --git a/src/cert_management.rs b/src/cert_management.rs new file mode 100644 index 0000000000000000000000000000000000000000..a68909eb8b06d9c2f070b556a4737659479edd51 --- /dev/null +++ b/src/cert_management.rs @@ -0,0 +1,191 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use crate::iq::{IqSetPayload, IqGetPayload, IqResultPayload}; +use crate::util::helpers::Base64; + +generate_elem_id!( + /// The name of a certificate. + Name, "name", SASL_CERT +); + +generate_element!( + /// An X.509 certificate. + Cert, "x509cert", SASL_CERT, + text: ( + /// The BER X.509 data. + data: Base64> + ) +); + +generate_element!( + /// For the client to upload an X.509 certificate. + Append, "append", SASL_CERT, + children: [ + /// The name of this certificate. + name: Required = ("name", SASL_CERT) => Name, + + /// The X.509 certificate to set. + cert: Required = ("x509cert", SASL_CERT) => Cert, + + /// This client is forbidden from managing certificates. + no_cert_management: Present<_> = ("no-cert-management", SASL_CERT) => bool + ] +); + +impl IqSetPayload for Append {} + +generate_empty_element!( + /// Client requests the current list of X.509 certificates. + ListCertsQuery, "items", SASL_CERT +); + +impl IqGetPayload for ListCertsQuery {} + +generate_elem_id!( + /// One resource currently using a certificate. + Resource, "resource", SASL_CERT +); + +generate_element!( + /// A list of resources currently using this certificate. + Users, "users", SASL_CERT, + children: [ + /// Resources currently using this certificate. + resources: Vec = ("resource", SASL_CERT) => Resource + ] +); + +generate_element!( + /// An X.509 certificate being set for this user. + Item, "item", SASL_CERT, + children: [ + /// The name of this certificate. + name: Required = ("name", SASL_CERT) => Name, + + /// The X.509 certificate to set. + cert: Required = ("x509cert", SASL_CERT) => Cert, + + /// This client is forbidden from managing certificates. + no_cert_management: Present<_> = ("no-cert-management", SASL_CERT) => bool, + + /// List of resources currently using this certificate. + users: Option = ("users", SASL_CERT) => Users + ] +); + +generate_element!( + /// Server answers with the current list of X.509 certificates. + ListCertsResponse, "items", SASL_CERT, + children: [ + /// List of certificates. + items: Vec = ("item", SASL_CERT) => Item + ] +); + +impl IqResultPayload for ListCertsResponse {} + +generate_element!( + /// Client disables an X.509 certificate. + Disable, "disable", SASL_CERT, + children: [ + /// Name of the certificate to disable. + name: Required = ("name", SASL_CERT) => Name + ] +); + +impl IqSetPayload for Disable {} + +generate_element!( + /// Client revokes an X.509 certificate. + Revoke, "revoke", SASL_CERT, + children: [ + /// Name of the certificate to revoke. + name: Required = ("name", SASL_CERT) => Name + ] +); + +impl IqSetPayload for Revoke {} + +#[cfg(test)] +mod tests { + use super::*; + use minidom::Element; + use std::convert::TryFrom; + use std::str::FromStr; + use crate::ns; + + #[test] + fn test_size() { + assert_size!(Append, 56); + } + + #[test] + fn simple() { + let elem: Element = "Mobile ClientAAAA".parse().unwrap(); + let append = Append::try_from(elem).unwrap(); + assert_eq!(append.name.0, "Mobile Client"); + assert_eq!(append.cert.data, b"\0\0\0"); + + let elem: Element = "Mobile Client".parse().unwrap(); + let disable = Disable::try_from(elem).unwrap(); + assert_eq!(disable.name.0, "Mobile Client"); + + let elem: Element = "Mobile Client".parse().unwrap(); + let revoke = Revoke::try_from(elem).unwrap(); + assert_eq!(revoke.name.0, "Mobile Client"); + } + + #[test] + fn list() { + let elem: Element = r#" + + + Mobile Client + AAAA + + Phone + + + + Laptop + BBBB + + "#.parse().unwrap(); + let mut list = ListCertsResponse::try_from(elem).unwrap(); + assert_eq!(list.items.len(), 2); + + let item = list.items.pop().unwrap(); + assert_eq!(item.name.0, "Laptop"); + assert_eq!(item.cert.data, [4, 16, 65]); + assert!(item.users.is_none()); + + let item = list.items.pop().unwrap(); + assert_eq!(item.name.0, "Mobile Client"); + assert_eq!(item.cert.data, b"\0\0\0"); + assert_eq!(item.users.unwrap().resources.len(), 1); + } + + #[test] + fn test_serialise() { + let append = Append { + name: Name::from_str("Mobile Client").unwrap(), + cert: Cert { data: b"\0\0\0".to_vec() }, + no_cert_management: false, + }; + let elem: Element = append.into(); + assert!(elem.is("append", ns::SASL_CERT)); + + let disable = Disable { + name: Name::from_str("Mobile Client").unwrap(), + }; + let elem: Element = disable.into(); + assert!(elem.is("disable", ns::SASL_CERT)); + let elem = elem.children().cloned().collect::>().pop().unwrap(); + assert!(elem.is("name", ns::SASL_CERT)); + assert_eq!(elem.text(), "Mobile Client"); + } +} diff --git a/src/lib.rs b/src/lib.rs index 06048c8e30634d8c8b4051047592105832d59e45..9f6053cc6fce8b24d146c93e99c9f1144fdbd84c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -150,6 +150,9 @@ pub mod bob; /// XEP-0234: Jingle File Transfer pub mod jingle_ft; +/// XEP-0257: Client Certificate Management for SASL EXTERNAL +pub mod cert_management; + /// XEP-0260: Jingle SOCKS5 Bytestreams Transport Method pub mod jingle_s5b; diff --git a/src/ns.rs b/src/ns.rs index ef785082e8a4809b24a9db5f130952d26be448b5..328ed4820484e2dcc758e37d51f049ada5b8fa4f 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -140,6 +140,9 @@ pub const JINGLE_FT: &str = "urn:xmpp:jingle:apps:file-transfer:5"; /// XEP-0234: Jingle File Transfer pub const JINGLE_FT_ERROR: &str = "urn:xmpp:jingle:apps:file-transfer:errors:0"; +/// XEP-0257: Client Certificate Management for SASL EXTERNAL +pub const SASL_CERT: &str = "urn:xmpp:saslcert:1"; + /// XEP-0260: Jingle SOCKS5 Bytestreams Transport Method pub const JINGLE_S5B: &str = "urn:xmpp:jingle:transports:s5b:1"; diff --git a/src/util/macros.rs b/src/util/macros.rs index f5fda080cdd1fdd0c3d52f8fd3e83c66881e5474..fd7f58cc649c2ff3ef5352ff5e6a3a047edb395d 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -494,6 +494,9 @@ macro_rules! start_decl { (Required, $type:ty) => ( $type ); + (Present, $type:ty) => ( + bool + ); } macro_rules! start_parse_elem { @@ -506,6 +509,9 @@ macro_rules! start_parse_elem { ($temp:ident: Required) => { let mut $temp = None; }; + ($temp:ident: Present) => { + let mut $temp = false; + }; } macro_rules! do_parse { @@ -548,6 +554,18 @@ macro_rules! do_parse_elem { } $temp = Some(do_parse!($elem, $constructor)); }; + ($temp:ident: Present = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => { + if $temp { + return Err(crate::util::error::Error::ParseError(concat!( + "Element ", + $parent_name, + " must not have more than one ", + $name, + " child." + ))); + } + $temp = true; + }; } macro_rules! finish_parse_elem { @@ -566,6 +584,9 @@ macro_rules! finish_parse_elem { " element." )))? }; + ($temp:ident: Present = $name:tt, $parent_name:tt) => { + $temp + }; } macro_rules! generate_serialiser { @@ -595,6 +616,9 @@ macro_rules! generate_serialiser { ($builder:ident, $parent:ident, $elem:ident, Vec, $constructor:ident, ($name:tt, $ns:ident)) => { $builder.append_all($parent.$elem.into_iter()) }; + ($builder:ident, $parent:ident, $elem:ident, Present, $constructor:ident, ($name:tt, $ns:ident)) => { + $builder.append(::minidom::Node::Element(::minidom::Element::builder($name).ns(crate::ns::$ns).build())) + }; ($builder:ident, $parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => { $builder.append(::minidom::Node::Element(::minidom::Element::from($parent.$elem))) }; From 9941e9c34f8eae76292fa43799aa3da41e821eef Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 8 Sep 2019 16:09:49 +0200 Subject: [PATCH 673/698] Add a new JID Prep parser (XEP-0328). --- ChangeLog | 1 + doap.xml | 8 +++++++ src/jid_prep.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 +++ src/ns.rs | 3 +++ src/util/helpers.rs | 15 ++++++++++++ 6 files changed, 86 insertions(+) create mode 100644 src/jid_prep.rs diff --git a/ChangeLog b/ChangeLog index f1f253dca3490fdb4ae7b171f6a52af7d028e4d1..43367a608978f220b79ddfb5d19b4a00e973522e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,7 @@ Version NEXT: DATE Emmanuel Gil Peyrot * New parsers/serialisers: - Client Certificate Management for SASL EXTERNAL (XEP-0257) + - JID Prep (XEP-0328) - Client State Indication (XEP-0352) - OpenPGP for XMPP (XEP-0373) - Anonymous unique occupant identifiers for MUCs (XEP-0421) diff --git a/doap.xml b/doap.xml index b9fc9965d9b3c03739fade79adc7e193a4633ac1..f591bfff5e86ea6bc965e187cf6967b64a0e7e96 100644 --- a/doap.xml +++ b/doap.xml @@ -400,6 +400,14 @@ 0.13.0 + + + + complete + 0.1 + NEXT + + diff --git a/src/jid_prep.rs b/src/jid_prep.rs new file mode 100644 index 0000000000000000000000000000000000000000..9ecff61ad7dd09f5dde4b4d831eb5f4354c96b9d --- /dev/null +++ b/src/jid_prep.rs @@ -0,0 +1,56 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use crate::iq::{IqGetPayload, IqResultPayload}; +use crate::util::helpers::{PlainText, JidCodec}; +use jid::Jid; + +generate_element!( + /// TODO + JidPrepQuery, "jid", JID_PREP, + text: ( + /// TODO + data: PlainText> + ) +); + +impl IqGetPayload for JidPrepQuery {} + +generate_element!( + /// TODO + JidPrepResponse, "jid", JID_PREP, + text: ( + /// TODO + jid: JidCodec + ) +); + +impl IqResultPayload for JidPrepResponse {} + +#[cfg(test)] +mod tests { + use super::*; + use minidom::Element; + use std::convert::TryFrom; + use std::str::FromStr; + + #[test] + fn test_size() { + assert_size!(JidPrepQuery, 24); + assert_size!(JidPrepResponse, 80); + } + + #[test] + fn simple() { + let elem: Element = "ROMeo@montague.lit/orchard".parse().unwrap(); + let query = JidPrepQuery::try_from(elem).unwrap(); + assert_eq!(query.data.unwrap(), "ROMeo@montague.lit/orchard"); + + let elem: Element = "romeo@montague.lit/orchard".parse().unwrap(); + let response = JidPrepResponse::try_from(elem).unwrap(); + assert_eq!(response.jid, Jid::from_str("romeo@montague.lit/orchard").unwrap()); + } +} diff --git a/src/lib.rs b/src/lib.rs index 9f6053cc6fce8b24d146c93e99c9f1144fdbd84c..b69e56ad4408e19f4542926a5725394ab6a3a867 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -180,6 +180,9 @@ pub mod idle; /// XEP-0320: Use of DTLS-SRTP in Jingle Sessions pub mod jingle_dtls_srtp; +/// XEP-0328: JID Prep +pub mod jid_prep; + /// XEP-0352: Client State Indication pub mod csi; diff --git a/src/ns.rs b/src/ns.rs index 328ed4820484e2dcc758e37d51f049ada5b8fa4f..70d68cff156aff8aebddb24e91b02c2559caf018 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -185,6 +185,9 @@ pub const IDLE: &str = "urn:xmpp:idle:1"; /// XEP-0320: Use of DTLS-SRTP in Jingle Sessions pub const JINGLE_DTLS: &str = "urn:xmpp:jingle:apps:dtls:0"; +/// XEP-0328: JID Prep +pub const JID_PREP: &str = "urn:xmpp:jidprep:0"; + /// XEP-0352: Client State Indication pub const CSI: &str = "urn:xmpp:csi:0"; diff --git a/src/util/helpers.rs b/src/util/helpers.rs index a455a139f0eb26bd9e13f01f9e1a0a47f6cad8c1..a78fa7f6db822e2091ba8182a1420244165fdfe9 100644 --- a/src/util/helpers.rs +++ b/src/util/helpers.rs @@ -5,6 +5,8 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::util::error::Error; +use jid::Jid; +use std::str::FromStr; /// Codec for plain text content. pub struct PlainText; @@ -89,3 +91,16 @@ impl ColonSeparatedHex { Some(bytes.join(":")) } } + +/// Codec for a JID. +pub struct JidCodec; + +impl JidCodec { + pub fn decode(s: &str) -> Result { + Ok(Jid::from_str(s)?) + } + + pub fn encode(jid: &Jid) -> Option { + Some(jid.to_string()) + } +} From a9a68cb1d749051558ef6978e3804d32e1bf3a6c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 8 Sep 2019 16:22:12 +0200 Subject: [PATCH 674/698] jid_prep: Add constructor, documentation, and switch from Option to String. --- src/jid_prep.rs | 23 ++++++++++++++++------- src/util/helpers.rs | 13 +++++++++++++ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/jid_prep.rs b/src/jid_prep.rs index 9ecff61ad7dd09f5dde4b4d831eb5f4354c96b9d..d151f6c04bec1d5af2e6106c3c812e8239536644 100644 --- a/src/jid_prep.rs +++ b/src/jid_prep.rs @@ -5,25 +5,34 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::iq::{IqGetPayload, IqResultPayload}; -use crate::util::helpers::{PlainText, JidCodec}; +use crate::util::helpers::{Text, JidCodec}; use jid::Jid; generate_element!( - /// TODO + /// Request from a client to stringprep/PRECIS a string into a JID. JidPrepQuery, "jid", JID_PREP, text: ( - /// TODO - data: PlainText> + /// The potential JID. + data: Text ) ); impl IqGetPayload for JidPrepQuery {} +impl JidPrepQuery { + /// Create a new JID Prep query. + pub fn new>(jid: J) -> JidPrepQuery { + JidPrepQuery { + data: jid.into(), + } + } +} + generate_element!( - /// TODO + /// Response from the server with the stringprep’d/PRECIS’d JID. JidPrepResponse, "jid", JID_PREP, text: ( - /// TODO + /// The JID. jid: JidCodec ) ); @@ -47,7 +56,7 @@ mod tests { fn simple() { let elem: Element = "ROMeo@montague.lit/orchard".parse().unwrap(); let query = JidPrepQuery::try_from(elem).unwrap(); - assert_eq!(query.data.unwrap(), "ROMeo@montague.lit/orchard"); + assert_eq!(query.data, "ROMeo@montague.lit/orchard"); let elem: Element = "romeo@montague.lit/orchard".parse().unwrap(); let response = JidPrepResponse::try_from(elem).unwrap(); diff --git a/src/util/helpers.rs b/src/util/helpers.rs index a78fa7f6db822e2091ba8182a1420244165fdfe9..7bf3feb1f2b59e453bd4dd58694aa9083e1d6f3e 100644 --- a/src/util/helpers.rs +++ b/src/util/helpers.rs @@ -8,6 +8,19 @@ use crate::util::error::Error; use jid::Jid; use std::str::FromStr; +/// Codec for text content. +pub struct Text; + +impl Text { + pub fn decode(s: &str) -> Result { + Ok(s.to_owned()) + } + + pub fn encode(string: &str) -> Option { + Some(string.to_owned()) + } +} + /// Codec for plain text content. pub struct PlainText; From c7038ace1a1a77e209b48fd955be0c79c8cd01a8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 8 Sep 2019 18:24:45 +0200 Subject: [PATCH 675/698] jingle, pubsub: Fix spelling thanks to codespell! --- src/jingle.rs | 2 +- src/pubsub/pubsub.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index 4a8e5ac357b577e88861aeb5db7535db0a3b67fe..7e27ebbc6efe2ff73d628fdbcc4aedb5819db276 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -445,7 +445,7 @@ pub struct Jingle { /// Unique session identifier between two entities. pub sid: SessionId, - /// A list of contents to be negociated in this session. + /// A list of contents to be negotiated in this session. pub contents: Vec, /// An optional reason. diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index 727ef2b882d5c86de46ec215e82127a317b029fc..875c1989538364ba805e8fac39ffcb8fe525f13f 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -46,7 +46,7 @@ generate_attribute!( /// You are a publisher on this node, you can publish and retract items to it. Publisher => "publisher", - /// You can publish and retract items on this node, but not subscribe or retrive items. + /// You can publish and retract items on this node, but not subscribe or retrieve items. PublishOnly => "publish-only", } ); @@ -85,7 +85,7 @@ generate_element!( /// Request for a default node configuration. Default, "default", PUBSUB, attributes: [ - /// The node targetted by this request, otherwise the entire service. + /// The node targeted by this request, otherwise the entire service. node: Option = "node", // TODO: do we really want to support collection nodes? From 8b54b7fd43083babdf188e731d6f6c579a3645f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Fri, 13 Sep 2019 03:33:08 +0200 Subject: [PATCH 676/698] CI: refactor, add caching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- .gitlab-ci.yml | 54 +++++++++++++++++++++++++++++++++++--------------- ChangeLog | 2 ++ 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9f74e6dca4c9dc3dca4175f28c3d106d2e7cfc1a..cd0be07974f035ea057fe9f435605d282972c53a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,37 +4,59 @@ stages: variables: FEATURES: "" + RUST_BACKTRACE: "full" -rust-latest-build: - stage: build +.stable: image: rust:latest - script: - - cargo build --verbose --no-default-features --features=$FEATURES + cache: + key: stable + paths: + - target/ -rust-nightly-build: - stage: build +.nightly: image: rustlang/rust:nightly + cache: + key: nightly + paths: + - target/ + +.build: + stage: build script: - cargo build --verbose --no-default-features --features=$FEATURES -rust-latest-test: +.test: stage: test - image: rust:latest script: - cargo test --verbose --no-default-features --features=$FEATURES +rust-latest-build: + extends: + - .build + - .stable + +rust-nightly-build: + extends: + - .build + - .nightly + + +rust-latest-test: + extends: + - .test + - .stable + rust-nightly-test: - stage: test - image: rustlang/rust:nightly - script: - - cargo test --verbose --no-default-features --features=$FEATURES + extends: + - .test + - .nightly -"rust-latest-test with features=disable-validation": - extends: rust-latest-test +rust-latest-build with features=disable-validation: + extends: rust-latest-build variables: FEATURES: "disable-validation" -"rust-nightly-test with features=disable-validation": - extends: rust-nightly-test +rust-latest-test with features=disable-validation: + extends: rust-latest-test variables: FEATURES: "disable-validation" diff --git a/ChangeLog b/ChangeLog index 43367a608978f220b79ddfb5d19b4a00e973522e..381e8933ae03738206b530c64882c47642aeb9db 100644 --- a/ChangeLog +++ b/ChangeLog @@ -8,6 +8,8 @@ DATE Emmanuel Gil Peyrot - Anonymous unique occupant identifiers for MUCs (XEP-0421) * Breaking changes: - Presence constructors now take Into and assume Some. + * Improvements: + - CI: refactor, add caching Version 0.15.0: 2019-09-06 Emmanuel Gil Peyrot From eac385700c336d3e9190db5a5344afb679c1f87e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 17 Sep 2019 17:13:18 +0200 Subject: [PATCH 677/698] bob, jid_prep, cert_management: Add size tests for 32-bit. --- src/bob.rs | 4 ++-- src/cert_management.rs | 23 +++++++++++++++++++++++ src/jid_prep.rs | 8 ++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/bob.rs b/src/bob.rs index 704ed0c94ef7c692f1cea8478f353306c9affdd8..edb0c89103128ef94f5045c05c9ade37f78f9d74 100644 --- a/src/bob.rs +++ b/src/bob.rs @@ -95,8 +95,8 @@ mod tests { #[cfg(target_pointer_width = "32")] #[test] fn test_size() { - assert_size!(ContentId, 24); - assert_size!(Data, 24); + assert_size!(ContentId, 28); + assert_size!(Data, 60); } #[cfg(target_pointer_width = "64")] diff --git a/src/cert_management.rs b/src/cert_management.rs index a68909eb8b06d9c2f070b556a4737659479edd51..7ed5ca7a4f461bf90cdc9814a2667006cd428cd6 100644 --- a/src/cert_management.rs +++ b/src/cert_management.rs @@ -118,9 +118,32 @@ mod tests { use std::str::FromStr; use crate::ns; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Append, 28); + assert_size!(Disable, 12); + assert_size!(Revoke, 12); + assert_size!(ListCertsQuery, 0); + assert_size!(ListCertsResponse, 12); + assert_size!(Item, 40); + assert_size!(Resource, 12); + assert_size!(Users, 12); + assert_size!(Cert, 12); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Append, 56); + assert_size!(Disable, 24); + assert_size!(Revoke, 24); + assert_size!(ListCertsQuery, 0); + assert_size!(ListCertsResponse, 24); + assert_size!(Item, 80); + assert_size!(Resource, 24); + assert_size!(Users, 24); + assert_size!(Cert, 24); } #[test] diff --git a/src/jid_prep.rs b/src/jid_prep.rs index d151f6c04bec1d5af2e6106c3c812e8239536644..ba8c9d84e18a28a3ade067108b53765af97bc84a 100644 --- a/src/jid_prep.rs +++ b/src/jid_prep.rs @@ -46,6 +46,14 @@ mod tests { use std::convert::TryFrom; use std::str::FromStr; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(JidPrepQuery, 12); + assert_size!(JidPrepResponse, 40); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(JidPrepQuery, 24); From 89d24de30234bffd175e6768f780f64b9a240a48 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 20 Sep 2019 01:41:30 +0200 Subject: [PATCH 678/698] receipts: Update to XEP-0184 version 1.4.0. --- doap.xml | 2 +- src/avatar.rs | 1 + src/receipts.rs | 19 ++++++++++++++----- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/doap.xml b/doap.xml index f591bfff5e86ea6bc965e187cf6967b64a0e7e96..3320bfa7c02bb7134498891960886ebf2fb2afc8 100644 --- a/doap.xml +++ b/doap.xml @@ -235,7 +235,7 @@ complete - 1.3.0 + 1.4.0 0.1.0 diff --git a/src/avatar.rs b/src/avatar.rs index e5c09d2a14957737c50a266ced9db2d1f25be35f..0940c33a41ce68a2d13c9e3b63a84ef9113b47d9 100644 --- a/src/avatar.rs +++ b/src/avatar.rs @@ -58,6 +58,7 @@ impl PubSubPayload for Data {} mod tests { use super::*; use crate::hashes::Algo; + #[cfg(not(feature = "disable-validation"))] use crate::util::error::Error; use minidom::Element; use std::convert::TryFrom; diff --git a/src/receipts.rs b/src/receipts.rs index 3b54d3cddb1663a8c4084f15493c29ae12667393..af40f641e605088bcefd14fbb938f7c5eba8ddd4 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -22,7 +22,7 @@ generate_element!( Received, "received", RECEIPTS, attributes: [ /// The 'id' attribute of the received message. - id: Option = "id", + id: Required = "id", ] ); @@ -34,6 +34,7 @@ mod tests { use crate::ns; use minidom::Element; use std::convert::TryFrom; + use crate::util::error::Error; #[cfg(target_pointer_width = "32")] #[test] @@ -54,15 +55,23 @@ mod tests { let elem: Element = "".parse().unwrap(); Request::try_from(elem).unwrap(); - let elem: Element = "".parse().unwrap(); - Received::try_from(elem).unwrap(); - let elem: Element = "" .parse() .unwrap(); Received::try_from(elem).unwrap(); } + #[test] + fn test_missing_id() { + let elem: Element = "".parse().unwrap(); + let error = Received::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'id' missing."); + } + #[test] fn test_serialise() { let receipt = Request; @@ -71,7 +80,7 @@ mod tests { assert_eq!(elem.attrs().count(), 0); let receipt = Received { - id: Some(String::from("coucou")), + id: String::from("coucou"), }; let elem: Element = receipt.into(); assert!(elem.is("received", ns::RECEIPTS)); From 552fef90d31bfb4c66fa8f2b17aac623926f00f1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 20 Sep 2019 01:46:44 +0200 Subject: [PATCH 679/698] DOAP: Mention that XEP-0068 is supported There is no module for this XEP, but it is only a best practice document about XEP-0004 so there is no need for one. --- doap.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doap.xml b/doap.xml index 3320bfa7c02bb7134498891960886ebf2fb2afc8..e75603dc198d6c2de3d54551d71bf184f28e7f77 100644 --- a/doap.xml +++ b/doap.xml @@ -111,6 +111,15 @@ 0.5.0 + + + + complete + 1.2 + 0.1.0 + there is no specific module for this, the feature is all in the XEP-0004 module + + From d1d98ff3d58109f8ade63abd226994e30c5ef736 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 25 Sep 2019 10:28:44 +0200 Subject: [PATCH 680/698] Use crate::Element instead of minidom::Element. This makes refactoring much easier. --- src/attention.rs | 2 +- src/avatar.rs | 2 +- src/bind.rs | 2 +- src/blocking.rs | 2 +- src/bob.rs | 2 +- src/bookmarks.rs | 2 +- src/caps.rs | 2 +- src/carbons.rs | 2 +- src/cert_management.rs | 2 +- src/chatstates.rs | 2 +- src/component.rs | 2 +- src/csi.rs | 2 +- src/data_forms.rs | 2 +- src/delay.rs | 2 +- src/disco.rs | 2 +- src/ecaps2.rs | 2 +- src/eme.rs | 2 +- src/forwarding.rs | 2 +- src/hashes.rs | 2 +- src/ibb.rs | 2 +- src/ibr.rs | 2 +- src/idle.rs | 2 +- src/iq.rs | 2 +- src/jid_prep.rs | 2 +- src/jingle.rs | 2 +- src/jingle_dtls_srtp.rs | 2 +- src/jingle_ibb.rs | 2 +- src/jingle_ice_udp.rs | 2 +- src/jingle_message.rs | 2 +- src/jingle_rtp.rs | 2 +- src/jingle_s5b.rs | 2 +- src/media_element.rs | 2 +- src/message.rs | 2 +- src/message_correct.rs | 2 +- src/mood.rs | 2 +- src/muc/muc.rs | 2 +- src/muc/user.rs | 2 +- src/nick.rs | 2 +- src/occupant_id.rs | 2 +- src/ping.rs | 2 +- src/pubsub/event.rs | 2 +- src/pubsub/pubsub.rs | 2 +- src/receipts.rs | 2 +- src/roster.rs | 2 +- src/rsm.rs | 2 +- src/sasl.rs | 4 +-- src/sm.rs | 2 +- src/stanza_error.rs | 2 +- src/stanza_id.rs | 2 +- src/stream.rs | 2 +- src/time.rs | 2 +- src/tune.rs | 2 +- src/util/compare_elements.rs | 2 +- src/util/macros.rs | 62 ++++++++++++++++++------------------ src/version.rs | 2 +- src/websocket.rs | 2 +- 56 files changed, 87 insertions(+), 87 deletions(-) diff --git a/src/attention.rs b/src/attention.rs index a2af3a9cd79a9be54cef033e5b7468da397c1ccc..83a871ccfba3a0dc3c31e4e71839b8e157512dde 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -20,7 +20,7 @@ mod tests { use super::*; #[cfg(not(feature = "disable-validation"))] use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[test] diff --git a/src/avatar.rs b/src/avatar.rs index 0940c33a41ce68a2d13c9e3b63a84ef9113b47d9..c475db82ac767caee63f9300a30717d3dfecef66 100644 --- a/src/avatar.rs +++ b/src/avatar.rs @@ -60,7 +60,7 @@ mod tests { use crate::hashes::Algo; #[cfg(not(feature = "disable-validation"))] use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/bind.rs b/src/bind.rs index 16fb487bc7ef00a931d3bcd16a736589c19ec50c..4da3564156abb8abd6025e0fa49fa9442508d11f 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -8,7 +8,7 @@ use crate::util::error::Error; use crate::iq::{IqResultPayload, IqSetPayload}; use crate::ns; use jid::{FullJid, Jid}; -use minidom::Element; +use crate::Element; use std::str::FromStr; use std::convert::TryFrom; diff --git a/src/blocking.rs b/src/blocking.rs index 741d43405412864166729d53f17e0d210ba5cfe6..1ddfa5c9a6d7cddc6212577292308983aefbd009 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -8,7 +8,7 @@ use crate::util::error::Error; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; use jid::Jid; -use minidom::Element; +use crate::Element; use std::convert::TryFrom; generate_empty_element!( diff --git a/src/bob.rs b/src/bob.rs index edb0c89103128ef94f5045c05c9ade37f78f9d74..c78f6c34ed274ecf8c06fe498aa14385cb862b92 100644 --- a/src/bob.rs +++ b/src/bob.rs @@ -88,7 +88,7 @@ generate_element!( #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; use std::error::Error as StdError; diff --git a/src/bookmarks.rs b/src/bookmarks.rs index c00195a526bd6b8568d94fe241e5660916a8d854..900a3a911656feccdd6bce3c556ac9a892eea570 100644 --- a/src/bookmarks.rs +++ b/src/bookmarks.rs @@ -71,7 +71,7 @@ impl Storage { mod tests { use super::*; use crate::util::compare_elements::NamespaceAwareCompare; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/caps.rs b/src/caps.rs index 32f8f5735edd94fad2c3cb6e217bce745f264c50..96ffa22adefe47fcf24891c7df2aec091ab14cba 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -12,7 +12,7 @@ use crate::ns; use crate::presence::PresencePayload; use blake2::VarBlake2b; use digest::{Digest, Input, VariableOutput}; -use minidom::Element; +use crate::Element; use sha1::Sha1; use sha2::{Sha256, Sha512}; use sha3::{Sha3_256, Sha3_512}; diff --git a/src/carbons.rs b/src/carbons.rs index b40c1e2e7563c8b1f02e4c3311079c7567cb619e..43047c2b27f511fa8b05e55e69714deeabb868f6 100644 --- a/src/carbons.rs +++ b/src/carbons.rs @@ -63,7 +63,7 @@ impl MessagePayload for Sent {} #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/cert_management.rs b/src/cert_management.rs index 7ed5ca7a4f461bf90cdc9814a2667006cd428cd6..817a01bb284fcdb81cff9a2e36a6e8ec333aa79d 100644 --- a/src/cert_management.rs +++ b/src/cert_management.rs @@ -113,7 +113,7 @@ impl IqSetPayload for Revoke {} #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; use std::str::FromStr; use crate::ns; diff --git a/src/chatstates.rs b/src/chatstates.rs index d7ae1e37d8a6dbe94bdf41b76ae6351a4da26b1b..4d8f9a4dbb7edcf17cb0919a6ee785407eb00359 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -34,7 +34,7 @@ mod tests { use super::*; use crate::util::error::Error; use crate::ns; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[test] diff --git a/src/component.rs b/src/component.rs index a0588177fecc45c3c133b319ea4d2fb8238bdcef..b8120ffde74e18f3ae3120489090525d6adfe6f4 100644 --- a/src/component.rs +++ b/src/component.rs @@ -43,7 +43,7 @@ impl Handshake { #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/csi.rs b/src/csi.rs index 0465f94b2014f3d98e050a069052414e3d8647ff..f3a9130d4afca79f24f772517dbbead2de9ef501 100644 --- a/src/csi.rs +++ b/src/csi.rs @@ -22,7 +22,7 @@ generate_empty_element!( #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; use crate::ns; diff --git a/src/data_forms.rs b/src/data_forms.rs index 80c9131b32da9798eba8ce64d57505ab9500b5db..37f94dfa541e068694b8830b45eeddab99ddaff0 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -7,7 +7,7 @@ use crate::util::error::Error; use crate::media_element::MediaElement; use crate::ns; -use minidom::Element; +use crate::Element; use std::convert::TryFrom; generate_element!( diff --git a/src/delay.rs b/src/delay.rs index e987a4f0e013352c8221ce586f4c67fc49d74df9..e7c7449a73d3e4fafc3832439fbaf520770828ca 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -33,7 +33,7 @@ impl PresencePayload for Delay {} mod tests { use super::*; use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::str::FromStr; use std::convert::TryFrom; diff --git a/src/disco.rs b/src/disco.rs index 857afaf8c758de96eac503d50249a572475375f5..040ce8c42e9b4d94f242d09092240b3c80f9ca67 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -9,7 +9,7 @@ use crate::util::error::Error; use crate::iq::{IqGetPayload, IqResultPayload}; use crate::ns; use jid::Jid; -use minidom::Element; +use crate::Element; use std::convert::TryFrom; generate_element!( diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 8722d065050bca5c65d33291c20db39da6c4c962..cfcb340f34a1891465f015736174104d6c51beb8 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -174,7 +174,7 @@ pub fn query_ecaps2(hash: Hash) -> DiscoInfoQuery { mod tests { use super::*; use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/eme.rs b/src/eme.rs index 623a0cd1eb1378c6b24a9fb55e15eb72ee633167..1014318e36eceb79dd3ef0313969aa7d3102596a 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -25,7 +25,7 @@ impl MessagePayload for ExplicitMessageEncryption {} mod tests { use super::*; use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/forwarding.rs b/src/forwarding.rs index 5286323cfe1bcb21228b886c230acd69f8ca7436..6517d22f9f280d27fb4d339d99312a11efca36df 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -27,7 +27,7 @@ generate_element!( mod tests { use super::*; use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/hashes.rs b/src/hashes.rs index 9987407a694fb736b4f783606b43e4a1fe6d2586..ae7b61bc2585efc6199ed1b5dc48f8c2c936a498 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -191,7 +191,7 @@ impl Deref for Sha1HexAttribute { #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/ibb.rs b/src/ibb.rs index 8a81b651826903733a271e3aab7c9953302ace4d..719fd8537e66390ba8722dc8c9edaafecad4c6ec 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -73,7 +73,7 @@ impl IqSetPayload for Close {} mod tests { use super::*; use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::error::Error as StdError; use std::convert::TryFrom; diff --git a/src/ibr.rs b/src/ibr.rs index 8ced963f259e0c27375efbd4875b7e38d23b354a..b1b15286653395d7b63bb619d342f91ecd3a2bd8 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -8,7 +8,7 @@ use crate::data_forms::DataForm; use crate::util::error::Error; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; -use minidom::Element; +use crate::Element; use std::collections::HashMap; use std::convert::TryFrom; diff --git a/src/idle.rs b/src/idle.rs index cba3698b4db829c20cc6afd7232cd7cef1d7e07b..47d1c18335c7627c7f20449b21eba42cd491927d 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -22,7 +22,7 @@ impl PresencePayload for Idle {} mod tests { use super::*; use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::error::Error as StdError; use std::str::FromStr; use std::convert::TryFrom; diff --git a/src/iq.rs b/src/iq.rs index f3aa7f5d17a8f2f477b4b8ed79bdbdc53d6d876b..644926163157e0e0dedea535fe9adbb5c7db5023 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -9,7 +9,7 @@ use crate::util::error::Error; use crate::ns; use crate::stanza_error::StanzaError; use jid::Jid; -use minidom::Element; +use crate::Element; use minidom::IntoAttributeValue; use std::convert::TryFrom; diff --git a/src/jid_prep.rs b/src/jid_prep.rs index ba8c9d84e18a28a3ade067108b53765af97bc84a..5fbc0a0c0f3f7ab29bf42f978cd329ab248af273 100644 --- a/src/jid_prep.rs +++ b/src/jid_prep.rs @@ -42,7 +42,7 @@ impl IqResultPayload for JidPrepResponse {} #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; use std::str::FromStr; diff --git a/src/jingle.rs b/src/jingle.rs index 7e27ebbc6efe2ff73d628fdbcc4aedb5819db276..4c568bfc55e683fb449cf6a65146829641b882d2 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -8,7 +8,7 @@ use crate::util::error::Error; use crate::iq::IqSetPayload; use crate::ns; use jid::Jid; -use minidom::Element; +use crate::Element; use std::collections::BTreeMap; use std::str::FromStr; use std::convert::TryFrom; diff --git a/src/jingle_dtls_srtp.rs b/src/jingle_dtls_srtp.rs index e65c368170a229208e130dc0a9f4d8b4fb63449c..150388f6eac7f3128f079f376964c725b5d42911 100644 --- a/src/jingle_dtls_srtp.rs +++ b/src/jingle_dtls_srtp.rs @@ -49,7 +49,7 @@ generate_element!( #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index d1fe61f9d295271c6582a3285f3e178f32cccb70..f0fb90f0f8a12b43758d046b8f1cf7933fa8e99d 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -25,7 +25,7 @@ attributes: [ mod tests { use super::*; use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::error::Error as StdError; use std::convert::TryFrom; diff --git a/src/jingle_ice_udp.rs b/src/jingle_ice_udp.rs index 323014380d89a17a26d30390ae96e337e58d075c..51a725ee705ed6b0598e7b6a862a4973bb669950 100644 --- a/src/jingle_ice_udp.rs +++ b/src/jingle_ice_udp.rs @@ -91,7 +91,7 @@ generate_element!( #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; use crate::hashes::Algo; use crate::jingle_dtls_srtp::Setup; diff --git a/src/jingle_message.rs b/src/jingle_message.rs index bf0478cf4801aed8018eae1a115a7069ac3626a1..78bf1db22ca38112eae8b3a363ce9563e8515b9d 100644 --- a/src/jingle_message.rs +++ b/src/jingle_message.rs @@ -7,7 +7,7 @@ use crate::util::error::Error; use crate::jingle::SessionId; use crate::ns; -use minidom::Element; +use crate::Element; use std::convert::TryFrom; /// Defines a protocol for broadcasting Jingle requests to all of the clients diff --git a/src/jingle_rtp.rs b/src/jingle_rtp.rs index 108010d00404ae87fdc3ccbf4086c146ed6660da..5e686ca965eab32e60edaa0391896cc4b515e88a 100644 --- a/src/jingle_rtp.rs +++ b/src/jingle_rtp.rs @@ -75,7 +75,7 @@ generate_element!( #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 17c5d996fb3bb7070cf8f3bf5d442d310883deb4..b6df19ed6d835abb8f7b5e4779366b9ea715d2c9 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -7,7 +7,7 @@ use crate::util::error::Error; use crate::ns; use jid::Jid; -use minidom::Element; +use crate::Element; use std::net::IpAddr; use std::convert::TryFrom; diff --git a/src/media_element.rs b/src/media_element.rs index f632e48c13428c4e722e9871a62019f07d3f2b9b..099dadae5ab37387540ca22170afbec6d5f7eeaa 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -47,7 +47,7 @@ mod tests { use super::*; use crate::data_forms::DataForm; use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::error::Error as StdError; use std::convert::TryFrom; diff --git a/src/message.rs b/src/message.rs index 4a78c6c0b7794e1cda6738af6739718031acd9e0..2c7eb29946abca98cfa5926ab459a109c78e9a78 100644 --- a/src/message.rs +++ b/src/message.rs @@ -7,7 +7,7 @@ use crate::util::error::Error; use crate::ns; use jid::Jid; -use minidom::Element; +use crate::Element; use std::collections::BTreeMap; use std::convert::TryFrom; diff --git a/src/message_correct.rs b/src/message_correct.rs index 1055af9bd261bc0e3a9cb0b72c0a5996aab92fed..374900f41304c35e879e6d5980a9aa767d20fedf 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -22,7 +22,7 @@ impl MessagePayload for Replace {} mod tests { use super::*; use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/mood.rs b/src/mood.rs index f4b529c592ba6e7719e261138c03a735c1d2ff04..4270c5889642bbce1c2f2b631175615a38e14586 100644 --- a/src/mood.rs +++ b/src/mood.rs @@ -271,7 +271,7 @@ generate_elem_id!( #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/muc/muc.rs b/src/muc/muc.rs index a4d13f8a90a082740069319f6d1e92a041253c52..01688ec3623c4a073a8482fcd7592e520695c3fd 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -96,7 +96,7 @@ mod tests { use super::*; use crate::util::compare_elements::NamespaceAwareCompare; use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::str::FromStr; use std::convert::TryFrom; diff --git a/src/muc/user.rs b/src/muc/user.rs index d785d2356cd208adc2a4a22d0a363fb1a66f5128..31157f7681d7b367f98336d24ec11344cb27e93d 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -8,7 +8,7 @@ use crate::util::error::Error; use crate::ns; use jid::FullJid; -use minidom::Element; +use crate::Element; use std::convert::TryFrom; generate_attribute_enum!( diff --git a/src/nick.rs b/src/nick.rs index 07f07f770125550a41cbd5e53674ff842f8586c1..20ae7a94c9cf03eb34751f4b87c2647af521fe2e 100644 --- a/src/nick.rs +++ b/src/nick.rs @@ -16,7 +16,7 @@ mod tests { use super::*; #[cfg(not(feature = "disable-validation"))] use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/occupant_id.rs b/src/occupant_id.rs index 730652942d9073973f3e7d149796e5155a6b5a7d..418a878bd5729fb44d0d8b266cb32a8083aee383 100644 --- a/src/occupant_id.rs +++ b/src/occupant_id.rs @@ -27,7 +27,7 @@ impl PresencePayload for OccupantId {} mod tests { use super::*; use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/ping.rs b/src/ping.rs index be8ac48c2be8f2ef4625bc683fd89428135dda7e..70663fa3a79d483da66f040b3b2419c4245272f6 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -22,7 +22,7 @@ mod tests { use super::*; #[cfg(not(feature = "disable-validation"))] use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[test] diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 30c118de688830d9b8924de15c7a97a42c7274b0..2228a067dd1ea49af5b73598414e3d3d72a5f5f3 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -10,7 +10,7 @@ use crate::util::error::Error; use crate::ns; use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId, Item as PubSubItem}; use jid::Jid; -use minidom::Element; +use crate::Element; use std::convert::TryFrom; /// Event wrapper for a PubSub ``. diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index 875c1989538364ba805e8fac39ffcb8fe525f13f..8faf9c433c2dcece35c7af4c80a5f02a63b8eb98 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -10,7 +10,7 @@ use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; use crate::pubsub::{NodeName, Subscription, SubscriptionId, Item as PubSubItem}; use jid::Jid; -use minidom::Element; +use crate::Element; use std::convert::TryFrom; // TODO: a better solution would be to split this into a query and a result elements, like for diff --git a/src/receipts.rs b/src/receipts.rs index af40f641e605088bcefd14fbb938f7c5eba8ddd4..e837a17fdcedf984266206c513561f5433dc4847 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -32,7 +32,7 @@ impl MessagePayload for Received {} mod tests { use super::*; use crate::ns; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; use crate::util::error::Error; diff --git a/src/roster.rs b/src/roster.rs index 6c871ba651d84cfaf9f22668143c68ed732e3219..730da52df65f0b2f3d2e0a0ae9f9c607e3f1caa0 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -94,7 +94,7 @@ mod tests { use super::*; use crate::util::compare_elements::NamespaceAwareCompare; use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::str::FromStr; use std::convert::TryFrom; diff --git a/src/rsm.rs b/src/rsm.rs index 8299c9b67132b27d50e43d6df1222d5cc20facf3..169681d1e911f47aed85802c0cae31aaf92e12b0 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -6,7 +6,7 @@ use crate::util::error::Error; use crate::ns; -use minidom::Element; +use crate::Element; use std::convert::TryFrom; /// Requests paging through a potentially big set of items (represented by an diff --git a/src/sasl.rs b/src/sasl.rs index e8f3aa6e041727102c112b3ea28ddd9f0130837b..e3f0900576adf0f29af36d679f68e398a16283bd 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -7,7 +7,7 @@ use crate::util::error::Error; use crate::util::helpers::Base64; use crate::ns; -use minidom::Element; +use crate::Element; use std::collections::BTreeMap; use std::convert::TryFrom; @@ -221,7 +221,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/sm.rs b/src/sm.rs index b483514023031ca34bdf02fe07ddf1c75daacdfb..a78320f22c54b2a33d64f1be1646a67cd5e580d6 100644 --- a/src/sm.rs +++ b/src/sm.rs @@ -145,7 +145,7 @@ generate_empty_element!( #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 1f4dbbe16de934e405af539040c7231763341bb5..0bdc2887f1c36a55dc62a7dba7927ee64cf7cdb7 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -9,7 +9,7 @@ use crate::message::MessagePayload; use crate::ns; use crate::presence::PresencePayload; use jid::Jid; -use minidom::Element; +use crate::Element; use std::collections::BTreeMap; use std::convert::TryFrom; diff --git a/src/stanza_id.rs b/src/stanza_id.rs index bbbfb48f2c5bfbfef5f0f4baba3946c3b1d522e0..1ba48f85da115c69d93a790b021b0eee6fd1e418 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -38,7 +38,7 @@ impl MessagePayload for OriginId {} mod tests { use super::*; use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::str::FromStr; use std::convert::TryFrom; diff --git a/src/stream.rs b/src/stream.rs index e6f5be009b0dfce49565f359c4b193f4dd43c589..c1b5e1ea6276aedb5523e79e48f345ce5c284ef7 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -73,7 +73,7 @@ impl Stream { #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/time.rs b/src/time.rs index 601b3a4cd0032a323c90664f9c554328f24e8746..363cd210890eb6de2f0427ad02e4058c17d56fe5 100644 --- a/src/time.rs +++ b/src/time.rs @@ -9,7 +9,7 @@ use crate::date::DateTime; use crate::iq::{IqGetPayload, IqResultPayload}; use crate::ns; use crate::util::error::Error; -use minidom::Element; +use crate::Element; use std::convert::TryFrom; use std::str::FromStr; diff --git a/src/tune.rs b/src/tune.rs index df9b6679865114fffb7c652eb93d28666c3f78b2..ed35357d8bef5e9b4e7fdd7854e6de13bd516e1a 100644 --- a/src/tune.rs +++ b/src/tune.rs @@ -7,7 +7,7 @@ use crate::util::error::Error; use crate::pubsub::PubSubPayload; use crate::ns; -use minidom::Element; +use crate::Element; use std::convert::TryFrom; generate_elem_id!( diff --git a/src/util/compare_elements.rs b/src/util/compare_elements.rs index df2e4157cc2930f7bc6625280e352407bbe532ae..7494be7bf2fd14a724bb0e683227b143840c8328 100644 --- a/src/util/compare_elements.rs +++ b/src/util/compare_elements.rs @@ -51,7 +51,7 @@ impl NamespaceAwareCompare for Element { #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; #[test] fn simple() { diff --git a/src/util/macros.rs b/src/util/macros.rs index fd7f58cc649c2ff3ef5352ff5e6a3a047edb395d..107083a4b9ad7687d13af85de235324ea8b96416 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -230,9 +230,9 @@ macro_rules! generate_element_enum { $enum ),+ } - impl ::std::convert::TryFrom<::minidom::Element> for $elem { + impl ::std::convert::TryFrom for $elem { type Error = crate::util::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { + fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); @@ -242,9 +242,9 @@ macro_rules! generate_element_enum { }) } } - impl From<$elem> for ::minidom::Element { - fn from(elem: $elem) -> ::minidom::Element { - ::minidom::Element::builder( + impl From<$elem> for crate::Element { + fn from(elem: $elem) -> crate::Element { + crate::Element::builder( match elem { $($elem::$enum => $enum_name,)+ } @@ -274,9 +274,9 @@ macro_rules! generate_attribute_enum { $enum ),+ } - impl ::std::convert::TryFrom<::minidom::Element> for $elem { + impl ::std::convert::TryFrom for $elem { type Error = crate::util::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { + fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); check_no_unknown_attributes!(elem, $name, [$attr]); @@ -286,9 +286,9 @@ macro_rules! generate_attribute_enum { }) } } - impl From<$elem> for ::minidom::Element { - fn from(elem: $elem) -> ::minidom::Element { - ::minidom::Element::builder($name) + impl From<$elem> for crate::Element { + fn from(elem: $elem) -> crate::Element { + crate::Element::builder($name) .ns(crate::ns::$ns) .attr($attr, match elem { $($elem::$enum => $enum_name,)+ @@ -377,10 +377,10 @@ macro_rules! generate_empty_element { #[derive(Debug, Clone)] pub struct $elem; - impl ::std::convert::TryFrom<::minidom::Element> for $elem { + impl ::std::convert::TryFrom for $elem { type Error = crate::util::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { + fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); @@ -388,9 +388,9 @@ macro_rules! generate_empty_element { } } - impl From<$elem> for ::minidom::Element { - fn from(_: $elem) -> ::minidom::Element { - ::minidom::Element::builder($name) + impl From<$elem> for crate::Element { + fn from(_: $elem) -> crate::Element { + crate::Element::builder($name) .ns(crate::ns::$ns) .build() } @@ -439,9 +439,9 @@ macro_rules! generate_elem_id { $(#[$meta])* #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $elem(pub $type); - impl ::std::convert::TryFrom<::minidom::Element> for $elem { + impl ::std::convert::TryFrom for $elem { type Error = crate::util::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { + fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); @@ -449,9 +449,9 @@ macro_rules! generate_elem_id { Ok($elem(elem.text().parse()?)) } } - impl From<$elem> for ::minidom::Element { - fn from(elem: $elem) -> ::minidom::Element { - ::minidom::Element::builder($name) + impl From<$elem> for crate::Element { + fn from(elem: $elem) -> crate::Element { + crate::Element::builder($name) .ns(crate::ns::$ns) .append(elem.0.to_string()) .build() @@ -592,14 +592,14 @@ macro_rules! finish_parse_elem { macro_rules! generate_serialiser { ($builder:ident, $parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => { $builder.append( - ::minidom::Element::builder($name) + crate::Element::builder($name) .ns(crate::ns::$ns) .append(::minidom::Node::Text($parent.$elem)) ) }; ($builder:ident, $parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => { $builder.append_all($parent.$elem.map(|elem| { - ::minidom::Element::builder($name) + crate::Element::builder($name) .ns(crate::ns::$ns) .append(::minidom::Node::Text(elem)) }) @@ -607,9 +607,9 @@ macro_rules! generate_serialiser { }; ($builder:ident, $parent:ident, $elem:ident, Option, $constructor:ident, ($name:tt, $ns:ident)) => { $builder.append_all($parent.$elem.map(|elem| { - ::minidom::Element::builder($name) + crate::Element::builder($name) .ns(crate::ns::$ns) - .append(::minidom::Node::Element(::minidom::Element::from(elem))) + .append(::minidom::Node::Element(crate::Element::from(elem))) }) ) }; @@ -617,10 +617,10 @@ macro_rules! generate_serialiser { $builder.append_all($parent.$elem.into_iter()) }; ($builder:ident, $parent:ident, $elem:ident, Present, $constructor:ident, ($name:tt, $ns:ident)) => { - $builder.append(::minidom::Node::Element(::minidom::Element::builder($name).ns(crate::ns::$ns).build())) + $builder.append(::minidom::Node::Element(crate::Element::builder($name).ns(crate::ns::$ns).build())) }; ($builder:ident, $parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => { - $builder.append(::minidom::Node::Element(::minidom::Element::from($parent.$elem))) + $builder.append(::minidom::Node::Element(crate::Element::from($parent.$elem))) }; } @@ -661,10 +661,10 @@ macro_rules! generate_element { )* } - impl ::std::convert::TryFrom<::minidom::Element> for $elem { + impl ::std::convert::TryFrom for $elem { type Error = crate::util::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { + fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> { check_self!(elem, $name, $ns); check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); $( @@ -693,9 +693,9 @@ macro_rules! generate_element { } } - impl From<$elem> for ::minidom::Element { - fn from(elem: $elem) -> ::minidom::Element { - let mut builder = ::minidom::Element::builder($name) + impl From<$elem> for crate::Element { + fn from(elem: $elem) -> crate::Element { + let mut builder = crate::Element::builder($name) .ns(crate::ns::$ns); $( builder = builder.attr($attr_name, elem.$attr); diff --git a/src/version.rs b/src/version.rs index afc617897adf033e0dfd46865165e04af4e551f2..028ce968a2bc70e7a5bf168366958d04109b0061 100644 --- a/src/version.rs +++ b/src/version.rs @@ -42,7 +42,7 @@ impl IqResultPayload for VersionResult {} mod tests { use super::*; use crate::util::compare_elements::NamespaceAwareCompare; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/websocket.rs b/src/websocket.rs index 1092423a10cddb9612823af7865ff81d62774b97..fbc1fb2b7aa983210195e1f4d439b0efb2332020 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -72,7 +72,7 @@ impl Open { #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] From 2eb521fa023383e46e1621e9e7d205f975b390e1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 29 Sep 2019 01:47:21 +0200 Subject: [PATCH 681/698] Add a Bookmarks 2 (This Time it's Serious) parser. --- ChangeLog | 1 + doap.xml | 8 ++++ src/bookmarks2.rs | 119 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 ++ src/ns.rs | 5 ++ 5 files changed, 136 insertions(+) create mode 100644 src/bookmarks2.rs diff --git a/ChangeLog b/ChangeLog index 381e8933ae03738206b530c64882c47642aeb9db..182d559713bd5acc6bfdfbb43d7a8279c1ca78f6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,6 +5,7 @@ DATE Emmanuel Gil Peyrot - JID Prep (XEP-0328) - Client State Indication (XEP-0352) - OpenPGP for XMPP (XEP-0373) + - Bookmarks 2 (This Time it's Serious) (XEP-0402) - Anonymous unique occupant identifiers for MUCs (XEP-0421) * Breaking changes: - Presence constructors now take Into and assume Some. diff --git a/doap.xml b/doap.xml index e75603dc198d6c2de3d54551d71bf184f28e7f77..40b28e3bd08a5a950638c2ea27e0e4daf5d74c18 100644 --- a/doap.xml +++ b/doap.xml @@ -465,6 +465,14 @@ 0.1.0 + + + + complete + 0.3.0 + NEXT + + diff --git a/src/bookmarks2.rs b/src/bookmarks2.rs new file mode 100644 index 0000000000000000000000000000000000000000..2a182f12a613dfcd9580e4490bf3a07df17959d3 --- /dev/null +++ b/src/bookmarks2.rs @@ -0,0 +1,119 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +generate_attribute!( + /// Whether a conference bookmark should be joined automatically. + Autojoin, + "autojoin", + bool +); + +generate_element!( + /// A conference bookmark. + Conference, "conference", BOOKMARKS2, + attributes: [ + /// Whether a conference bookmark should be joined automatically. + autojoin: Default = "autojoin", + + /// A user-defined name for this conference. + name: Option = "name", + ], + children: [ + /// The nick the user will use to join this conference. + nick: Option = ("nick", BOOKMARKS2) => String, + + /// The password required to join this conference. + password: Option = ("password", BOOKMARKS2) => String + ] +); + +impl Conference { + /// Create a new conference. + pub fn new() -> Conference { + Conference { + autojoin: Autojoin::False, + name: None, + nick: None, + password: None, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::util::compare_elements::NamespaceAwareCompare; + use crate::Element; + use std::convert::TryFrom; + use crate::pubsub::pubsub::Item as PubSubItem; + use crate::pubsub::event::PubSubEvent; + use crate::ns; + + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Conference, 40); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(Conference, 80); + } + + #[test] + fn simple() { + let elem: Element = "".parse().unwrap(); + let elem1 = elem.clone(); + let conference = Conference::try_from(elem).unwrap(); + assert_eq!(conference.autojoin, Autojoin::False); + assert_eq!(conference.name, None); + assert_eq!(conference.nick, None); + assert_eq!(conference.password, None); + + let elem2 = Element::from(Conference::new()); + assert!(elem1.compare_to(&elem2)); + } + + #[test] + fn complete() { + let elem: Element = "Coucousecret".parse().unwrap(); + let conference = Conference::try_from(elem).unwrap(); + assert_eq!(conference.autojoin, Autojoin::True); + assert_eq!(conference.name, Some(String::from("Test MUC"))); + assert_eq!(conference.clone().nick.unwrap(), "Coucou"); + assert_eq!(conference.clone().password.unwrap(), "secret"); + } + + #[test] + fn wrapped() { + let elem: Element = "Coucousecret".parse().unwrap(); + let item = PubSubItem::try_from(elem).unwrap(); + let payload = item.payload.clone().unwrap(); + let conference = Conference::try_from(payload).unwrap(); + assert_eq!(conference.autojoin, Autojoin::True); + assert_eq!(conference.name, Some(String::from("Test MUC"))); + assert_eq!(conference.clone().nick.unwrap(), "Coucou"); + assert_eq!(conference.clone().password.unwrap(), "secret"); + + let elem: Element = "Coucousecret".parse().unwrap(); + let mut items = match PubSubEvent::try_from(elem) { + Ok(PubSubEvent::PublishedItems { node, items }) => { + assert_eq!(&node.0, ns::BOOKMARKS2); + items + }, + _ => panic!(), + }; + assert_eq!(items.len(), 1); + let item = items.pop().unwrap(); + let payload = item.payload.clone().unwrap(); + let conference = Conference::try_from(payload).unwrap(); + assert_eq!(conference.autojoin, Autojoin::True); + assert_eq!(conference.name, Some(String::from("Test MUC"))); + assert_eq!(conference.clone().nick.unwrap(), "Coucou"); + assert_eq!(conference.clone().password.unwrap(), "secret"); + } +} diff --git a/src/lib.rs b/src/lib.rs index b69e56ad4408e19f4542926a5725394ab6a3a867..2f856e0a27d6d56e80d8ff5961742e741900a149 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -201,5 +201,8 @@ pub mod eme; /// XEP-0390: Entity Capabilities 2.0 pub mod ecaps2; +/// XEP-0402: Bookmarks 2 (This Time it's Serious) +pub mod bookmarks2; + /// XEP-0421: Anonymous unique occupant identifiers for MUCs pub mod occupant_id; diff --git a/src/ns.rs b/src/ns.rs index 70d68cff156aff8aebddb24e91b02c2559caf018..5aea7a65b56a7069f3a1a35b074a9a789e05ed3a 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -210,6 +210,11 @@ pub const ECAPS2: &str = "urn:xmpp:caps"; /// XEP-0390: Entity Capabilities 2.0 pub const ECAPS2_OPTIMIZE: &str = "urn:xmpp:caps:optimize"; +/// XEP-0402: Bookmarks 2 (This Time it's Serious) +pub const BOOKMARKS2: &str = "urn:xmpp:bookmarks:0"; +/// XEP-0402: Bookmarks 2 (This Time it's Serious) +pub const BOOKMARKS2_COMPAT: &str = "urn:xmpp:bookmarks:0#compat"; + /// XEP-0421: Anonymous unique occupant identifiers for MUCs pub const OID: &str = "urn:xmpp:occupant-id:0"; From fcdffba31cd1de76fdb6c66c0ec421f57fd711e7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 Oct 2019 17:10:36 +0200 Subject: [PATCH 682/698] macros: Add support for wildcard namespaces. --- src/util/macros.rs | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/util/macros.rs b/src/util/macros.rs index 107083a4b9ad7687d13af85de235324ea8b96416..d63591aa6964e9078f92448f9349c926cbfa2b25 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -605,6 +605,14 @@ macro_rules! generate_serialiser { }) ) }; + ($builder:ident, $parent:ident, $elem:ident, Option, $constructor:ident, ($name:tt, *)) => { + $builder.append_all($parent.$elem.map(|elem| { + crate::Element::builder($name) + .ns(elem.get_ns()) + .append(::minidom::Node::Element(crate::Element::from(elem))) + }) + ) + }; ($builder:ident, $parent:ident, $elem:ident, Option, $constructor:ident, ($name:tt, $ns:ident)) => { $builder.append_all($parent.$elem.map(|elem| { crate::Element::builder($name) @@ -624,6 +632,15 @@ macro_rules! generate_serialiser { }; } +macro_rules! generate_child_test { + ($child:ident, $name:tt, *) => { + true + }; + ($child:ident, $name:tt, $ns:tt) => { + $child.is($name, crate::ns::$ns) + }; +} + macro_rules! generate_element { ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),+,]) => ( generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: []); @@ -631,10 +648,10 @@ macro_rules! generate_element { ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),+]) => ( generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: []); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:tt) => $child_constructor:ident),*]) => ( generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:tt) => $child_constructor:ident),*]) => ( generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]); ); ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >)) => ( @@ -643,7 +660,7 @@ macro_rules! generate_element { ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),+], text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >)) => ( generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: [], text: ($(#[$text_meta])* $text_ident: $codec<$text_type>)); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*] $(, text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >))*) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:tt) => $child_constructor:ident),*] $(, text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >))*) => ( $(#[$meta])* #[derive(Debug, Clone)] pub struct $elem { @@ -672,7 +689,7 @@ macro_rules! generate_element { )* for _child in elem.children() { $( - if _child.is($child_name, crate::ns::$child_ns) { + if generate_child_test!(_child, $child_name, $child_ns) { do_parse_elem!($child_ident: $coucou = $child_constructor => _child, $child_name, $name); continue; } From 7f8cdc5bf0395353066059e3b56f7b3c6c5aebed Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 Oct 2019 17:11:34 +0200 Subject: [PATCH 683/698] jingle_dtls_srtp: Add constructors from Hash and from strings. --- src/hashes.rs | 17 +++++++++++++++-- src/jingle_dtls_srtp.rs | 21 ++++++++++++++++++++- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/hashes.rs b/src/hashes.rs index ae7b61bc2585efc6199ed1b5dc48f8c2c936a498..56b61355507471a549f96caf3125910009b5d307 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -131,6 +131,19 @@ impl Hash { Ok(Hash::new(algo, bytes)) } + /// Like [new](#method.new) but takes hex-encoded data before decoding it. + pub fn from_colon_separated_hex(algo: Algo, hex: &str) -> Result { + let mut bytes = vec![]; + for i in 0..(1 + hex.len()) / 3 { + let byte = u8::from_str_radix(&hex[3 * i..3 * i + 2], 16)?; + if 3 * i + 2 < hex.len() { + assert_eq!(&hex[3 * i + 2..3 * i + 3], ":"); + } + bytes.push(byte); + } + Ok(Hash::new(algo, bytes)) + } + /// Formats this hash into base64. pub fn to_base64(&self) -> String { base64::encode(&self.hash[..]) @@ -146,7 +159,7 @@ impl Hash { } /// Formats this hash into colon-separated hexadecimal. - pub fn to_colon_hex(&self) -> String { + pub fn to_colon_separated_hex(&self) -> String { let mut bytes = vec![]; for byte in self.hash.iter() { bytes.push(format!("{:02x}", byte)); @@ -225,7 +238,7 @@ mod tests { let hash = Hash::try_from(elem).unwrap(); assert_eq!(hash.to_base64(), "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU="); assert_eq!(hash.to_hex(), "d976ab9b04e53710c0324bf29a5a17dd2e7e55bca536b26dfe5e50c8f6be6285"); - assert_eq!(hash.to_colon_hex(), "d9:76:ab:9b:04:e5:37:10:c0:32:4b:f2:9a:5a:17:dd:2e:7e:55:bc:a5:36:b2:6d:fe:5e:50:c8:f6:be:62:85"); + assert_eq!(hash.to_colon_separated_hex(), "d9:76:ab:9b:04:e5:37:10:c0:32:4b:f2:9a:5a:17:dd:2e:7e:55:bc:a5:36:b2:6d:fe:5e:50:c8:f6:be:62:85"); } #[test] diff --git a/src/jingle_dtls_srtp.rs b/src/jingle_dtls_srtp.rs index 150388f6eac7f3128f079f376964c725b5d42911..e6a3ee235730fa3c102e41064c6b07a87764da0f 100644 --- a/src/jingle_dtls_srtp.rs +++ b/src/jingle_dtls_srtp.rs @@ -5,7 +5,8 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::util::helpers::ColonSeparatedHex; -use crate::hashes::Algo; +use crate::util::error::Error; +use crate::hashes::{Hash, Algo}; generate_attribute!( /// Indicates which of the end points should initiate the TCP connection establishment. @@ -46,6 +47,24 @@ generate_element!( ) ); +impl Fingerprint { + /// Create a new Fingerprint from a Setup and a Hash. + pub fn from_hash(setup: Setup, hash: Hash) -> Fingerprint { + Fingerprint { + hash: hash.algo, + setup, + value: hash.hash, + } + } + + /// Create a new Fingerprint from a Setup and parsing the hash. + pub fn from_colon_separated_hex(setup: Setup, algo: &str, hash: &str) -> Result { + let algo = algo.parse()?; + let hash = Hash::from_colon_separated_hex(algo, hash)?; + Ok(Fingerprint::from_hash(setup, hash)) + } +} + #[cfg(test)] mod tests { use super::*; From b91e5bdc4e7f3b0135273f653b346e95eb47d232 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 Oct 2019 17:13:02 +0200 Subject: [PATCH 684/698] jingle_ice_udp: Add a constructor for Transport. --- src/jingle_ice_udp.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/jingle_ice_udp.rs b/src/jingle_ice_udp.rs index 51a725ee705ed6b0598e7b6a862a4973bb669950..be5dbc97e1af4c1a849c233f0f58481038b51045 100644 --- a/src/jingle_ice_udp.rs +++ b/src/jingle_ice_udp.rs @@ -26,6 +26,18 @@ generate_element!( ] ); +impl Transport { + /// Create a new ICE-UDP transport. + pub fn new() -> Transport { + Transport { + pwd: None, + ufrag: None, + candidates: Vec::new(), + fingerprint: None, + } + } +} + generate_attribute!( /// A Candidate Type as defined in ICE-CORE. Type, "type", { From 7665f7e5d8577441d078da9bc69989124d531bba Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 Oct 2019 17:13:42 +0200 Subject: [PATCH 685/698] jingle: Wrap all supported and unknown transports in an enum. --- src/jingle.rs | 89 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 82 insertions(+), 7 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index 4c568bfc55e683fb449cf6a65146829641b882d2..fbccce0da23d9d96dfa304f91dca34a90dd8a2e1 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -6,6 +6,9 @@ use crate::util::error::Error; use crate::iq::IqSetPayload; +use crate::jingle_ice_udp::Transport as IceUdpTransport; +use crate::jingle_ibb::Transport as IbbTransport; +use crate::jingle_s5b::Transport as Socks5Transport; use crate::ns; use jid::Jid; use crate::Element; @@ -164,6 +167,78 @@ generate_id!( ContentId ); +/// Enum wrapping all of the various supported transports of a Content. +#[derive(Debug, Clone)] +pub enum Transport { + /// Jingle ICE-UDP Bytestreams (XEP-0176) transport. + IceUdp(IceUdpTransport), + + /// Jingle In-Band Bytestreams (XEP-0261) transport. + Ibb(IbbTransport), + + /// Jingle SOCKS5 Bytestreams (XEP-0260) transport. + Socks5(Socks5Transport), + + /// To be used for any transport that isn’t known at compile-time. + Unknown(Element), +} + +impl TryFrom for Transport { + type Error = Error; + + fn try_from(elem: Element) -> Result { + Ok(if elem.is("transport", ns::JINGLE_ICE_UDP) { + Transport::IceUdp(IceUdpTransport::try_from(elem)?) + } else if elem.is("transport", ns::JINGLE_IBB) { + Transport::Ibb(IbbTransport::try_from(elem)?) + } else if elem.is("transport", ns::JINGLE_S5B) { + Transport::Socks5(Socks5Transport::try_from(elem)?) + } else { + Transport::Unknown(elem) + }) + } +} + +impl From for Transport { + fn from(transport: IceUdpTransport) -> Transport { + Transport::IceUdp(transport) + } +} + +impl From for Transport { + fn from(transport: IbbTransport) -> Transport { + Transport::Ibb(transport) + } +} + +impl From for Transport { + fn from(transport: Socks5Transport) -> Transport { + Transport::Socks5(transport) + } +} + +impl From for Element { + fn from(transport: Transport) -> Element { + match transport { + Transport::IceUdp(transport) => transport.into(), + Transport::Ibb(transport) => transport.into(), + Transport::Socks5(transport) => transport.into(), + Transport::Unknown(elem) => elem, + } + } +} + +impl Transport { + fn get_ns(&self) -> String { + match self { + Transport::IceUdp(_) => String::from(ns::JINGLE_ICE_UDP), + Transport::Ibb(_) => String::from(ns::JINGLE_IBB), + Transport::Socks5(_) => String::from(ns::JINGLE_S5B), + Transport::Unknown(elem) => elem.ns().unwrap_or_else(|| String::new()), + } + } +} + generate_element!( /// Describes a session’s content, there can be multiple content in one /// session. @@ -186,7 +261,7 @@ generate_element!( description: Option = ("description", JINGLE) => Element, /// How to send it. - transport: Option = ("transport", JINGLE) => Element, + transport: Option = ("transport", *) => Transport, /// With which security. security: Option = ("security", JINGLE) => Element @@ -226,8 +301,8 @@ impl Content { } /// Set the transport of this content. - pub fn with_transport(mut self, transport: Element) -> Content { - self.transport = Some(transport); + pub fn with_transport>(mut self, transport: T) -> Content { + self.transport = Some(transport.into()); self } @@ -575,7 +650,7 @@ mod tests { assert_size!(Senders, 1); assert_size!(Disposition, 1); assert_size!(ContentId, 24); - assert_size!(Content, 344); + assert_size!(Content, 384); assert_size!(Reason, 1); assert_size!(ReasonElement, 32); assert_size!(SessionId, 24); @@ -626,18 +701,18 @@ mod tests { #[test] fn test_content() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); let jingle = Jingle::try_from(elem).unwrap(); assert_eq!(jingle.contents[0].creator, Creator::Initiator); assert_eq!(jingle.contents[0].name, ContentId(String::from("coucou"))); assert_eq!(jingle.contents[0].senders, Senders::Both); assert_eq!(jingle.contents[0].disposition, Disposition::Session); - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); let jingle = Jingle::try_from(elem).unwrap(); assert_eq!(jingle.contents[0].senders, Senders::Both); - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); let jingle = Jingle::try_from(elem).unwrap(); assert_eq!(jingle.contents[0].disposition, Disposition::EarlySession); } From 5338cd659909aefa2e3b916abe152c88336258c3 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 Oct 2019 17:14:00 +0200 Subject: [PATCH 686/698] pubsub: Add a constructor for items request. --- src/pubsub/pubsub.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index 8faf9c433c2dcece35c7af4c80a5f02a63b8eb98..39b30c7427ab6abf379d3d880e78cd71826cd927 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -113,6 +113,18 @@ generate_element!( ] ); +impl Items { + /// Create a new items request. + pub fn new(node: &str) -> Items { + Items { + node: NodeName(String::from(node)), + max_items: None, + subid: None, + items: Vec::new(), + } + } +} + /// Response wrapper for a PubSub ``. #[derive(Debug, Clone)] pub struct Item(pub PubSubItem); From d654b1bd309d90d3aece15977e21666ab6235a52 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 Oct 2019 17:16:36 +0200 Subject: [PATCH 687/698] jingle_rtp: Add constructors. --- src/jingle_rtp.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/jingle_rtp.rs b/src/jingle_rtp.rs index 5e686ca965eab32e60edaa0391896cc4b515e88a..7e5557b2f43e098f1b424fe229c2ba4253f35d4a 100644 --- a/src/jingle_rtp.rs +++ b/src/jingle_rtp.rs @@ -24,6 +24,17 @@ generate_element!( ] ); +impl Description { + /// Create a new RTP description. + pub fn new(media: String) -> Description { + Description { + media, + ssrc: None, + payload_types: Vec::new(), + } + } +} + generate_attribute!( /// The number of channels. Channels, "channels", u8, Default = 1 @@ -59,6 +70,21 @@ generate_element!( ] ); +impl PayloadType { + /// Create a new RTP payload-type. + pub fn new(id: u8, name: String) -> PayloadType { + PayloadType { + channels: Default::default(), + clockrate: None, + id, + maxptime: None, + name: Some(name), + ptime: None, + parameters: Vec::new(), + } + } +} + generate_element!( /// Parameter related to a payload. Parameter, "parameter", JINGLE_RTP, From cd32ea4c034d547023a595b6df66535ea35bf10c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 Oct 2019 18:17:04 +0200 Subject: [PATCH 688/698] Add a parser for XEP-0339. --- doap.xml | 8 ++++ src/jingle_rtp.rs | 12 ++++- src/jingle_ssma.rs | 114 +++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 ++ src/ns.rs | 3 ++ 5 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 src/jingle_ssma.rs diff --git a/doap.xml b/doap.xml index 40b28e3bd08a5a950638c2ea27e0e4daf5d74c18..f3f43b13988a25e4ac650294ec32f5ce98f6e0ba 100644 --- a/doap.xml +++ b/doap.xml @@ -417,6 +417,14 @@ NEXT + + + + complete + 0.3 + NEXT + + diff --git a/src/jingle_rtp.rs b/src/jingle_rtp.rs index 7e5557b2f43e098f1b424fe229c2ba4253f35d4a..7f9e5a1aa18fdc88b7a4f403781143944b6776a4 100644 --- a/src/jingle_rtp.rs +++ b/src/jingle_rtp.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use crate::jingle_ssma::{Source, Group}; + generate_element!( /// Wrapper element describing an RTP session. Description, "description", JINGLE_RTP, @@ -18,7 +20,13 @@ generate_element!( ], children: [ /// List of encodings that can be used for this RTP stream. - payload_types: Vec = ("payload-type", JINGLE_RTP) => PayloadType + payload_types: Vec = ("payload-type", JINGLE_RTP) => PayloadType, + + /// List of ssrc-group. + ssrc_groups: Vec = ("ssrc-group", JINGLE_SSMA) => Group, + + /// List of ssrc. + ssrcs: Vec = ("ssrc", JINGLE_SSMA) => Source // TODO: Add support for and . ] @@ -31,6 +39,8 @@ impl Description { media, ssrc: None, payload_types: Vec::new(), + ssrc_groups: Vec::new(), + ssrcs: Vec::new(), } } } diff --git a/src/jingle_ssma.rs b/src/jingle_ssma.rs new file mode 100644 index 0000000000000000000000000000000000000000..4d966b86ff8dd0676150363ee352b0228ee7fede --- /dev/null +++ b/src/jingle_ssma.rs @@ -0,0 +1,114 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +generate_element!( + /// Source element for the ssrc SDP attribute. + Source, "source", JINGLE_SSMA, + attributes: [ + /// Maps to the ssrc-id parameter. + id: Required = "ssrc", + ], + children: [ + /// List of attributes for this source. + parameters: Vec = ("parameter", JINGLE_SSMA) => Parameter + ] +); + +impl Source { + /// Create a new SSMA Source element. + pub fn new(id: String) -> Source { + Source { + id, + parameters: Vec::new(), + } + } +} + +generate_element!( + /// Parameter associated with a ssrc. + Parameter, "parameter", JINGLE_SSMA, + attributes: [ + /// The name of the parameter. + name: Required = "name", + + /// The optional value of the parameter. + value: Option = "value", + ] +); + +generate_element!( + /// Element grouping multiple ssrc. + Group, "ssrc-group", JINGLE_SSMA, + attributes: [ + /// The semantics of this group. + semantics: Required = "semantics", + ], + children: [ + /// The various ssrc concerned by this group. + sources: Vec = ("source", JINGLE_SSMA) => Source + ] +); + +#[cfg(test)] +mod tests { + use super::*; + use crate::Element; + use std::convert::TryFrom; + + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Source, 24); + assert_size!(Parameter, 24); + assert_size!(Group, 24); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(Source, 48); + assert_size!(Parameter, 48); + assert_size!(Group, 48); + } + + #[test] + fn parse_source() { + let elem: Element = " + + + +" + .parse() + .unwrap(); + let mut ssrc = Source::try_from(elem).unwrap(); + assert_eq!(ssrc.id, "1656081975"); + assert_eq!(ssrc.parameters.len(), 2); + let parameter = ssrc.parameters.pop().unwrap(); + assert_eq!(parameter.name, "msid"); + assert_eq!(parameter.value.unwrap(), "MLTJKIHilGn71fNQoszkQ4jlPTuS5vJyKVIv MLTJKIHilGn71fNQoszkQ4jlPTuS5vJyKVIva0"); + let parameter = ssrc.parameters.pop().unwrap(); + assert_eq!(parameter.name, "cname"); + assert_eq!(parameter.value.unwrap(), "Yv/wvbCdsDW2Prgd"); + } + + #[test] + fn parse_source_group() { + let elem: Element = " + + + +" + .parse() + .unwrap(); + let mut group = Group::try_from(elem).unwrap(); + assert_eq!(group.semantics, "FID"); + assert_eq!(group.sources.len(), 2); + let source = group.sources.pop().unwrap(); + assert_eq!(source.id, "386328120"); + let source = group.sources.pop().unwrap(); + assert_eq!(source.id, "2301230316"); + } +} diff --git a/src/lib.rs b/src/lib.rs index 2f856e0a27d6d56e80d8ff5961742e741900a149..7e4f034e30dd0a55ee8bb8c53ff7e6b185b2be0a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -183,6 +183,9 @@ pub mod jingle_dtls_srtp; /// XEP-0328: JID Prep pub mod jid_prep; +/// XEP-0339: Source-Specific Media Attributes in Jingle +pub mod jingle_ssma; + /// XEP-0352: Client State Indication pub mod csi; diff --git a/src/ns.rs b/src/ns.rs index 5aea7a65b56a7069f3a1a35b074a9a789e05ed3a..46090f09e77fdc567d201d932cb60031335e7e92 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -188,6 +188,9 @@ pub const JINGLE_DTLS: &str = "urn:xmpp:jingle:apps:dtls:0"; /// XEP-0328: JID Prep pub const JID_PREP: &str = "urn:xmpp:jidprep:0"; +/// XEP-0339: Source-Specific Media Attributes in Jingle +pub const JINGLE_SSMA: &str = "urn:xmpp:jingle:apps:rtp:ssma:0"; + /// XEP-0352: Client State Indication pub const CSI: &str = "urn:xmpp:csi:0"; From aeb8bc95f4a376781763f5ac67738aa1fff23055 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 Oct 2019 19:10:50 +0200 Subject: [PATCH 689/698] Add a parser for XEP-0293. --- doap.xml | 9 +++++++++ src/jingle_rtcp_fb.rs | 46 +++++++++++++++++++++++++++++++++++++++++++ src/jingle_rtp.rs | 11 ++++++++--- src/lib.rs | 3 +++ src/ns.rs | 3 +++ 5 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 src/jingle_rtcp_fb.rs diff --git a/doap.xml b/doap.xml index f3f43b13988a25e4ac650294ec32f5ce98f6e0ba..86c9fdb525a4878f79779106a5d9acf99c5ed2f0 100644 --- a/doap.xml +++ b/doap.xml @@ -361,6 +361,15 @@ 0.15.0 + + + + partial + 1.0.1 + NEXT + Only supported in payload-type, and only for rtcp-fb. + + diff --git a/src/jingle_rtcp_fb.rs b/src/jingle_rtcp_fb.rs new file mode 100644 index 0000000000000000000000000000000000000000..da52b88794b23c581bc39334f4bf4a817504db79 --- /dev/null +++ b/src/jingle_rtcp_fb.rs @@ -0,0 +1,46 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +generate_element!( + /// Wrapper element for a rtcp-fb. + RtcpFb, "rtcp-fb", JINGLE_RTCP_FB, + attributes: [ + /// Type of this rtcp-fb. + type_: Required = "type", + + /// Subtype of this rtcp-fb, if relevant. + subtype: Option = "subtype", + ] +); + +#[cfg(test)] +mod tests { + use super::*; + use crate::Element; + use std::convert::TryFrom; + + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(RtcpFb, 24); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(RtcpFb, 48); + } + + #[test] + fn parse_simple() { + let elem: Element = "" + .parse() + .unwrap(); + let rtcp_fb = RtcpFb::try_from(elem).unwrap(); + assert_eq!(rtcp_fb.type_, "nack"); + assert_eq!(rtcp_fb.subtype.unwrap(), "sli"); + } +} diff --git a/src/jingle_rtp.rs b/src/jingle_rtp.rs index 7f9e5a1aa18fdc88b7a4f403781143944b6776a4..0e9dcd85430179d46f0e8511e7943b33c195bcd0 100644 --- a/src/jingle_rtp.rs +++ b/src/jingle_rtp.rs @@ -5,6 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::jingle_ssma::{Source, Group}; +use crate::jingle_rtcp_fb::RtcpFb; generate_element!( /// Wrapper element describing an RTP session. @@ -76,7 +77,10 @@ generate_element!( /// List of parameters specifying this payload-type. /// /// Their order MUST be ignored. - parameters: Vec = ("parameter", JINGLE_RTP) => Parameter + parameters: Vec = ("parameter", JINGLE_RTP) => Parameter, + + /// List of rtcp-fb parameters from XEP-0293. + rtcp_fbs: Vec = ("rtcp-fb", JINGLE_RTCP_FB) => RtcpFb ] ); @@ -91,6 +95,7 @@ impl PayloadType { name: Some(name), ptime: None, parameters: Vec::new(), + rtcp_fbs: Vec::new(), } } } @@ -126,9 +131,9 @@ mod tests { #[cfg(target_pointer_width = "64")] #[test] fn test_size() { - assert_size!(Description, 72); + assert_size!(Description, 120); assert_size!(Channels, 1); - assert_size!(PayloadType, 80); + assert_size!(PayloadType, 104); assert_size!(Parameter, 48); } diff --git a/src/lib.rs b/src/lib.rs index 7e4f034e30dd0a55ee8bb8c53ff7e6b185b2be0a..4977a8bc9937aab5a9840bb20e52d42602a052e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -162,6 +162,9 @@ pub mod jingle_ibb; /// XEP-0280: Message Carbons pub mod carbons; +/// XEP-0293: Jingle RTP Feedback Negotiation +pub mod jingle_rtcp_fb; + /// XEP-0297: Stanza Forwarding pub mod forwarding; diff --git a/src/ns.rs b/src/ns.rs index 46090f09e77fdc567d201d932cb60031335e7e92..231d78ed4168eaae90989b0feb169289cd335660 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -155,6 +155,9 @@ pub const MICROBLOG: &str = "urn:xmpp:microblog:0"; /// XEP-0280: Message Carbons pub const CARBONS: &str = "urn:xmpp:carbons:2"; +/// XEP-0293: Jingle RTP Feedback Negotiation +pub const JINGLE_RTCP_FB: &str = "urn:xmpp:jingle:apps:rtp:rtcp-fb:0"; + /// XEP-0297: Stanza Forwarding pub const FORWARD: &str = "urn:xmpp:forward:0"; From ed5e6608afad4876c7a6fa1656e1211708b8991e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Tue, 15 Oct 2019 22:49:16 +0200 Subject: [PATCH 690/698] DOAP: Update foaf:mbox_sha1sum for pep. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- Cargo.toml | 2 +- doap.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 91f5fccc380bd1158005d22307f52d812fd9ca07..203a3cd216fb8f243694f186457229e4b755c683 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ edition = "2018" [dependencies] minidom = "0.11.0" -jid = { version = "0.7.0", features = ["minidom"] } +jid = { version = "0.7.2", features = ["minidom"] } base64 = "0.10" digest = "0.8" sha-1 = "0.8" diff --git a/doap.xml b/doap.xml index 86c9fdb525a4878f79779106a5d9acf99c5ed2f0..80dc172d0c088518f676187faf4db54fca1d7756 100644 --- a/doap.xml +++ b/doap.xml @@ -41,7 +41,7 @@ pep. - TODO + 99bcf9784288e323b0d2dea9c9ac7a2ede98395a From 567b22db72df6f24d7ed197ff30b2e468c9783ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Tue, 15 Oct 2019 22:51:08 +0200 Subject: [PATCH 691/698] DOAP: Update chatroom URI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- doap.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doap.xml b/doap.xml index 80dc172d0c088518f676187faf4db54fca1d7756..101eddad9931175ac9e4c703f2c47c8fe8c07bc0 100644 --- a/doap.xml +++ b/doap.xml @@ -18,8 +18,8 @@ - - + + From 0b936b57c709b56d2a6cf065f2b6594a14f00ca6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Tue, 15 Oct 2019 23:15:17 +0200 Subject: [PATCH 692/698] Prepare for release 0.16.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- Cargo.toml | 2 +- ChangeLog | 7 +++++-- doap.xml | 16 ++++++++-------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 203a3cd216fb8f243694f186457229e4b755c683..2b48af295bd7567dd2207337f97bbc68c25b3678 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ edition = "2018" [dependencies] minidom = "0.11.0" -jid = { version = "0.7.2", features = ["minidom"] } +jid = { version = "0.8", features = ["minidom"] } base64 = "0.10" digest = "0.8" sha-1 = "0.8" diff --git a/ChangeLog b/ChangeLog index 182d559713bd5acc6bfdfbb43d7a8279c1ca78f6..9f9ea7df6583936a9a9cf34315aa2f869fdd807a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,5 @@ -Version NEXT: -DATE Emmanuel Gil Peyrot +Version 0.16.0: +2019-10-15 Emmanuel Gil Peyrot * New parsers/serialisers: - Client Certificate Management for SASL EXTERNAL (XEP-0257) - JID Prep (XEP-0328) @@ -7,10 +7,13 @@ DATE Emmanuel Gil Peyrot - OpenPGP for XMPP (XEP-0373) - Bookmarks 2 (This Time it's Serious) (XEP-0402) - Anonymous unique occupant identifiers for MUCs (XEP-0421) + - Source-Specific Media Attributes in Jingle (XEP-0339) + - Jingle RTP Feedback Negotiation (XEP-0293) * Breaking changes: - Presence constructors now take Into and assume Some. * Improvements: - CI: refactor, add caching + - Update jid-rs to 0.8 Version 0.15.0: 2019-09-06 Emmanuel Gil Peyrot diff --git a/doap.xml b/doap.xml index 101eddad9931175ac9e4c703f2c47c8fe8c07bc0..87e6fe952cf5f9b24e2d41a032844835673beb0c 100644 --- a/doap.xml +++ b/doap.xml @@ -325,7 +325,7 @@ complete 0.3 - NEXT + 0.16.0 @@ -366,7 +366,7 @@ partial 1.0.1 - NEXT + 0.16.0 Only supported in payload-type, and only for rtcp-fb. @@ -423,7 +423,7 @@ complete 0.1 - NEXT + 0.16.0 @@ -431,7 +431,7 @@ complete 0.3 - NEXT + 0.16.0 @@ -439,7 +439,7 @@ complete 0.3.0 - NEXT + 0.16.0 @@ -463,7 +463,7 @@ partial 0.4.0 - NEXT + 0.16.0 @@ -487,7 +487,7 @@ complete 0.3.0 - NEXT + 0.16.0 @@ -495,7 +495,7 @@ complete 0.1.0 - NEXT + 0.16.0 From dfd28eaf406ba88e39ac1ee71835288780ddf7c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Tue, 15 Oct 2019 23:18:08 +0200 Subject: [PATCH 693/698] Actually bump version to 0.16.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 2b48af295bd7567dd2207337f97bbc68c25b3678..e02950d45585dbada1aabbd99692d34d870b0db2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.15.0" +version = "0.16.0" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", From 8c21efb2ac9402e5b79ddbe5150e81ad219c1e96 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 18 Oct 2019 13:05:14 +0200 Subject: [PATCH 694/698] jingle_rtp: Use the correct element name for ssrc. --- src/jingle_rtp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jingle_rtp.rs b/src/jingle_rtp.rs index 0e9dcd85430179d46f0e8511e7943b33c195bcd0..4fa596a991a12db2bc049eeae18f18c239cc38a0 100644 --- a/src/jingle_rtp.rs +++ b/src/jingle_rtp.rs @@ -27,7 +27,7 @@ generate_element!( ssrc_groups: Vec = ("ssrc-group", JINGLE_SSMA) => Group, /// List of ssrc. - ssrcs: Vec = ("ssrc", JINGLE_SSMA) => Source + ssrcs: Vec = ("source", JINGLE_SSMA) => Source // TODO: Add support for and . ] From 7b536e5bfc4be06773c761c5efc1257cc36cb0c5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 18 Oct 2019 13:05:33 +0200 Subject: [PATCH 695/698] macros: Implement Display for attributes. --- src/util/macros.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/util/macros.rs b/src/util/macros.rs index d63591aa6964e9078f92448f9349c926cbfa2b25..d703e258325dbb0a03a67ee46a64f6712d29afe2 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -85,6 +85,13 @@ macro_rules! generate_attribute { }) } } + impl std::fmt::Display for $elem { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + write!(fmt, "{}", match self { + $($elem::$a => $b),+ + }) + } + } impl ::minidom::IntoAttributeValue for $elem { fn into_attribute_value(self) -> Option { Some(String::from(match self { From 03e8ef75691adc5eae5fbc391a0f3fe1de5a444d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 18 Oct 2019 13:06:08 +0200 Subject: [PATCH 696/698] jingle: Expose Description the same way we expose Transport. --- src/jingle.rs | 53 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index fbccce0da23d9d96dfa304f91dca34a90dd8a2e1..82875686b8d6835eb4e0d2a894ac4eec03730429 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -6,6 +6,7 @@ use crate::util::error::Error; use crate::iq::IqSetPayload; +use crate::jingle_rtp::Description as RtpDescription; use crate::jingle_ice_udp::Transport as IceUdpTransport; use crate::jingle_ibb::Transport as IbbTransport; use crate::jingle_s5b::Transport as Socks5Transport; @@ -167,6 +168,52 @@ generate_id!( ContentId ); +/// Enum wrapping all of the various supported descriptions of a Content. +#[derive(Debug, Clone)] +pub enum Description { + /// Jingle RTP Sessions (XEP-0167) description. + Rtp(RtpDescription), + + /// To be used for any description that isn’t known at compile-time. + Unknown(Element), +} + +impl TryFrom for Description { + type Error = Error; + + fn try_from(elem: Element) -> Result { + Ok(if elem.is("description", ns::JINGLE_RTP) { + Description::Rtp(RtpDescription::try_from(elem)?) + } else { + Description::Unknown(elem) + }) + } +} + +impl From for Description { + fn from(desc: RtpDescription) -> Description { + Description::Rtp(desc) + } +} + +impl From for Element { + fn from(desc: Description) -> Element { + match desc { + Description::Rtp(desc) => desc.into(), + Description::Unknown(elem) => elem, + } + } +} + +impl Description { + fn get_ns(&self) -> String { + match self { + Description::Rtp(_) => String::from(ns::JINGLE_RTP), + Description::Unknown(elem) => elem.ns().unwrap_or_else(|| String::new()), + } + } +} + /// Enum wrapping all of the various supported transports of a Content. #[derive(Debug, Clone)] pub enum Transport { @@ -258,7 +305,7 @@ generate_element!( ], children: [ /// What to send. - description: Option = ("description", JINGLE) => Element, + description: Option = ("description", *) => Description, /// How to send it. transport: Option = ("transport", *) => Transport, @@ -295,8 +342,8 @@ impl Content { } /// Set the description of this content. - pub fn with_description(mut self, description: Element) -> Content { - self.description = Some(description); + pub fn with_description>(mut self, description: D) -> Content { + self.description = Some(description.into()); self } From 8a13d7f0106230ca0b4a6f82328a507d53d9f47a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 18 Oct 2019 13:06:36 +0200 Subject: [PATCH 697/698] jingle_ice_udp: Add methods to add a RTP candidate and set the DTLS-SRTP fingerprint. --- src/jingle_ice_udp.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/jingle_ice_udp.rs b/src/jingle_ice_udp.rs index be5dbc97e1af4c1a849c233f0f58481038b51045..b7f9e777886a2e686355b88a96b1d15f3b6620e5 100644 --- a/src/jingle_ice_udp.rs +++ b/src/jingle_ice_udp.rs @@ -36,6 +36,18 @@ impl Transport { fingerprint: None, } } + + /// Add a candidate to this transport. + pub fn add_candidate(mut self, candidate: Candidate) -> Self { + self.candidates.push(candidate); + self + } + + /// Set the DTLS-SRTP fingerprint of this transport. + pub fn with_fingerprint(mut self, fingerprint: Fingerprint) -> Self { + self.fingerprint = Some(fingerprint); + self + } } generate_attribute!( From dc76f296de77ff828c2acb9e188648513e73fb25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Fri, 18 Oct 2019 13:45:51 +0200 Subject: [PATCH 698/698] Prepare for merge: Move all xmpp-parsers files into xmpp-parsers/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- .gitlab-ci.yml => xmpp-parsers/.gitlab-ci.yml | 0 Cargo.toml => xmpp-parsers/Cargo.toml | 0 ChangeLog => xmpp-parsers/ChangeLog | 0 LICENSE => xmpp-parsers/LICENSE | 0 doap.xml => xmpp-parsers/doap.xml | 0 {examples => xmpp-parsers/examples}/generate-caps.rs | 0 {src => xmpp-parsers/src}/attention.rs | 0 {src => xmpp-parsers/src}/avatar.rs | 0 {src => xmpp-parsers/src}/bind.rs | 0 {src => xmpp-parsers/src}/blocking.rs | 0 {src => xmpp-parsers/src}/bob.rs | 0 {src => xmpp-parsers/src}/bookmarks.rs | 0 {src => xmpp-parsers/src}/bookmarks2.rs | 0 {src => xmpp-parsers/src}/caps.rs | 0 {src => xmpp-parsers/src}/carbons.rs | 0 {src => xmpp-parsers/src}/cert_management.rs | 0 {src => xmpp-parsers/src}/chatstates.rs | 0 {src => xmpp-parsers/src}/component.rs | 0 {src => xmpp-parsers/src}/csi.rs | 0 {src => xmpp-parsers/src}/data_forms.rs | 0 {src => xmpp-parsers/src}/date.rs | 0 {src => xmpp-parsers/src}/delay.rs | 0 {src => xmpp-parsers/src}/disco.rs | 0 {src => xmpp-parsers/src}/ecaps2.rs | 0 {src => xmpp-parsers/src}/eme.rs | 0 {src => xmpp-parsers/src}/forwarding.rs | 0 {src => xmpp-parsers/src}/hashes.rs | 0 {src => xmpp-parsers/src}/ibb.rs | 0 {src => xmpp-parsers/src}/ibr.rs | 0 {src => xmpp-parsers/src}/idle.rs | 0 {src => xmpp-parsers/src}/iq.rs | 0 {src => xmpp-parsers/src}/jid_prep.rs | 0 {src => xmpp-parsers/src}/jingle.rs | 0 {src => xmpp-parsers/src}/jingle_dtls_srtp.rs | 0 {src => xmpp-parsers/src}/jingle_ft.rs | 0 {src => xmpp-parsers/src}/jingle_ibb.rs | 0 {src => xmpp-parsers/src}/jingle_ice_udp.rs | 0 {src => xmpp-parsers/src}/jingle_message.rs | 0 {src => xmpp-parsers/src}/jingle_rtcp_fb.rs | 0 {src => xmpp-parsers/src}/jingle_rtp.rs | 0 {src => xmpp-parsers/src}/jingle_s5b.rs | 0 {src => xmpp-parsers/src}/jingle_ssma.rs | 0 {src => xmpp-parsers/src}/lib.rs | 0 {src => xmpp-parsers/src}/mam.rs | 0 {src => xmpp-parsers/src}/media_element.rs | 0 {src => xmpp-parsers/src}/message.rs | 0 {src => xmpp-parsers/src}/message_correct.rs | 0 {src => xmpp-parsers/src}/mood.rs | 0 {src => xmpp-parsers/src}/muc/mod.rs | 0 {src => xmpp-parsers/src}/muc/muc.rs | 0 {src => xmpp-parsers/src}/muc/user.rs | 0 {src => xmpp-parsers/src}/nick.rs | 0 {src => xmpp-parsers/src}/ns.rs | 0 {src => xmpp-parsers/src}/occupant_id.rs | 0 {src => xmpp-parsers/src}/openpgp.rs | 0 {src => xmpp-parsers/src}/ping.rs | 0 {src => xmpp-parsers/src}/presence.rs | 0 {src => xmpp-parsers/src}/pubsub/event.rs | 0 {src => xmpp-parsers/src}/pubsub/mod.rs | 0 {src => xmpp-parsers/src}/pubsub/pubsub.rs | 0 {src => xmpp-parsers/src}/receipts.rs | 0 {src => xmpp-parsers/src}/roster.rs | 0 {src => xmpp-parsers/src}/rsm.rs | 0 {src => xmpp-parsers/src}/sasl.rs | 0 {src => xmpp-parsers/src}/server_info.rs | 0 {src => xmpp-parsers/src}/sm.rs | 0 {src => xmpp-parsers/src}/stanza_error.rs | 0 {src => xmpp-parsers/src}/stanza_id.rs | 0 {src => xmpp-parsers/src}/stream.rs | 0 {src => xmpp-parsers/src}/time.rs | 0 {src => xmpp-parsers/src}/tune.rs | 0 {src => xmpp-parsers/src}/util/compare_elements.rs | 0 {src => xmpp-parsers/src}/util/error.rs | 0 {src => xmpp-parsers/src}/util/helpers.rs | 0 {src => xmpp-parsers/src}/util/macros.rs | 0 {src => xmpp-parsers/src}/util/mod.rs | 0 {src => xmpp-parsers/src}/version.rs | 0 {src => xmpp-parsers/src}/websocket.rs | 0 {src => xmpp-parsers/src}/xhtml.rs | 0 79 files changed, 0 insertions(+), 0 deletions(-) rename .gitlab-ci.yml => xmpp-parsers/.gitlab-ci.yml (100%) rename Cargo.toml => xmpp-parsers/Cargo.toml (100%) rename ChangeLog => xmpp-parsers/ChangeLog (100%) rename LICENSE => xmpp-parsers/LICENSE (100%) rename doap.xml => xmpp-parsers/doap.xml (100%) rename {examples => xmpp-parsers/examples}/generate-caps.rs (100%) rename {src => xmpp-parsers/src}/attention.rs (100%) rename {src => xmpp-parsers/src}/avatar.rs (100%) rename {src => xmpp-parsers/src}/bind.rs (100%) rename {src => xmpp-parsers/src}/blocking.rs (100%) rename {src => xmpp-parsers/src}/bob.rs (100%) rename {src => xmpp-parsers/src}/bookmarks.rs (100%) rename {src => xmpp-parsers/src}/bookmarks2.rs (100%) rename {src => xmpp-parsers/src}/caps.rs (100%) rename {src => xmpp-parsers/src}/carbons.rs (100%) rename {src => xmpp-parsers/src}/cert_management.rs (100%) rename {src => xmpp-parsers/src}/chatstates.rs (100%) rename {src => xmpp-parsers/src}/component.rs (100%) rename {src => xmpp-parsers/src}/csi.rs (100%) rename {src => xmpp-parsers/src}/data_forms.rs (100%) rename {src => xmpp-parsers/src}/date.rs (100%) rename {src => xmpp-parsers/src}/delay.rs (100%) rename {src => xmpp-parsers/src}/disco.rs (100%) rename {src => xmpp-parsers/src}/ecaps2.rs (100%) rename {src => xmpp-parsers/src}/eme.rs (100%) rename {src => xmpp-parsers/src}/forwarding.rs (100%) rename {src => xmpp-parsers/src}/hashes.rs (100%) rename {src => xmpp-parsers/src}/ibb.rs (100%) rename {src => xmpp-parsers/src}/ibr.rs (100%) rename {src => xmpp-parsers/src}/idle.rs (100%) rename {src => xmpp-parsers/src}/iq.rs (100%) rename {src => xmpp-parsers/src}/jid_prep.rs (100%) rename {src => xmpp-parsers/src}/jingle.rs (100%) rename {src => xmpp-parsers/src}/jingle_dtls_srtp.rs (100%) rename {src => xmpp-parsers/src}/jingle_ft.rs (100%) rename {src => xmpp-parsers/src}/jingle_ibb.rs (100%) rename {src => xmpp-parsers/src}/jingle_ice_udp.rs (100%) rename {src => xmpp-parsers/src}/jingle_message.rs (100%) rename {src => xmpp-parsers/src}/jingle_rtcp_fb.rs (100%) rename {src => xmpp-parsers/src}/jingle_rtp.rs (100%) rename {src => xmpp-parsers/src}/jingle_s5b.rs (100%) rename {src => xmpp-parsers/src}/jingle_ssma.rs (100%) rename {src => xmpp-parsers/src}/lib.rs (100%) rename {src => xmpp-parsers/src}/mam.rs (100%) rename {src => xmpp-parsers/src}/media_element.rs (100%) rename {src => xmpp-parsers/src}/message.rs (100%) rename {src => xmpp-parsers/src}/message_correct.rs (100%) rename {src => xmpp-parsers/src}/mood.rs (100%) rename {src => xmpp-parsers/src}/muc/mod.rs (100%) rename {src => xmpp-parsers/src}/muc/muc.rs (100%) rename {src => xmpp-parsers/src}/muc/user.rs (100%) rename {src => xmpp-parsers/src}/nick.rs (100%) rename {src => xmpp-parsers/src}/ns.rs (100%) rename {src => xmpp-parsers/src}/occupant_id.rs (100%) rename {src => xmpp-parsers/src}/openpgp.rs (100%) rename {src => xmpp-parsers/src}/ping.rs (100%) rename {src => xmpp-parsers/src}/presence.rs (100%) rename {src => xmpp-parsers/src}/pubsub/event.rs (100%) rename {src => xmpp-parsers/src}/pubsub/mod.rs (100%) rename {src => xmpp-parsers/src}/pubsub/pubsub.rs (100%) rename {src => xmpp-parsers/src}/receipts.rs (100%) rename {src => xmpp-parsers/src}/roster.rs (100%) rename {src => xmpp-parsers/src}/rsm.rs (100%) rename {src => xmpp-parsers/src}/sasl.rs (100%) rename {src => xmpp-parsers/src}/server_info.rs (100%) rename {src => xmpp-parsers/src}/sm.rs (100%) rename {src => xmpp-parsers/src}/stanza_error.rs (100%) rename {src => xmpp-parsers/src}/stanza_id.rs (100%) rename {src => xmpp-parsers/src}/stream.rs (100%) rename {src => xmpp-parsers/src}/time.rs (100%) rename {src => xmpp-parsers/src}/tune.rs (100%) rename {src => xmpp-parsers/src}/util/compare_elements.rs (100%) rename {src => xmpp-parsers/src}/util/error.rs (100%) rename {src => xmpp-parsers/src}/util/helpers.rs (100%) rename {src => xmpp-parsers/src}/util/macros.rs (100%) rename {src => xmpp-parsers/src}/util/mod.rs (100%) rename {src => xmpp-parsers/src}/version.rs (100%) rename {src => xmpp-parsers/src}/websocket.rs (100%) rename {src => xmpp-parsers/src}/xhtml.rs (100%) diff --git a/.gitlab-ci.yml b/xmpp-parsers/.gitlab-ci.yml similarity index 100% rename from .gitlab-ci.yml rename to xmpp-parsers/.gitlab-ci.yml diff --git a/Cargo.toml b/xmpp-parsers/Cargo.toml similarity index 100% rename from Cargo.toml rename to xmpp-parsers/Cargo.toml diff --git a/ChangeLog b/xmpp-parsers/ChangeLog similarity index 100% rename from ChangeLog rename to xmpp-parsers/ChangeLog diff --git a/LICENSE b/xmpp-parsers/LICENSE similarity index 100% rename from LICENSE rename to xmpp-parsers/LICENSE diff --git a/doap.xml b/xmpp-parsers/doap.xml similarity index 100% rename from doap.xml rename to xmpp-parsers/doap.xml diff --git a/examples/generate-caps.rs b/xmpp-parsers/examples/generate-caps.rs similarity index 100% rename from examples/generate-caps.rs rename to xmpp-parsers/examples/generate-caps.rs diff --git a/src/attention.rs b/xmpp-parsers/src/attention.rs similarity index 100% rename from src/attention.rs rename to xmpp-parsers/src/attention.rs diff --git a/src/avatar.rs b/xmpp-parsers/src/avatar.rs similarity index 100% rename from src/avatar.rs rename to xmpp-parsers/src/avatar.rs diff --git a/src/bind.rs b/xmpp-parsers/src/bind.rs similarity index 100% rename from src/bind.rs rename to xmpp-parsers/src/bind.rs diff --git a/src/blocking.rs b/xmpp-parsers/src/blocking.rs similarity index 100% rename from src/blocking.rs rename to xmpp-parsers/src/blocking.rs diff --git a/src/bob.rs b/xmpp-parsers/src/bob.rs similarity index 100% rename from src/bob.rs rename to xmpp-parsers/src/bob.rs diff --git a/src/bookmarks.rs b/xmpp-parsers/src/bookmarks.rs similarity index 100% rename from src/bookmarks.rs rename to xmpp-parsers/src/bookmarks.rs diff --git a/src/bookmarks2.rs b/xmpp-parsers/src/bookmarks2.rs similarity index 100% rename from src/bookmarks2.rs rename to xmpp-parsers/src/bookmarks2.rs diff --git a/src/caps.rs b/xmpp-parsers/src/caps.rs similarity index 100% rename from src/caps.rs rename to xmpp-parsers/src/caps.rs diff --git a/src/carbons.rs b/xmpp-parsers/src/carbons.rs similarity index 100% rename from src/carbons.rs rename to xmpp-parsers/src/carbons.rs diff --git a/src/cert_management.rs b/xmpp-parsers/src/cert_management.rs similarity index 100% rename from src/cert_management.rs rename to xmpp-parsers/src/cert_management.rs diff --git a/src/chatstates.rs b/xmpp-parsers/src/chatstates.rs similarity index 100% rename from src/chatstates.rs rename to xmpp-parsers/src/chatstates.rs diff --git a/src/component.rs b/xmpp-parsers/src/component.rs similarity index 100% rename from src/component.rs rename to xmpp-parsers/src/component.rs diff --git a/src/csi.rs b/xmpp-parsers/src/csi.rs similarity index 100% rename from src/csi.rs rename to xmpp-parsers/src/csi.rs diff --git a/src/data_forms.rs b/xmpp-parsers/src/data_forms.rs similarity index 100% rename from src/data_forms.rs rename to xmpp-parsers/src/data_forms.rs diff --git a/src/date.rs b/xmpp-parsers/src/date.rs similarity index 100% rename from src/date.rs rename to xmpp-parsers/src/date.rs diff --git a/src/delay.rs b/xmpp-parsers/src/delay.rs similarity index 100% rename from src/delay.rs rename to xmpp-parsers/src/delay.rs diff --git a/src/disco.rs b/xmpp-parsers/src/disco.rs similarity index 100% rename from src/disco.rs rename to xmpp-parsers/src/disco.rs diff --git a/src/ecaps2.rs b/xmpp-parsers/src/ecaps2.rs similarity index 100% rename from src/ecaps2.rs rename to xmpp-parsers/src/ecaps2.rs diff --git a/src/eme.rs b/xmpp-parsers/src/eme.rs similarity index 100% rename from src/eme.rs rename to xmpp-parsers/src/eme.rs diff --git a/src/forwarding.rs b/xmpp-parsers/src/forwarding.rs similarity index 100% rename from src/forwarding.rs rename to xmpp-parsers/src/forwarding.rs diff --git a/src/hashes.rs b/xmpp-parsers/src/hashes.rs similarity index 100% rename from src/hashes.rs rename to xmpp-parsers/src/hashes.rs diff --git a/src/ibb.rs b/xmpp-parsers/src/ibb.rs similarity index 100% rename from src/ibb.rs rename to xmpp-parsers/src/ibb.rs diff --git a/src/ibr.rs b/xmpp-parsers/src/ibr.rs similarity index 100% rename from src/ibr.rs rename to xmpp-parsers/src/ibr.rs diff --git a/src/idle.rs b/xmpp-parsers/src/idle.rs similarity index 100% rename from src/idle.rs rename to xmpp-parsers/src/idle.rs diff --git a/src/iq.rs b/xmpp-parsers/src/iq.rs similarity index 100% rename from src/iq.rs rename to xmpp-parsers/src/iq.rs diff --git a/src/jid_prep.rs b/xmpp-parsers/src/jid_prep.rs similarity index 100% rename from src/jid_prep.rs rename to xmpp-parsers/src/jid_prep.rs diff --git a/src/jingle.rs b/xmpp-parsers/src/jingle.rs similarity index 100% rename from src/jingle.rs rename to xmpp-parsers/src/jingle.rs diff --git a/src/jingle_dtls_srtp.rs b/xmpp-parsers/src/jingle_dtls_srtp.rs similarity index 100% rename from src/jingle_dtls_srtp.rs rename to xmpp-parsers/src/jingle_dtls_srtp.rs diff --git a/src/jingle_ft.rs b/xmpp-parsers/src/jingle_ft.rs similarity index 100% rename from src/jingle_ft.rs rename to xmpp-parsers/src/jingle_ft.rs diff --git a/src/jingle_ibb.rs b/xmpp-parsers/src/jingle_ibb.rs similarity index 100% rename from src/jingle_ibb.rs rename to xmpp-parsers/src/jingle_ibb.rs diff --git a/src/jingle_ice_udp.rs b/xmpp-parsers/src/jingle_ice_udp.rs similarity index 100% rename from src/jingle_ice_udp.rs rename to xmpp-parsers/src/jingle_ice_udp.rs diff --git a/src/jingle_message.rs b/xmpp-parsers/src/jingle_message.rs similarity index 100% rename from src/jingle_message.rs rename to xmpp-parsers/src/jingle_message.rs diff --git a/src/jingle_rtcp_fb.rs b/xmpp-parsers/src/jingle_rtcp_fb.rs similarity index 100% rename from src/jingle_rtcp_fb.rs rename to xmpp-parsers/src/jingle_rtcp_fb.rs diff --git a/src/jingle_rtp.rs b/xmpp-parsers/src/jingle_rtp.rs similarity index 100% rename from src/jingle_rtp.rs rename to xmpp-parsers/src/jingle_rtp.rs diff --git a/src/jingle_s5b.rs b/xmpp-parsers/src/jingle_s5b.rs similarity index 100% rename from src/jingle_s5b.rs rename to xmpp-parsers/src/jingle_s5b.rs diff --git a/src/jingle_ssma.rs b/xmpp-parsers/src/jingle_ssma.rs similarity index 100% rename from src/jingle_ssma.rs rename to xmpp-parsers/src/jingle_ssma.rs diff --git a/src/lib.rs b/xmpp-parsers/src/lib.rs similarity index 100% rename from src/lib.rs rename to xmpp-parsers/src/lib.rs diff --git a/src/mam.rs b/xmpp-parsers/src/mam.rs similarity index 100% rename from src/mam.rs rename to xmpp-parsers/src/mam.rs diff --git a/src/media_element.rs b/xmpp-parsers/src/media_element.rs similarity index 100% rename from src/media_element.rs rename to xmpp-parsers/src/media_element.rs diff --git a/src/message.rs b/xmpp-parsers/src/message.rs similarity index 100% rename from src/message.rs rename to xmpp-parsers/src/message.rs diff --git a/src/message_correct.rs b/xmpp-parsers/src/message_correct.rs similarity index 100% rename from src/message_correct.rs rename to xmpp-parsers/src/message_correct.rs diff --git a/src/mood.rs b/xmpp-parsers/src/mood.rs similarity index 100% rename from src/mood.rs rename to xmpp-parsers/src/mood.rs diff --git a/src/muc/mod.rs b/xmpp-parsers/src/muc/mod.rs similarity index 100% rename from src/muc/mod.rs rename to xmpp-parsers/src/muc/mod.rs diff --git a/src/muc/muc.rs b/xmpp-parsers/src/muc/muc.rs similarity index 100% rename from src/muc/muc.rs rename to xmpp-parsers/src/muc/muc.rs diff --git a/src/muc/user.rs b/xmpp-parsers/src/muc/user.rs similarity index 100% rename from src/muc/user.rs rename to xmpp-parsers/src/muc/user.rs diff --git a/src/nick.rs b/xmpp-parsers/src/nick.rs similarity index 100% rename from src/nick.rs rename to xmpp-parsers/src/nick.rs diff --git a/src/ns.rs b/xmpp-parsers/src/ns.rs similarity index 100% rename from src/ns.rs rename to xmpp-parsers/src/ns.rs diff --git a/src/occupant_id.rs b/xmpp-parsers/src/occupant_id.rs similarity index 100% rename from src/occupant_id.rs rename to xmpp-parsers/src/occupant_id.rs diff --git a/src/openpgp.rs b/xmpp-parsers/src/openpgp.rs similarity index 100% rename from src/openpgp.rs rename to xmpp-parsers/src/openpgp.rs diff --git a/src/ping.rs b/xmpp-parsers/src/ping.rs similarity index 100% rename from src/ping.rs rename to xmpp-parsers/src/ping.rs diff --git a/src/presence.rs b/xmpp-parsers/src/presence.rs similarity index 100% rename from src/presence.rs rename to xmpp-parsers/src/presence.rs diff --git a/src/pubsub/event.rs b/xmpp-parsers/src/pubsub/event.rs similarity index 100% rename from src/pubsub/event.rs rename to xmpp-parsers/src/pubsub/event.rs diff --git a/src/pubsub/mod.rs b/xmpp-parsers/src/pubsub/mod.rs similarity index 100% rename from src/pubsub/mod.rs rename to xmpp-parsers/src/pubsub/mod.rs diff --git a/src/pubsub/pubsub.rs b/xmpp-parsers/src/pubsub/pubsub.rs similarity index 100% rename from src/pubsub/pubsub.rs rename to xmpp-parsers/src/pubsub/pubsub.rs diff --git a/src/receipts.rs b/xmpp-parsers/src/receipts.rs similarity index 100% rename from src/receipts.rs rename to xmpp-parsers/src/receipts.rs diff --git a/src/roster.rs b/xmpp-parsers/src/roster.rs similarity index 100% rename from src/roster.rs rename to xmpp-parsers/src/roster.rs diff --git a/src/rsm.rs b/xmpp-parsers/src/rsm.rs similarity index 100% rename from src/rsm.rs rename to xmpp-parsers/src/rsm.rs diff --git a/src/sasl.rs b/xmpp-parsers/src/sasl.rs similarity index 100% rename from src/sasl.rs rename to xmpp-parsers/src/sasl.rs diff --git a/src/server_info.rs b/xmpp-parsers/src/server_info.rs similarity index 100% rename from src/server_info.rs rename to xmpp-parsers/src/server_info.rs diff --git a/src/sm.rs b/xmpp-parsers/src/sm.rs similarity index 100% rename from src/sm.rs rename to xmpp-parsers/src/sm.rs diff --git a/src/stanza_error.rs b/xmpp-parsers/src/stanza_error.rs similarity index 100% rename from src/stanza_error.rs rename to xmpp-parsers/src/stanza_error.rs diff --git a/src/stanza_id.rs b/xmpp-parsers/src/stanza_id.rs similarity index 100% rename from src/stanza_id.rs rename to xmpp-parsers/src/stanza_id.rs diff --git a/src/stream.rs b/xmpp-parsers/src/stream.rs similarity index 100% rename from src/stream.rs rename to xmpp-parsers/src/stream.rs diff --git a/src/time.rs b/xmpp-parsers/src/time.rs similarity index 100% rename from src/time.rs rename to xmpp-parsers/src/time.rs diff --git a/src/tune.rs b/xmpp-parsers/src/tune.rs similarity index 100% rename from src/tune.rs rename to xmpp-parsers/src/tune.rs diff --git a/src/util/compare_elements.rs b/xmpp-parsers/src/util/compare_elements.rs similarity index 100% rename from src/util/compare_elements.rs rename to xmpp-parsers/src/util/compare_elements.rs diff --git a/src/util/error.rs b/xmpp-parsers/src/util/error.rs similarity index 100% rename from src/util/error.rs rename to xmpp-parsers/src/util/error.rs diff --git a/src/util/helpers.rs b/xmpp-parsers/src/util/helpers.rs similarity index 100% rename from src/util/helpers.rs rename to xmpp-parsers/src/util/helpers.rs diff --git a/src/util/macros.rs b/xmpp-parsers/src/util/macros.rs similarity index 100% rename from src/util/macros.rs rename to xmpp-parsers/src/util/macros.rs diff --git a/src/util/mod.rs b/xmpp-parsers/src/util/mod.rs similarity index 100% rename from src/util/mod.rs rename to xmpp-parsers/src/util/mod.rs diff --git a/src/version.rs b/xmpp-parsers/src/version.rs similarity index 100% rename from src/version.rs rename to xmpp-parsers/src/version.rs diff --git a/src/websocket.rs b/xmpp-parsers/src/websocket.rs similarity index 100% rename from src/websocket.rs rename to xmpp-parsers/src/websocket.rs diff --git a/src/xhtml.rs b/xmpp-parsers/src/xhtml.rs similarity index 100% rename from src/xhtml.rs rename to xmpp-parsers/src/xhtml.rs