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 00000000..a9d37c56 --- /dev/null +++ b/.hgignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..10c55dd5 --- /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 00000000..f4eb70c8 --- /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 00000000..7b2232ec --- /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 00000000..50b94128 --- /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 00000000..bd7d2d9c --- /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 00000000..a6aa1901 --- /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 00000000..1fc84507 --- /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 00000000..36bd127c --- /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 7b2232ec..bc65e3a1 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 00000000..e732c6b4 --- /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 a6aa1901..56d17f47 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 36bd127c..55da2eb8 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 e732c6b4..37912a02 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 1fc84507..86f286ee 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 56d17f47..d8a3f8cc 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 55da2eb8..7c0903aa 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 00000000..7176fa42 --- /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 00000000..6ec2e22d --- /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 d8a3f8cc..62996e9c 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 7c0903aa..84ca86e9 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 bc65e3a1..e436b262 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 00000000..1868772e --- /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 62996e9c..f7c964b9 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 84ca86e9..24b81133 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 00000000..0bf2f498 --- /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 f7c964b9..e0798c12 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 24b81133..4f18ccc8 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 6ec2e22d..8941408d 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 e0798c12..8914a6c6 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 0bf2f498..43bd6914 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 8941408d..d84880c6 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 f4eb70c8..68e0e00d 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 e436b262..d51636e2 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 1868772e..64363c35 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 37912a02..00b390e4 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 86f286ee..d912f126 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 7176fa42..39145e22 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 68e0e00d..342c0c45 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 d51636e2..3100d4b5 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 00b390e4..a2fc331d 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 d912f126..7d774008 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 39145e22..de7aeb7b 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 43bd6914..43c6c833 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 d84880c6..77db1413 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 8914a6c6..c6f93d93 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 43c6c833..d02e508c 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 77db1413..1651e3a5 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 c6f93d93..f7608cd7 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 4f18ccc8..49293f4f 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 00000000..a19dff1c --- /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 d02e508c..7c5fcae4 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 3100d4b5..afb1fa7f 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 afb1fa7f..2a4ff64a 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 2a4ff64a..4b2d0334 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 50b94128..fddc712b 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 7c5fcae4..64624695 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 1651e3a5..5a9a4dc4 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 342c0c45..4ea50c96 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 4b2d0334..39befce5 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 64363c35..fb368a56 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 a2fc331d..40ba69da 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 7d774008..db40dfc6 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 49293f4f..a1848e52 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 de7aeb7b..adb0f45c 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 a19dff1c..a45a54c1 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 10c55dd5..7102bc64 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 fddc712b..21efc9c3 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 21efc9c3..da36f9e1 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 64624695..45406e6c 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 5a9a4dc4..8732d565 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 4ea50c96..dd9b3e11 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 39befce5..a9d8b370 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 fb368a56..00d30f3f 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 40ba69da..2b7f253f 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 db40dfc6..b48636a5 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 adb0f45c..2fb3e5cc 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 a45a54c1..052982ca 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 | 61 ++++++++++++++++++++++++++++++++++++++++-------------- src/ns.rs | 32 +++++++++++++++++++++++----- 2 files changed, 73 insertions(+), 20 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f7608cd7..dc8fcdb1 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; - -pub mod error; -pub mod ns; - -pub mod body; -pub mod disco; -pub mod data_forms; -pub mod media_element; -pub mod ecaps2; -pub mod jingle; -pub mod ping; -pub mod chatstates; -pub mod ibb; -pub mod receipts; - 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; + +/// XEP-0004: Data Forms +pub mod data_forms; + +/// 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; + +/// 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 a1848e52..5bcf6d1e 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 a9d8b370..4ac7240e 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 da36f9e1..e2e2de18 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 00d30f3f..2e99506b 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 2b7f253f..0caba248 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 e2e2de18..5b74beaa 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 5b74beaa..68012d96 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 00000000..2438dc6b --- /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 dc8fcdb1..2ba49d65 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 5bcf6d1e..2b5ba3f1 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 2ba49d65..1e4b975f 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 1e4b975f..c9315f6e 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 00000000..789febb9 --- /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 2b5ba3f1..df708fbe 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 68012d96..fce5e6ea 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 bd7d2d9c..c55a52a1 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 c9315f6e..50679604 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 2e99506b..6811e68c 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 6811e68c..f77541c8 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 f77541c8..89aad820 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 00000000..cf33de7f --- /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 50679604..167f58ec 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 df708fbe..6aa1d3f7 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 00000000..f4f59356 --- /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 167f58ec..e00ccb52 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 6aa1d3f7..8aefa2ee 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 f4f59356..b54dd047 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 00000000..21301633 --- /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 e00ccb52..d26a5c9a 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 8aefa2ee..d3d21068 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 fce5e6ea..fabd2e51 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 0caba248..80ad7b06 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 21301633..f9e1e846 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 c55a52a1..e9506bae 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 00000000..6e3e2803 --- /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 d26a5c9a..614737dd 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 d3d21068..5dfb3f5d 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 89aad820..a8705cd1 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 00000000..4ab73e68 --- /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 614737dd..b311682b 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 5dfb3f5d..de18053a 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 80ad7b06..fbda090c 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 45406e6c..dcc4389d 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 dcc4389d..f170cd38 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 2438dc6b..f64dbf5b 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 052982ca..8eb07ff9 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 b54dd047..54fa7e87 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 cf33de7f..79d8c89b 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 f170cd38..7703d655 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 8732d565..7632086e 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 b311682b..8efa52a3 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 789febb9..09596ec0 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 fbda090c..9cea6cf8 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 dd9b3e11..f25c8818 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 a8705cd1..507e3aa4 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 2fb3e5cc..a8c94c50 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 7102bc64..f513c3b4 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 8efa52a3..bb9dae7a 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 00000000..985b3eb6 --- /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 6e3e2803..82941b36 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 bb9dae7a..7be94346 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 985b3eb6..609aa77a 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 f513c3b4..262fe7e7 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 de18053a..130f1a19 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 7be94346..79b7d1da 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 00000000..51e5854e --- /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 f9e1e846..febf8b3f 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 fabd2e51..780c62e1 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 51e5854e..bb3334ef 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 262fe7e7..b46e00d7 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 780c62e1..d9e9a7de 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 79b7d1da..ba1f6501 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 ba1f6501..2536aaa4 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 00000000..6412ffc0 --- /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 bb3334ef..ea2da4a3 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 00000000..afdb463f --- /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 2536aaa4..a21168fe 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 afdb463f..87477c3e 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 87477c3e..eda650ac 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 a8c94c50..1d5c89ac 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 eda650ac..9d7fd79f 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 507e3aa4..ba8fcafb 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 4ab73e68..29590cba 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 29590cba..d6f009a9 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 9cea6cf8..c8a2dfc0 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 c8a2dfc0..9e2549cf 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 82941b36..05ea3b14 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 05ea3b14..596c1409 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 d9e9a7de..fc9327a3 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 fc9327a3..bfd208cb 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 b46e00d7..c049bd61 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 4ac7240e..5961518c 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 c049bd61..e236251d 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 bfd208cb..d5a13dd3 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 e9506bae..07b01c19 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 ba8fcafb..6281d140 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 596c1409..ee5bb954 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 130f1a19..415858c9 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 ee5bb954..4cd73b1d 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 4cd73b1d..4d5a86a6 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 9e2549cf..800cb7ae 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 800cb7ae..14372070 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 14372070..4c7a17c9 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 9d7fd79f..2edf6c86 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 6281d140..7152d722 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 2edf6c86..e2ac6b35 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 d5a13dd3..fade1f97 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 a21168fe..19689510 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 19689510..e2a656b3 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 415858c9..a3484637 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 00000000..14d842b7 --- /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 54fa7e87..6d0634a2 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 609aa77a..34dc49b5 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 00000000..d7925d7a --- /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 e2a656b3..0dde87af 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 a3484637..16f73394 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 0dde87af..81f62e2d 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 16f73394..d06aff62 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 00000000..a70bdd89 --- /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 07b01c19..1f001ca5 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 81f62e2d..e3b19cae 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 00000000..416aefc9 --- /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 d06aff62..b9674c0f 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 416aefc9..4c0441be 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_invalid_id() { - let elem: Element = "".parse().unwrap(); + 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_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 d7925d7a..d1d47a52 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 4d5a86a6..71b671c8 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 34dc49b5..a915b221 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 ea2da4a3..a144e371 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 e236251d..7118c203 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 7118c203..bef0ab8f 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 00000000..14e2f777 --- /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 f64dbf5b..2f12545e 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 7703d655..8f23b569 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 7632086e..d26791ef 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 f25c8818..ebd2ff2d 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 6d0634a2..8e8e382e 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 5961518c..5bdaef8b 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 fade1f97..aa2cde7e 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 79d8c89b..879ddda0 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 1f001ca5..a65f7e2d 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 d1d47a52..991d508a 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 febf8b3f..c489a32f 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 7152d722..d805eb81 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 e2ac6b35..645c4ba0 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 4c7a17c9..bd903d08 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 71b671c8..678bca48 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 d6f009a9..8e0d04ca 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 e3b19cae..aadf4f79 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 4c0441be..cb0cc7ac 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 b48636a5..a3c360e5 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 a915b221..a88db4c9 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 09596ec0..27ac7e91 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 b9674c0f..495c2856 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 1d5c89ac..b6b6f617 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 a144e371..0f437958 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 8eb07ff9..3dc3b787 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 a70bdd89..d23fa1b6 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 14d842b7..5d34c3dc 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 6412ffc0..66bde73e 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 ebd2ff2d..3b3757ef 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 5bdaef8b..bc448091 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 bd903d08..717a3b77 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 678bca48..2028fe93 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 aadf4f79..0a5eac0a 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 0f437958..cafb7f6e 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 66bde73e..00000000 --- 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 cafb7f6e..ad6301eb 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 ad6301eb..3f298333 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 | 82 +++++++++++++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 30 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index 3f298333..13517929 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] @@ -299,29 +308,39 @@ mod tests { } #[test] - fn test_status() { - let elem: Element = "".parse().unwrap(); + fn test_empty_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."), - } + assert_eq!(presence.payloads.len(), 0); + assert_eq!(presence.statuses.len(), 1); + assert_eq!(presence.statuses[0], ""); + } + + #[test] + fn test_status() { + let elem: Element = "Here!".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], "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 13517929..16c5f0fc 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 16c5f0fc..fb26d94f 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 0a5eac0a..67a335fc 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 495c2856..8e7c08b1 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 00000000..0a1eb425 --- /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 fb26d94f..3b3c1faa 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 a88db4c9..9d3ea6fe 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 645c4ba0..cf970553 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 00000000..83fad57c --- /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 8e8e382e..06c980f6 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 cf970553..c7728ae2 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 2f12545e..21f8e0b5 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.")); +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) } - for _ in root.children() { - return Err(Error::ParseError("Unknown child in attention element.")); - } - 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 67a335fc..3076fccf 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 9d3ea6fe..3550e810 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 bef0ab8f..3f13f303 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 3f13f303..ea7eecef 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 06c980f6..2b2149eb 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 2028fe93..7cd729e1 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 3550e810..6fcc681d 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 3b3c1faa..bf7b57df 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 | 404 ++++++++++++++++++++++++++------------------------ 2 files changed, 214 insertions(+), 200 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index c7728ae2..8a4e70e7 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 717a3b77..dfe0d710 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.")); - } - 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.")); - } - 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, - }); - } else { - other.push(child.clone()); + fn try_from(root: &'a Element) -> Result { + if !root.is("jingle", ns::JINGLE) { + return Err(Error::ParseError("This is not a Jingle element.")); } - } - Ok(Jingle { - action: action, - initiator: initiator, - responder: responder, - sid: sid.to_owned(), - contents: contents, - reason: reason_element, - other: other, - }) + 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(), + )); + } + } + 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.")); + } + 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, + }); + } else { + other.push(child.clone()); + } + } + + 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 8a4e70e7..d1e3eeaa 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 b6b6f617..4fe2fd86 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 d805eb81..0156cfee 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 d1e3eeaa..65b49cdb 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 a65f7e2d..2f7b254f 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 00000000..6a6ee50f --- /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 3076fccf..e3fb5356 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 8e7c08b1..c98bff58 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 6a6ee50f..b0d7266f 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 8e0d04ca..4ac847cc 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 dfe0d710..fb928aee 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 6fcc681d..48001452 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 3dc3b787..27392fb8 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 5d34c3dc..d478b1aa 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 | 214 +++++++++++++++++++++++++++-------------------------- 2 files changed, 116 insertions(+), 109 deletions(-) diff --git a/src/mam.rs b/src/mam.rs index cb0cc7ac..77a3b6cf 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 d23fa1b6..4caab080 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.")); - } - max = Some(child.text().parse()?); - } else { - return Err(Error::ParseError("Unknown child in set element.")); +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.")); + } + } + 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()); +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 } - 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; + 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 48001452..c513abee 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 27ac7e91..ecc555cc 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.")); +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 }) } - for _ in root.children() { - return Err(Error::ParseError("Unknown child in replace element.")); - } - 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 7cd729e1..f2b981fa 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.")); } - range_hashes.push(parse_hash(hash_element)?); + 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.")); + } + 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()); +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 } - 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)); - } - 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 d26791ef..9ed2b10c 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 c513abee..dfc5e6eb 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 2b2149eb..a15b61bc 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.")); +impl<'a> TryFrom<&'a Element> for Delay { + type Error = Error; + + 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, + }) } - 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, - }) } -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() -} - -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 991d508a..d3441457 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 dfc5e6eb..476920a0 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 bf7b57df..de5944c2 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 aa2cde7e..62242db7 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 c489a32f..6e7bcb2d 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.")); +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, + }) } - 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, - }) } -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 f2b981fa..efbe9c9d 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 | 68 +++++++++++++++++++++++--------------------- 2 files changed, 39 insertions(+), 34 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index 3b3757ef..94dae1ca 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 a3c360e5..be2adda1 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; - 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.")); - } - uris.push(URI { type_: type_.to_owned(), uri: text }); - } else { - return Err(Error::ParseError("Unknown child in media element.")); + 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 = 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.")); + } + } + 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 | 114 ++++++++++++++++++++++--------------------- src/disco.rs | 6 ++- src/mam.rs | 5 +- src/media_element.rs | 4 +- 4 files changed, 66 insertions(+), 63 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index 94dae1ca..a83ebb82 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; - 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? - } - } 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.")); + 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 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.")); + } + } + 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 }) } - 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 bc448091..9fb52906 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 77a3b6cf..308cc1c0 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 be2adda1..d4d31985 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 d3441457..75b7fbcc 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 308cc1c0..a826decc 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 62242db7..fdddfdaa 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 de5944c2..6ae97ecb 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 | 222 +++++++++++++++++++++++++------------------------- src/ecaps2.rs | 7 +- src/iq.rs | 8 +- 3 files changed, 120 insertions(+), 117 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index 9fb52906..48f4a2d0 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.")); - } +impl<'a> TryFrom<&'a Element> for Disco { + type Error = Error; - 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.")) - } - - 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.")); + 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.")); } - } - /* - // 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.")); - } - */ + let mut identities: Vec = vec!(); + let mut features: Vec = vec!(); + let mut extensions: Vec = vec!(); - Ok(Disco { - node: node, - identities: identities, - features: features, - extensions: extensions - }) + 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.")); + } + } + + /* + // 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 + }) + } } -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); +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 } - 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!"); - } - 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 fdddfdaa..3d7f2c94 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 65b49cdb..f48026a5 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 879ddda0..ad28afc7 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.")); +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, + }) } - 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, - }) } -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 476920a0..ce6622aa 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 | 408 ++++++++++++++++++++++++++++------------------------- 1 file changed, 215 insertions(+), 193 deletions(-) diff --git a/src/mam.rs b/src/mam.rs index a826decc..c831a099 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.")); - } - } - 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 }) -} +impl<'a> TryFrom<&'a Element> for Query { + type Error = Error; -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.")); + fn try_from(elem: &'a Element) -> Result { + if !elem.is("query", ns::MAM) { + return Err(Error::ParseError("This is not a query 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(Set::try_from(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()?); + 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.")); } - } 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()?); + } + 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 }) + } +} + +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.")); } - } 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((&form).into()); - //} - if let Some(ref set) = query.set { - elem.append_child(set.into()); - } - 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 -} - -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 -} - -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()); + 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.")); } - elem.append_child(always); + let forwarded = forwarded.unwrap(); + Ok(Result_ { + queryid, + id, + forwarded, + }) } - 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()); +} + +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.")); } - elem.append_child(never); + 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 }) + } +} + +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()?); + } + } 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 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 }) + } +} + +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 + } +} + +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 + } +} + +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 + } +} + +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); + } + 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 } - 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 | 165 +++++++++++++++++++++++--------------------- 4 files changed, 102 insertions(+), 99 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index f48026a5..0e0aca7f 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 ce6622aa..ee2c6447 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 6ae97ecb..4ec697cb 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 0a1eb425..9a0bfa03 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; - - 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()); + fn try_from(elem: &'a Element) -> Result { + if !elem.is("error", ns::JABBER_CLIENT) { + return Err(Error::ParseError("This is not an error element.")); } - } - if defined_condition.is_none() { - return Err(Error::ParseError("Error must have a defined-condition.")); - } - let defined_condition = defined_condition.unwrap(); + 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; - Ok(StanzaError { - type_: type_, - by: by, - defined_condition: defined_condition, - texts: texts, - other: other, - }) + 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()); + } + } + + 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); +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 } - 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; + 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 | 249 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 128 insertions(+), 121 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index 0e0aca7f..4f61260d 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() { + 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.")); + } + 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.")); } - error_payload = Some(StanzaError::try_from(elem)?); - } else if root.children().collect::>().len() != 2 { + } 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())), + }; + } + } + + 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 { - 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 - }; + panic!() + }; - 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()) - } 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_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(), + 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)) - | 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 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(), + } + } +} + +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 75b7fbcc..a2162e06 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 ee2c6447..51baf9b9 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 | 286 ++++++++++++++++++++++++------------------------ 1 file changed, 146 insertions(+), 140 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index 4ec697cb..cfaf5e71 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, +impl<'a> TryFrom<&'a Element> for Presence { + type Error = Error; - _ => 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)) + 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, + }) + } +} + +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(), } } - 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 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 } } -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 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 8f23b569..00000000 --- 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 e3fb5356..67c06296 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 51baf9b9..c54b0ab4 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 67c06296..7faf06c6 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 ea7eecef..85e089c9 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 83fad57c..d8e85e11 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 c54b0ab4..2c8959b8 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 2c8959b8..3248451c 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 3248451c..e7972c34 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 b0d7266f..4c55d765 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 c831a099..640a3c04 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 e7972c34..bb8e8b46 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 cfaf5e71..f354d310 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 9a0bfa03..2f509ee2 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 3d7f2c94..e397a455 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 6e7bcb2d..403cb0c9 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 efbe9c9d..9dd69df1 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 bb8e8b46..136f5585 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 136f5585..f76824ed 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 e397a455..b69a6449 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 f76824ed..a900502b 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 f354d310..e1212c74 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 e1212c74..67ca5fa0 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 4f61260d..1bf3ba14 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 1bf3ba14..13efbaac 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 13efbaac..f040b1bd 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 f040b1bd..b6216898 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 b6216898..43624750 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 a900502b..96153cfa 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 67ca5fa0..7eb53b41 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 4caab080..6a16d385 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 43624750..d2d4a689 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 96153cfa..dae600e8 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 6a16d385..a6aec04d 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 a6aec04d..7e139feb 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 0156cfee..4d11ec11 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 7faf06c6..24890252 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 7e139feb..99f18930 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 4d11ec11..ffdb907e 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 4ac847cc..73544c26 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 24890252..db9b95aa 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 99f18930..a697b370 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 a83ebb82..bb443c9b 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 85e089c9..59cafb26 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 9ed2b10c..f9fdb4f3 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 4fe2fd86..baebf68b 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 7eb53b41..d780d004 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 bb443c9b..6d4e3bc4 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 dae600e8..23e6686e 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 00000000..5261788e --- /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 db9b95aa..2eb2aecf 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 c98bff58..b51c6530 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 d780d004..fd6547b1 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 640a3c04..b673154b 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 d478b1aa..2b87890b 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 4c55d765..cb43e743 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 9dd69df1..454ce8d4 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 48f4a2d0..9610ad7e 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 403cb0c9..da16788b 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 d2d4a689..b1767735 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 23e6686e..a309fa01 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 fd6547b1..c15950a9 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 a15b61bc..79424dcb 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 ad28afc7..217517ce 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 ecc555cc..ab0d3b2b 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 2f509ee2..85f21298 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 59cafb26..41d4a464 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 | 56 ++++++++++++++++----------------- 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 | 61 +++++++++++++++++++----------------- 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, 376 insertions(+), 380 deletions(-) diff --git a/src/attention.rs b/src/attention.rs index 21f8e0b5..b28b5cc3 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 f9fdb4f3..54f0aefe 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 6d4e3bc4..21d2ab11 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 79424dcb..08b406c5 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 9610ad7e..acd2d487 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 b69a6449..ac9df22e 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 217517ce..edf6cd35 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 a2162e06..9ad416b8 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 da16788b..af9c3a05 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 ffdb907e..71bf6951 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 5261788e..2800a0e1 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 b1767735..aaf7b027 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(), + 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(ref elem) => elem.clone(), + 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 fb928aee..4796ae41 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 454ce8d4..57cf626c 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 73544c26..d89df221 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 cb43e743..fa5ee667 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 b673154b..22ec0576 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 d4d31985..54ffee13 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 a309fa01..be47756a 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(), + 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(ref elem) => elem.clone(), + 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 ab0d3b2b..35494f81 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 baebf68b..84a7cebf 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 c15950a9..9dd1c62c 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 27392fb8..f7c1d50d 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 a697b370..7ac80b19 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 85f21298..4f30f3a4 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 2b87890b..38008d0c 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 41d4a464..a0d95aa3 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 d8e85e11..e8c42e2a 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 9dd1c62c..6b4b1ac4 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 aaf7b027..28b49efb 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 be47756a..bc8a0035 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 28b49efb..7ab3309f 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 bc8a0035..13caed35 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 35494f81..8ce747f1 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 7ac80b19..21d86ee7 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 | 296 ++++++++++++++++++++++++++------------------------ 1 file changed, 154 insertions(+), 142 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index 4796ae41..7f925bf8 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 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!(), + }; - 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() { + 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 54ffee13..931cab6f 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 f7c1d50d..72b357a1 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 fa5ee667..42c188f8 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 7f925bf8..06582200 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 acd2d487..46d25dec 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 4f30f3a4..0a327579 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 d89df221..6f789d3e 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 ac9df22e..57577ca5 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 edf6cd35..fe973861 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 57577ca5..03f8a40b 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 af9c3a05..3c9a05eb 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 57cf626c..1a4550cf 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 22ec0576..c8cd2ac8 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 21d2ab11..4aba8528 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 931cab6f..b1c36a64 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 08b406c5..19b1a495 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 06582200..c5041b09 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 0a327579..3841ad5b 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 a0d95aa3..59a8a89b 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 00000000..d17cb501 --- /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 59a8a89b..d8b08268 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 2f7b254f..899e25f0 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 2800a0e1..ba13bd89 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 363dfec3..50ed56e6 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 6b4b1ac4..4d0391f1 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 3c9a05eb..f3117888 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 d17cb501..4b504241 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 03f8a40b..09f1516e 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 19b1a495..65349040 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 4b504241..b7753a19 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 46d25dec..486f81e9 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 09f1516e..7d9279bb 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 d8b08268..700d7f15 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 71bf6951..18cb14e8 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 1a4550cf..1d37b6a9 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 6f789d3e..afbf11c3 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 42c188f8..316a3210 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 21d86ee7..e8d7109d 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 316a3210..e244a8c0 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 e8c42e2a..375c06fc 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 700d7f15..84fe4add 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 375c06fc..63f21648 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 50ed56e6..c6f11031 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 99865ae5..4f563e62 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 00000000..aaca6d8e --- /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 4d0391f1..1d4ff35e 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 | 109 +++++++++++++++++++++++++++--------------------- 1 file changed, 61 insertions(+), 48 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index 1d4ff35e..b359eb5e 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, + Type::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::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 7ab3309f..76a97b1f 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 aaca6d8e..51e8eb13 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 51e8eb13..e8dd8ad5 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 f3117888..07f2822c 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 84fe4add..1a93c909 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 c6f11031..b5ee57ef 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 00000000..12923fc0 --- /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 4f563e62..d7706617 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 b359eb5e..d04a3d2e 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 12923fc0..c8248b75 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 d7706617..149025ac 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 65349040..d744751d 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 ba13bd89..82009668 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 4aba8528..70c2d3aa 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 b5ee57ef..46a2b8ef 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 149025ac..88541e0e 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 00000000..47d9a469 --- /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 00000000..9eb93944 --- /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 1a93c909..51a4a37b 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 63f21648..d219bf0b 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 51a4a37b..68d32e5d 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 88541e0e..af76dd90 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 | 161 +++++++++----------------------------------- 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, 156 insertions(+), 497 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index 70c2d3aa..0899cc5c 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 18cb14e8..5463caae 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 c5041b09..e5fab352 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, -} +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", +}); -impl FromStr for Action { - type Err = Error; +generate_attribute!(Creator, "creator", { + Initiator => "initiator", + Responder => "responder", +}); - 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!(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 e244a8c0..089d1605 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 46a2b8ef..4fc420b8 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 c8cd2ac8..920cf8d0 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 13caed35..4efabbdb 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 47d9a469..07e465c8 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 e8dd8ad5..f38817d0 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 3841ad5b..badd1efa 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 0899cc5c..9905f4b4 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 e5fab352..d189684e 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 4efabbdb..79038c12 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 d189684e..84600f43 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 9905f4b4..b5cd004b 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 5463caae..ad987377 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 afbf11c3..25dbaa37 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 79038c12..8025a2a1 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 84600f43..ca9858ae 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 ca9858ae..fad59aa3 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 1d37b6a9..a70b012b 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 899e25f0..f8d7372b 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 68d32e5d..224ace7d 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 54f0aefe..47df01e0 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 b5cd004b..7e187ec1 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 00000000..183d3716 --- /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 4fc420b8..00b2592c 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 af76dd90..fbc0048f 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 089d1605..b585cf19 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 00b2592c..cb8b9d4a 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 fad59aa3..224bb543 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 b585cf19..cc916a3b 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 25dbaa37..72ebba55 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 cc916a3b..57807be3 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 f38817d0..db939ad8 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 8025a2a1..47c8db16 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 d04a3d2e..c85f25be 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 c8248b75..00000000 --- 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 00000000..80d61d88 --- /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 00000000..75667d87 --- /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 00000000..8dae704a --- /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 | 82 ++++++++----------------------------------------- 1 file changed, 13 insertions(+), 69 deletions(-) diff --git a/src/muc/user.rs b/src/muc/user.rs index 8dae704a..833b5604 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, -} +generate_attribute!(Affiliation, "affiliation", { + Owner => "owner", + Admin => "admin", + Member => "member", + Outcast => "outcast", + None => "none", +}, Default = 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!(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 c85f25be..74458797 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 224ace7d..ac9eae51 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 d219bf0b..5edf6648 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 ac9eae51..608f2ff5 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 224bb543..ece7fa02 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 00000000..d6113342 --- /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 cb8b9d4a..f82e6df3 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 fbc0048f..7edee1e4 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 b7753a19..e883a64a 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 7d9279bb..da74533d 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 76a97b1f..9420b44c 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 | 164 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 119 insertions(+), 45 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index 9420b44c..976d79f8 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)?), - - // XEP-0199 - ("ping", ns::PING) => IqPayload::Ping(Ping::try_from(elem)?), + ("jingle", ns::JINGLE) => IqSetPayload::Jingle(Jingle::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) => IqSetPayload::MamQuery(MamQuery::try_from(elem)?), + ("prefs", ns::MAM) => IqSetPayload::MamPrefs(MamPrefs::try_from(elem)?), - _ => IqPayload::Unknown(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) => 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 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 608f2ff5..b7053811 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 b28b5cc3..c53040c4 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 e883a64a..aad50eb2 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 47df01e0..ff1baa95 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 7e187ec1..d4678c09 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 d744751d..7b56ab7e 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 a36f997e..43727d51 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 da74533d..0e92796d 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 fe973861..ee71e21c 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 9ad416b8..d10860ff 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 07f2822c..6520fede 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 ad987377..969f3419 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 183d3716..a0c7cc72 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 82009668..3809ff8b 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 976d79f8..10161021 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 ece7fa02..c9470326 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 a70b012b..5e070cd5 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 72ebba55..08a31329 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 d6113342..216e1877 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 57807be3..603dbb24 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 f82e6df3..35dacabd 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 920cf8d0..62b035e1 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 b1c36a64..aa15efb5 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 47c8db16..3c7fefc8 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 8ce747f1..5376f860 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 75667d87..e7e6a238 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 833b5604..1609b0c8 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 84a7cebf..27204451 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 74458797..1eb0b23f 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 07e465c8..ef5e994b 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 72b357a1..ae564f74 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 db939ad8..4e6a38c6 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 e8d7109d..cfb6f292 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 badd1efa..c25c5792 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 38008d0c..634def50 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 c53040c4..333d1900 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 333d1900..b72079b9 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 ff1baa95..a5af910c 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 7b56ab7e..b96db2bd 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 43727d51..aee3de6a 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 0e92796d..54228c8d 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 ee71e21c..ba07f95c 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 d10860ff..3afaabdf 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 6520fede..bcaf94ac 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 969f3419..cdc9b5cd 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 a0c7cc72..37c54f7b 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 3809ff8b..51c20393 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 10161021..d1078b34 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 c9470326..1d0c07f1 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 5e070cd5..7bd0df8d 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 08a31329..ed1c6d18 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 603dbb24..26852904 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 62b035e1..bbc08a6d 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 3c7fefc8..03fc2e8c 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 5376f860..bb5c2b2e 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 e7e6a238..550cc61e 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 1609b0c8..48da7966 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 27204451..df8b1939 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 1eb0b23f..e74a6e28 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 ae564f74..7c3625b8 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 4e6a38c6..73522c28 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 cfb6f292..008757dd 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 c25c5792..4a83b8dc 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 634def50..72687d9b 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 54228c8d..be80ffd5 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 3afaabdf..86474dd1 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 26852904..7ef1e502 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 bbc08a6d..b52e8041 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 b72079b9..e394785f 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 e74a6e28..30a2615d 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 d1078b34..aed6a83e 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 7bd0df8d..85ac2dfa 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 30a2615d..a0147c7d 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 d4678c09..bccdca09 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 37c54f7b..59dc15d8 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 03fc2e8c..bbb6a5e0 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 a0147c7d..098c401a 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 73522c28..a1ed2dc5 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 aed6a83e..aab1efcb 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 35dacabd..359ba7b0 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 bbb6a5e0..ea560425 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 098c401a..fe970e3b 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 359ba7b0..b3f57fdf 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 e394785f..7d0b1f24 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 a5af910c..4093edbd 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 ba07f95c..288431b9 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 df8b1939..c97fa502 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 b96db2bd..e5123ec2 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 aab1efcb..1eae2549 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 ea560425..29234321 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 fe970e3b..93c5cf94 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 ef5e994b..04519e96 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 4a83b8dc..3b7273b3 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 b7053811..b4a760b2 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 bccdca09..eb751e0b 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 aee3de6a..167440ed 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 59dc15d8..c51153e6 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 1d0c07f1..62fb4b5a 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 aa15efb5..b2e5857d 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 48da7966..afc9e721 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 04519e96..a6fa4550 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 a1ed2dc5..1a4cc50e 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 167440ed..aa816dec 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 7edee1e4..0d06a1d7 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 5edf6648..e10497ce 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 b4a760b2..6f27f34a 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 6f27f34a..7b425d44 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 aad50eb2..a2bdd11b 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 be80ffd5..593acec0 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 7b425d44..c5ff5e09 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 e10497ce..a780194d 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 593acec0..8166e5b0 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 7ef1e502..d454a3d9 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 eb751e0b..a2330a52 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 62fb4b5a..364ee4b8 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 008757dd..fa6f6c81 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 cdc9b5cd..2c01e1c7 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(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() - }, +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() + } +} + +#[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 1eae2549..2584ebeb 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 29234321..90bce3db 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 72687d9b..43ce9645 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 | 94 ++++++++++++++++++++++++++++++------------------- 2 files changed, 65 insertions(+), 41 deletions(-) diff --git a/src/message.rs b/src/message.rs index 90bce3db..759b7d23 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 7c3625b8..6ccec7ea 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 Receipt { +impl TryFrom for Request { type Err = Error; - fn try_from(elem: Element) -> Result { + 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 receipt element.")); + return Err(Error::ParseError("Unknown child in request 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.")); - } - } - let id = get_attr!(elem, "id", optional); - Ok(Receipt::Received(id)) - } else { - Err(Error::ParseError("This is not a receipt element.")) + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown attribute in request element.")); } + Ok(Request) } } -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(_: 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() } } @@ -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 a2330a52..15135ef3 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 aa816dec..1649682d 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 1649682d..f3fad62e 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 f3fad62e..88e105ce 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 88e105ce..b976e362 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 b3f57fdf..ce1610e4 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 ce1610e4..f8810823 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 1a4cc50e..a1a07eb1 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 759b7d23..80a0e044 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 a1a07eb1..82488a34 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 4093edbd..2cb657f6 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 c5ff5e09..c1f48c00 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 e5123ec2..97c10ad7 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 b976e362..b1d21680 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 2584ebeb..1fd308c0 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 364ee4b8..d36d0862 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 80a0e044..3bf73ae8 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 afc9e721..9e141ad2 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 93c5cf94..958a82b3 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 a6fa4550..108d0ab1 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 82488a34..9e2bba31 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 3b7273b3..0c7ca7e8 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 43ce9645..feeeadbe 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 c1f48c00..a7011854 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 86474dd1..2348068b 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 1fd308c0..7e919985 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 b52e8041..65299b70 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 3bf73ae8..5dba839e 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 0d06a1d7..5df077e1 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 958a82b3..a1f11fa6 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 0c7ca7e8..8ca9c862 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 f8810823..cc9a8c31 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 5df077e1..22e5f156 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 00000000..5e7f0abf --- /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 a7011854..2318f0f2 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 2cb657f6..9a63a28d 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 00000000..d133c85c --- /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 b1d21680..e67e7973 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 c51153e6..05281f33 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 7e919985..65c13a24 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 d36d0862..d20ed056 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 216e1877..57cebe80 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 d454a3d9..6f8bb902 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 cc9a8c31..75b092fb 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 5dba839e..3a4795b1 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 9e141ad2..322525cb 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 a1f11fa6..e48e6850 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 108d0ab1..d055476d 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 9e2bba31..ae26c3d2 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 fa6f6c81..97e7abc6 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 8ca9c862..1ed542c1 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 d133c85c..85c81e75 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 9a63a28d..ecbecdff 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 d20ed056..9d142612 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 57cebe80..779af992 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 1ed542c1..bbc31623 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 3a4795b1..add6d173 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 85ac2dfa..1931aabd 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 1931aabd..876aae37 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 a780194d..3f86f90e 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 2318f0f2..9db6539d 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 ae26c3d2..38b183f1 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 75b092fb..2184a843 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 7d0b1f24..da2e6f64 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 2184a843..9f3b80bb 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 c97fa502..d4044f47 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 bb5c2b2e..d8200ae1 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 a2bdd11b..2b986795 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 ecbecdff..995cd11e 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 9f3b80bb..5b52da05 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 288431b9..ad4b437f 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 15135ef3..e3a68f46 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 e67e7973..3e9d8ce1 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 d055476d..12f1f774 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 12f1f774..9f8b1e4c 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 3e9d8ce1..2c3189f0 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 995cd11e..e226f21d 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 e3a68f46..a54be9c2 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 5b52da05..fee722cc 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 9f8b1e4c..74b59ae6 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 00000000..084844e6 --- /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 fee722cc..7eba6799 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 22e5f156..045957d5 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 876aae37..97ad5cd4 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 97ad5cd4..3558d302 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 3558d302..5cc739c8 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), - }) - } -} + fn try_from(elem: Element) -> Result { + check_self!(elem, "file", ns::JINGLE_FT); + check_no_attributes!(elem, "file"); -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() - } -} + let mut file = File { + date: None, + media_type: None, + name: None, + descs: BTreeMap::new(), + size: None, + range: None, + hashes: vec!(), + }; -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.")); - } - - 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())?); - } - 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.")); + 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_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 5cc739c8..3ee4e4a9 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 3ee4e4a9..7e13351f 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 9d142612..c9737b03 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -43,17 +43,28 @@ generate_attribute!(Senders, "senders", { 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); -// 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, @@ -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 7eba6799..ccc84f02 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 d4044f47..fa696d08 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 | 68 ++++----------------------------------- 10 files changed, 91 insertions(+), 364 deletions(-) diff --git a/src/eme.rs b/src/eme.rs index ad4b437f..540b5073 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 2c01e1c7..7d7f3e73 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 7e13351f..15a21c67 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 ed1c6d18..632540ed 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 6f8bb902..f1396c5d 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 ccc84f02..5e767fc2 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 d8200ae1..26cdcf99 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 322525cb..fb309679 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 6ccec7ea..470a4647 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 feeeadbe..6e65e012 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, -} +generate_element_with_only_attributes!(StanzaId, "stanza-id", ns::SID, [ + id: String = "id" => required, + by: Jid = "by" => required, +]); -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!(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 5e767fc2..2a63302c 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 2c3189f0..9964cd8c 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 084844e6..997e2411 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 8166e5b0..347d1dac 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 00000000..a344eb58 --- /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 97c10ad7..53462792 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 51c20393..e4b09924 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 15a21c67..fda8dbef 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 2a63302c..68302afe 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 74b59ae6..cbeb6ef7 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 68302afe..db57ce26 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 65299b70..4a2872b4 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 db57ce26..4a40ae11 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 00000000..97f0adcd --- /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 9db6539d..3a447e9a 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 fda8dbef..3339e015 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 b2e5857d..478dbb31 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 478dbb31..b700eec1 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 3a447e9a..cd55dfbf 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 4a2872b4..82158a40 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 97e7abc6..17642986 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 53462792..e95ed4c2 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 f1396c5d..09b3dc56 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 a54be9c2..9b173b8a 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 540b5073..5d6e7111 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 cd55dfbf..4cf04e74 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 2b986795..27d511ca 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 347d1dac..b4c407c2 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 4a40ae11..e5b5439c 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 b4c407c2..587418b8 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 27d511ca..05f84e1e 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 7d7f3e73..cc2d89c3 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -25,41 +25,26 @@ 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; +impl Base64 { + fn decode(s: &str) -> Result, Error> { + Ok(base64::decode(s)?) + } - 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())?, - }) + fn encode(b: &Vec) -> String { + base64::encode(b) } } -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() - } -} +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 97f0adcd..155767ac 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 9964cd8c..b4efeee9 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 155767ac..136b32f9 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 00000000..e12000ac --- /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 cc2d89c3..d7717a7b 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 e5b5439c..2bc12e05 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 bcaf94ac..535f99c5 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 136b32f9..2d02a033 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 e12000ac..a457081d 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 e95ed4c2..6904ca83 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 b4efeee9..72dc934a 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 2d02a033..9177009d 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 72dc934a..c48c863c 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 9177009d..b1d9374c 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 3339e015..53730a32 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 53730a32..bf584380 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 38b183f1..cb57ed3f 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 e226f21d..6858c114 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.")), - }) + /// `` + Paused => "paused", } -} - -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() - } -} +); #[cfg(test)] mod tests { diff --git a/src/macros.rs b/src/macros.rs index b1d9374c..1569470a 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 bbc31623..4548864c 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 | 40 +++++++--------------------------------- 2 files changed, 23 insertions(+), 33 deletions(-) diff --git a/src/helpers.rs b/src/helpers.rs index a457081d..4db2d2c1 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 b700eec1..0ef386a4 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -11,40 +11,14 @@ use minidom::Element; use error::Error; use ns; +use helpers::TrimmedPlainText; -#[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() - } -} +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 0ef386a4..db91c44f 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 1569470a..b084207a 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 cb57ed3f..cb78e4ef 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, +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, - /// Name of this contact. - pub name: Option, + /// Name of this contact. + name: Option = "name" => optional_empty, - /// Subscription status of this contact. - pub subscription: Subscription, + /// Subscription status of this contact. + subscription: Subscription = "subscription" => default + ], - /// Groups this contact is part of. - pub groups: Vec, -} - -impl TryFrom for Item { - type Err = 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).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) - } -} - -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 2bc12e05..415572c0 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 00000000..c83915be --- /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 045957d5..b8a10bca 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 fb309679..6c25209f 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 b084207a..8fb57363 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 6c25209f..deff9452 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 deff9452..d09c73c4 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 4cf04e74..713b00a0 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 3f86f90e..e07a50a5 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 713b00a0..f27dae87 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 f27dae87..1f1ab4bf 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 415572c0..36b4b3e7 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 b8a10bca..dcc3946c 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 00000000..163ce3ef --- /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 1f1ab4bf..12239020 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 f8d7372b..912ad025 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 36b4b3e7..3f3df913 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 8fb57363..24041cb0 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 dcc3946c..f17e602b 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 00000000..a0ab15bb --- /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 00000000..6974ce50 --- /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 3f3df913..b7afeac6 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 f17e602b..b09c9d7e 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 6974ce50..3e76dba0 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 912ad025..562e656a 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 b7afeac6..5fde6358 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 24041cb0..3eded458 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 b09c9d7e..9f905e60 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 5fde6358..7d9091b6 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 9f905e60..af77fe32 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 00000000..83335ae2 --- /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 00000000..9c54118d --- /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 7d9091b6..e420e233 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 af77fe32..22278862 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 9c54118d..f9ac62fd 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 f9ac62fd..0f42e50a 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 535f99c5..e414b91a 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 c9737b03..ae313766 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 bf584380..c78e1d45 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 09b3dc56..4233d2d9 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 562e656a..dbfbd8af 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 4233d2d9..c670124d 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 add6d173..6bbab781 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 6bbab781..ffd8f219 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 ffd8f219..dad3c486 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 779af992..159ce597 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 550cc61e..9758b800 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 d09c73c4..236ca8c8 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 e48e6850..d25a7b09 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 5e7f0abf..e14bb422 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 2348068b..8ab4ead2 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 05281f33..892c67d9 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 65c13a24..9a7f3b59 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 ae313766..08002bcf 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 c670124d..558eb560 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 dad3c486..c4c4a17f 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 9758b800..14a65302 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 236ca8c8..a6614a63 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 d25a7b09..f4a4f11f 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 17642986..6b9f5439 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 4548864c..4977739d 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 e14bb422..9521dc99 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 3eded458..8fd05770 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 da2e6f64..9a7ed98c 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 6858c114..83ac6f55 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 0f42e50a..a93fc40b 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 6904ca83..8aba744e 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 5d6e7111..9016123f 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 e414b91a..47843cea 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 d7717a7b..2c0497cc 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 e4b09924..f2831ea1 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 632540ed..dca42668 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 8fd05770..5ea49210 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 db91c44f..b3e1aa57 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 26cdcf99..bfd33b86 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 c83915be..80f3147a 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 fa696d08..615580df 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 470a4647..e9ec8326 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 cb78e4ef..358e4a46 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 a0ab15bb..cb192804 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 6e65e012..3fbcfae8 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 83335ae2..168eee1f 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 163ce3ef..e98d278d 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 9b173b8a..1e297409 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 2c0497cc..d5188177 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 c78e1d45..788ff22f 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 dca42668..2cf0fb44 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 558eb560..7ef0bbd2 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 5ea49210..0a056f8d 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 82158a40..115daf65 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 c4c4a17f..36d09a7c 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 a6614a63..1c3d2cc9 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 cbeb6ef7..1d478cad 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 358e4a46..54847e02 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 cb192804..c53eba2f 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 4977739d..79386252 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 1e297409..74985edf 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 d5188177..3471715b 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 08002bcf..1b89dae7 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 788ff22f..e2539f99 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 2cf0fb44..2f6d0b15 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 7ef0bbd2..4e8ca817 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 0a056f8d..45b5c056 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 115daf65..6af739d9 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 36d09a7c..72fcbde4 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 1c3d2cc9..33b20d87 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 1d478cad..13648956 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 54847e02..96d39586 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 c53eba2f..e7a22632 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 79386252..5d26fa25 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 9a7ed98c..d7521e9c 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 83ac6f55..33ce0a9c 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 a93fc40b..778b9618 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 8aba744e..5591c113 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 9016123f..b023a5ab 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 47843cea..472d4c47 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 3471715b..555dfb2e 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 f2831ea1..ea82ee31 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 2f6d0b15..5cc0f256 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 45b5c056..752e5bb8 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 b3e1aa57..a3d159a3 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 bfd33b86..259eb88a 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 80f3147a..5ec89f60 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 615580df..cacf3c35 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 e9ec8326..0c785c1c 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 96d39586..556cf113 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 e7a22632..23a56c43 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 3fbcfae8..a02d8bf4 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 168eee1f..ee934116 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 e98d278d..4257278e 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 d7521e9c..02cc02f3 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 33ce0a9c..a328e234 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 778b9618..7c74ff1a 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 5591c113..84d12ad4 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 b023a5ab..de48c3b0 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 555dfb2e..60653da0 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 ea82ee31..a94a5ee6 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 5cc0f256..d96e9b91 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 752e5bb8..54297b38 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 a3d159a3..c9d6a129 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 259eb88a..edd9605f 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 5ec89f60..ed8b19b6 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 cacf3c35..12368c10 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 0c785c1c..564e8fc9 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 556cf113..01011fde 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 23a56c43..3cb84c78 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 a02d8bf4..bad88002 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 ee934116..9d9eff46 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 4257278e..24021648 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 02cc02f3..20f79bae 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 3e76dba0..45976361 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 997e2411..b24c9e49 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 05f84e1e..cfafef7e 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 a328e234..d341975f 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 7c74ff1a..6f86dc3c 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 74985edf..3d8615de 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 84d12ad4..4786365f 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 c48c863c..136a50ad 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 587418b8..8fcd0c9e 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 de48c3b0..ff3737b0 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 8ab4ead2..70eead56 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 472d4c47..06249ab2 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 60653da0..9c2cdccc 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 892c67d9..99cbcd57 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 a94a5ee6..1312efb9 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 9a7f3b59..8726a9d5 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 1b89dae7..8bca6054 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 e2539f99..eb7e9169 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 d96e9b91..2e68e685 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 4e8ca817..79949d73 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 54297b38..26a4b633 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 6af739d9..d9f88929 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 c9d6a129..43f0d7ac 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 72fcbde4..2fffdb90 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 edd9605f..3dbb86e4 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 ed8b19b6..400a91cd 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 14a65302..4a6fda40 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 33b20d87..b1390dd6 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 12368c10..8159ffe9 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 f4a4f11f..ecb58754 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 13648956..bd7a9351 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 564e8fc9..dfaf5473 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 01011fde..2b62f808 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 6b9f5439..74484e85 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 3cb84c78..98b4f93b 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 5d26fa25..f2501498 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 bad88002..b321b46c 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 9d9eff46..93a71c41 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 9521dc99..e01a40d3 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 24021648..b5aa7903 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 159ce597..268d93a4 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 2fffdb90..f103ba8e 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 b1390dd6..f53799cd 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 ecb58754..9eba1987 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 bd7a9351..8ac16caf 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 f2501498..46496c91 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 26a4b633..af7b7fcf 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 400a91cd..b8a3ef90 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 e420e233..a0b36c34 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 00000000..9844e931 --- /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 22278862..1eb6e301 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 8ac16caf..dea11ba4 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 9eb93944..89b6f8ce 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 dea11ba4..4ac75cf2 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 89b6f8ce..735fb9c6 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 af7b7fcf..0a2648d5 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 0a2648d5..81a7f82d 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 735fb9c6..e12d0c1a 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 00000000..ce1160d8 --- /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 | 301 ++++++++++++++++++++++--------------------- 1 file changed, 153 insertions(+), 148 deletions(-) diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index ce1160d8..5ca62e9a 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.")); } @@ -314,6 +382,88 @@ mod tests { use super::*; use compare_elements::NamespaceAwareCompare; + #[test] + 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!(), + } + + let elem2 = Element::from(pubsub); + assert!(elem1.compare_to(&elem2)); + + 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!(), + } + + let elem2 = Element::from(pubsub); + assert!(elem1.compare_to(&elem2)); + } + + #[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!(), + } + + let elem2 = Element::from(pubsub); + assert!(elem1.compare_to(&elem2)); + } + + #[test] + 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 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 invalid_empty_pubsub() { let elem: Element = "".parse().unwrap(); @@ -323,19 +473,13 @@ mod tests { _ => 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"); + assert_eq!(&publish_options.form.unwrap().form_type.unwrap(), "http://jabber.org/protocol/pubsub#publish-options"); } #[test] @@ -348,143 +492,4 @@ mod tests { 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 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 81a7f82d..20dff441 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 e12d0c1a..badf6729 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 5ca62e9a..d90ca982 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 8bca6054..8d7d309e 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 79949d73..fe9eaa10 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 8fcd0c9e..b96585bc 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 20dff441..d4b6427c 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 d4b6427c..df4b03e0 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 d9f88929..22052cf3 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 8726a9d5..73395c6f 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 73395c6f..e695dc3e 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 45976361..97a75aa3 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 b24c9e49..11287ba7 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 136a50ad..ea49476c 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 9c2cdccc..e33ca3bf 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 99cbcd57..72c02b2b 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 8d7d309e..b64b0e7c 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 22052cf3..664947ee 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 8159ffe9..c2f0a440 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 d90ca982..569a954a 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 2b62f808..e851c485 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 e01a40d3..49b23ad9 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 72c02b2b..6964755c 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 ea49476c..4c70edd2 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 a0b36c34..220a087f 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 1eb6e301..e5f79fb5 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 00000000..994c8da7 --- /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 49b23ad9..cb877336 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 4a6fda40..5132adbf 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 | 211 +++++++++++++++++++------------------------ src/mam.rs | 112 ++++++----------------- src/muc/muc.rs | 51 ++--------- src/muc/user.rs | 140 ++++++++-------------------- src/pubsub/pubsub.rs | 16 ++-- src/sm.rs | 4 +- src/version.rs | 79 ++-------------- 8 files changed, 201 insertions(+), 491 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index b64b0e7c..951a2fa1 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 df4b03e0..9adae519 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -424,14 +424,96 @@ macro_rules! generate_element_with_text { ); } +macro_rules! start_decl { + (Vec, $type:ty) => ( + Vec<$type> + ); + (Option, $type:ty) => ( + Option<$type> + ); + (Required, $type:ty) => ( + $type + ); +} + +macro_rules! start_parse_elem { + ($temp:ident: Vec) => ( + let mut $temp = Vec::new(); + ); + ($temp:ident: Option) => ( + let mut $temp = None; + ); + ($temp:ident: Required) => ( + let mut $temp = None; + ); +} + +macro_rules! do_parse { + ($elem:ident, Element) => ( + $elem.clone() + ); + ($elem:ident, String) => ( + $elem.text() + ); + ($elem:ident, $constructor:ident) => ( + $constructor::try_from($elem.clone())? + ); +} + +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)); + ); +} + +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() + ); + ($parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => ( + $parent.$elem.map(|elem| + ::minidom::Element::builder($name) + .ns(::ns::$ns) + .append(elem) + .build()) + ); + ($parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => ( + $parent.$elem + ); +} + 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, 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: 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: $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: 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: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( $(#[$meta])* #[derive(Debug, Clone)] pub struct $elem { @@ -441,7 +523,7 @@ macro_rules! generate_element_with_children { )* $( $(#[$child_meta])* - pub $child_ident: Vec<$child_type>, + pub $child_ident: start_decl!($coucou, $child_type), )* } @@ -451,12 +533,13 @@ 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_children = vec!(); + $( + start_parse_elem!($child_ident: $coucou); + )* 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); + do_parse_elem!($child_ident: $coucou = $child_constructor => child); continue; } )* @@ -467,7 +550,7 @@ macro_rules! generate_element_with_children { $attr: get_attr!(elem, $attr_name, $attr_action), )* $( - $child_ident: parsed_children, + $child_ident: finish_parse_elem!($child_ident: $coucou = $child_name), )* }) } @@ -481,116 +564,10 @@ 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() } } ); - ($(#[$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)) => ( - 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)] - 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/mam.rs b/src/mam.rs index 664947ee..b835fc50 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 5132adbf..80905a20 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 f53799cd..69060cef 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, -} +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 + ] +); -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!( + 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 569a954a..840232b0 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 994c8da7..4030bf71 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 cb877336..94cfcbb2 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 9adae519..3c07f180 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 4c70edd2..679b06e5 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 ff3737b0..a276ba25 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 e33ca3bf..2edb8ee5 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 1312efb9..22e84460 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 eb7e9169..b9e1d3ef 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 2e68e685..156ba4d3 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 fe9eaa10..94f3c0c7 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 3c07f180..9b950104 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 3dbb86e4..f3019797 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 69060cef..15cea9e4 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 840232b0..807ce908 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 dfaf5473..a557d008 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 4030bf71..3558c8e7 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 b321b46c..16a8d9fc 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 93a71c41..aa48ad9f 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 b5aa7903..78d0cada 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 679b06e5..b6b653c5 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 b96585bc..4caa7d35 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 951a2fa1..1600fed1 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 b9e1d3ef..4cb0fa52 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 9b950104..48a2702d 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 b835fc50..b1965667 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 43f0d7ac..9b9133c5 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 80905a20..355b089f 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 15cea9e4..8baa6271 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 807ce908..8ecb89b6 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 e851c485..bbd0c399 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 3558c8e7..20e9fc61 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 94cfcbb2..1265c162 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 48a2702d..971fe760 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 75656e50..e206ab3c 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 92627d3b..f6f05be3 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", -generate_attribute!(Mode, "mode", { - Tcp => "tcp", - Udp => "udp", -}, Default = Tcp); + /// Direct connection using the given interface. + Direct => "direct", -generate_id!(CandidateId); + /// SOCKS5 relay. + Proxy => "proxy", -generate_id!(StreamId); + /// Tunnel protocol such as Teredo. + Tunnel => "tunnel", + }, Default = Direct +); -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, -]); +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, + + /// The host to connect to. + host: IpAddr = "host" => required, + + /// The JID to request at the given end. + jid: Jid = "jid" => required, + + /// The port to connect to. + port: Option = "port" => optional, + + /// 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 | 219 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 186 insertions(+), 33 deletions(-) diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 85396cac..6b2cff9b 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_attribute!( + /// The type of the error. + ErrorType, "type", { + /// Retry after providing credentials. + Auth => "auth", -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", -}); + /// 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 6b2cff9b..edec4d5b 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 | 247 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 199 insertions(+), 48 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index 264aa182..dab53712 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!( + /// The action attribute. + Action, "action", { + /// Accept a content-add action received from another party. + ContentAccept => "content-accept", -generate_attribute!(Creator, "creator", { - Initiator => "initiator", - Responder => "responder", -}); + /// Add one or more new content definitions to the session. + ContentAdd => "content-add", -generate_attribute!(Senders, "senders", { - Both => "both", - Initiator => "initiator", - None => "none", - Responder => "responder", -}, Default = Both); + /// Change the directionality of media sending. + ContentModify => "content-modify", -// 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); + /// Reject a content-add action received from another party. + ContentReject => "content-reject", -generate_id!(ContentId); + /// 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 a63ab7f7..77901ba1 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 21450da0..cd0a85f2 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 4e40484d..82ab1c4a 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 f96dadb5..27046547 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 4d81b85c..c363107a 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 639bf046..e81358fb 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 edec4d5b..be276614 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 7b4226fb..398b2bfd 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 339ed2b5..c9fabd52 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 cd0a85f2..581e72e2 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 a276ba25..f596dc8f 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 ad4eb92e..11850a29 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 7600d104..dab55a28 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 6ff06bc3..04834518 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 10c0e51c..898a5dd6 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 be276614..37e71be0 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 07815107..07e947a3 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 f596dc8f..51eb2a6d 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 e81358fb..8bde1780 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 dab55a28..7506a3fa 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 dab53712..3102b105 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 1827f093..ffe9b95f 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 2c2a34f9..2e5bba5f 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 2e5bba5f..a438baec 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 77901ba1..25c851fb 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 82ab1c4a..6e71040d 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 398b2bfd..4bfffd7e 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 1922093b..6bfa8a31 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 6b6894c9..87a2fbd2 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 11b4a421..8ae06392 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 25c851fb..7fcaea80 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 c9fabd52..477bdded 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 27229f25..503e1034 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 a4397797..3fccd726 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 a344eb58..5f2038de 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 581e72e2..c4f7518f 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 59166eb8..7b435530 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 6e71040d..d4811b5c 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 51eb2a6d..271b842e 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 b65b4503..fbfacb11 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 bf7fafbd..7063dbc6 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 bede2f0b..e595fe4a 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 72324dee..71256b64 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 27046547..45352a22 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 aeeb836c..b5f27bf0 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 3102b105..38577f84 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 e206ab3c..0a3c022a 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 7438133c..c56c73d4 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 d8e30382..150e7a03 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 f6f05be3..a5fdff0d 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 5793fef0..3deade1d 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 11850a29..44564120 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 7ad264b2..722780f2 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 7506a3fa..2e9d9c65 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 04834518..e8233ee6 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 8612a42c..6bf8bd23 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 9844e931..2d2f90c4 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 0af81c0d..557f535a 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 8bde1780..02a9a9c7 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 898a5dd6..ecf0a21c 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 0bb74393..eeebddf8 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 d16751a1..2f176932 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 f27167b1..5272fd0d 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 e93aa24f..dfc3dff2 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 07e947a3..9dc3698a 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 693796dc..b81ba771 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 4354df50..7ed89743 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 102690cd..2f87437b 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 6bfa8a31..b6ec55d4 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 87a2fbd2..cd3f8fd8 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 8ae06392..7f68930d 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 7fcaea80..22ac1930 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 503e1034..0c9319cd 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 3fccd726..edf2608c 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 c4f7518f..46fec0fb 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 7b435530..7be0efc7 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 d4811b5c..ea5e03df 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 271b842e..417bbe52 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 fbfacb11..11efb610 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 7063dbc6..5fbf5b90 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 e595fe4a..87b335ec 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 71256b64..4d76a9d8 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 b5f27bf0..ff1e7b1f 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 38577f84..7e4ea82b 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 0a3c022a..604e6ee5 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 c56c73d4..e02a4347 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 150e7a03..1bef3269 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 a5fdff0d..904a4d77 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 44564120..819bc462 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 722780f2..dc0c2af7 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 2e9d9c65..9c392b79 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 e8233ee6..06b433f9 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 6bf8bd23..59204322 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 2d2f90c4..7a366657 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 02a9a9c7..db6ac7a6 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 ecf0a21c..49759c77 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 eeebddf8..c1f8641e 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 2f176932..fb321fa5 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 5272fd0d..1ec44af0 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 dfc3dff2..404b54eb 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 480b1d80..750823d3 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 9dc3698a..e45b528c 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 b81ba771..ad8ec4f1 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 7ed89743..cbd95839 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 2f87437b..f2d28ebf 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 c363107a..def14bae 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 7be0efc7..e76b21f2 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 def14bae..5e5d65e8 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 db6ac7a6..2cb115f5 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 a438baec..8fd7253b 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 4bfffd7e..e71b2644 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 b6ec55d4..81187ae3 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 cd3f8fd8..fd89c484 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 7f68930d..8f88845f 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 22ac1930..ce44e54c 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 477bdded..2d4d3830 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 0c9319cd..1fbb7ff3 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 edf2608c..622820d3 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 5f2038de..34bef242 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 46fec0fb..e67b2906 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 e76b21f2..7c765fb1 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 ea5e03df..ecadc153 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 417bbe52..5c13f510 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 11efb610..9f725132 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 5fbf5b90..93a17291 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 4db2d2c1..9d4f3ecb 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 87b335ec..19af5881 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 4d76a9d8..4afe719e 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 45352a22..53ededda 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 ff1e7b1f..1657fcb6 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 7e4ea82b..720eae86 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 604e6ee5..15bb4f32 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 e02a4347..129f7ad4 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 1bef3269..2f90d2a5 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 904a4d77..4d660d28 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 3deade1d..da96b67a 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 819bc462..ec88ead6 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 dc0c2af7..2bc512d8 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 9c392b79..caae40ab 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 06b433f9..24b56919 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 5e5d65e8..a49c1bb8 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 bf0fb308..7d114abd 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 7a366657..360348bd 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 557f535a..ddb636d3 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 2cb115f5..aece0cff 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 61b4396e..c1ccace3 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 52f51007..cf3d9803 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 49759c77..61877f57 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 c1f8641e..86d02309 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 fb321fa5..6bb02ba7 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 1ec44af0..30fa653a 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 404b54eb..b0d08d3e 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 750823d3..ec31ecb1 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 e45b528c..87595d43 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 cbd95839..4e969f4a 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 8fd7253b..2d3122cb 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 | 129 ++++++++++----- 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 | 111 ++++++++----- 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 | 40 +++-- src/muc/user.rs | 183 +++++++++++++++------- src/nick.rs | 24 ++- src/ping.rs | 12 +- src/presence.rs | 224 ++++++++++++++++++--------- src/pubsub/event.rs | 230 +++++++++++++++------------ src/pubsub/pubsub.rs | 219 +++++++++++++++++--------- src/receipts.rs | 12 +- src/roster.rs | 86 +++++++--- src/rsm.rs | 94 ++++++++--- src/sasl.rs | 65 +++++--- src/sm.rs | 30 +++- src/stanza_error.rs | 62 +++++--- src/stanza_id.rs | 29 +++- src/stream.rs | 2 +- src/version.rs | 18 ++- src/websocket.rs | 6 +- 51 files changed, 2283 insertions(+), 1331 deletions(-) diff --git a/src/attention.rs b/src/attention.rs index e71b2644..be33bb3b 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 81187ae3..ddda0392 100644 --- a/src/bind.rs +++ b/src/bind.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 crate::error::Error; +use crate::iq::{IqResultPayload, IqSetPayload}; +use crate::ns; +use jid::Jid; +use minidom::Element; use std::str::FromStr; use try_from::TryFrom; -use minidom::Element; - -use crate::error::Error; -use jid::Jid; -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. /// @@ -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 fd89c484..d34fee3d 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 crate::error::Error; +use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; +use crate::ns; use jid::Jid; use minidom::Element; - -use crate::error::Error; - -use crate::ns; -use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload}; +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 8f88845f..6048b2f0 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 ce44e54c..72b04263 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 85c81e75..df2e4157 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 1fbb7ff3..cb42fd7a 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 622820d3..9d7608df 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 5c13f510..4b8ffbe6 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 36bd0347..f2378a19 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 9f725132..e36ebf8e 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 93a17291..a7773ee1 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 9d4f3ecb..e4d23244 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 19af5881..f778ddf1 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 4afe719e..39aa229d 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -4,18 +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::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; -use minidom::Element; - -use crate::error::Error; - -use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload}; -use crate::data_forms::DataForm; - -use crate::ns; - /// Query for registering against a service. #[derive(Debug, Clone)] pub struct Query { @@ -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 53ededda..df2d005d 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 1657fcb6..d4ee8b41 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 crate::error::Error; +use crate::ns; +use crate::stanza_error::StanzaError; +use jid::Jid; use minidom::Element; use minidom::IntoAttributeValue; - -use jid::Jid; - -use crate::error::Error; - -use crate::ns; - -use crate::stanza_error::StanzaError; +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 720eae86..2a3c0966 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 15bb4f32..381eb21f 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 129f7ad4..1addc1ee 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 2f90d2a5..02c1e992 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 4d660d28..e85390ca 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 54994e19..f35d5b95 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 da96b67a..ac77ed55 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 ec88ead6..8124be9f 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 2bc512d8..f3a62387 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 caae40ab..159bcd2d 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 24b56919..9e7a08f7 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 59204322..da07bf50 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 a49c1bb8..32df468d 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::error::Error; - use std::str::FromStr; use crate::compare_elements::NamespaceAwareCompare; + use crate::error::Error; + use minidom::Element; + use std::str::FromStr; + 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 7d114abd..3f058a17 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 360348bd..edf8dcac 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 ddb636d3..4cb25f64 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 aece0cff..8f3d2cf1 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, + 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()) + 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 c1ccace3..b8f2bc00 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::date::DateTime; - -use crate::error::Error; - -use crate::ns; - use crate::data_forms::DataForm; - -use crate::pubsub::{NodeName, ItemId, Subscription, SubscriptionId}; +use crate::date::DateTime; +use crate::error::Error; +use crate::ns; +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") + 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) - .append(form) + .attr("uri", redirect) .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") - .ns(ns::PUBSUB_EVENT) - .attr("node", node) - .attr("expiry", expiry) - .attr("jid", jid) - .attr("subid", subid) - .attr("subscription", subscription) - .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 cf3d9803..e406d867 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::error::Error; - -use crate::ns; - -use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload}; use crate::data_forms::DataForm; - -use crate::pubsub::{NodeName, ItemId, Subscription, SubscriptionId}; +use crate::error::Error; +use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; +use crate::ns; +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 61877f57..c97d3e11 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 86d02309..40cc7a25 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::error::Error; - use std::str::FromStr; use crate::compare_elements::NamespaceAwareCompare; + use crate::error::Error; + use minidom::Element; + use std::str::FromStr; + 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 6bb02ba7..afa08be6 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 30fa653a..3ae0c7ad 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 b0d08d3e..fc8e7377 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 ec31ecb1..a0848eec 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::message::MessagePayload; -use crate::presence::PresencePayload; use crate::error::Error; -use jid::Jid; +use crate::message::MessagePayload; use crate::ns; +use crate::presence::PresencePayload; +use jid::Jid; 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 87595d43..2bac2de3 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 ad8ec4f1..fa416fd8 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 4e969f4a..c9e08c32 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 f2d28ebf..10693ca7 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 f35d5b95..36eb7882 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 ac77ed55..d1a40740 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 f778ddf1..f2aff83e 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 ddda0392..fe9472b2 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 8f3d2cf1..765e680d 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 afa08be6..0cd048ec 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 3ae0c7ad..05dd250a 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 a0848eec..c5e62026 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 00000000..913762c1 --- /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 00000000..0b4e77f0 --- /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 d1a40740..62c59736 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 3f058a17..2c1a6926 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 765e680d..1f9846c2 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 b8f2bc00..fac8e2fe 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 05dd250a..86e10760 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 2d3122cb..65935bab 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 be33bb3b..c786f313 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 fe9472b2..bd09d4f3 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 d34fee3d..b2fe70df 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 72b04263..3befdc9b 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 75d64d91..9a972aa7 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 381eb21f..359cd8b0 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 62c59736..daccec5c 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 9e7a08f7..e0d22d62 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 32df468d..40528383 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 2c1a6926..a104e319 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 edf8dcac..67a45ff9 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 4cb25f64..6f48dd55 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 1f9846c2..20d63e87 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 fac8e2fe..0cad1cc0 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 40cc7a25..c0d869d1 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 86e10760..21e58572 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 0b4e77f0..ba174224 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 40cc7a25..a64a4793 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 21e58572..6f922901 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 b2fe70df..e12e7e49 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 359cd8b0..d9a73b29 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 ba174224..3f233d21 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 65935bab..0e3b9a36 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 c786f313..bd4c7a15 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 bd09d4f3..9d3a894f 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 e12e7e49..53417f7f 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 3befdc9b..74042be3 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 9a972aa7..4eb173f1 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 d9a73b29..32dd41f6 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 daccec5c..aa89a45c 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 e0d22d62..3bba10e7 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 40528383..fc596fa9 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 a104e319..56c111e8 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 67a45ff9..03f544aa 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 6f48dd55..01853120 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 20d63e87..7e3e8aa8 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 0cad1cc0..0cef2c39 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 c0d869d1..c13b0f59 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 6f922901..45a56653 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 0b4e77f0..cf5215c0 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 45a56653..db9b17e6 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 aa89a45c..d0dfbc06 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 1e049837..e8629727 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 e8629727..b40d2d5d 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 bd4c7a15..1910c8c1 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 9d3a894f..8ded3e6a 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 53417f7f..2b021185 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 6048b2f0..12f95707 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 74042be3..73c79378 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 4eb173f1..2bf2d1b1 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 cb42fd7a..722495b8 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 9d7608df..30dbd29a 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 a6a99e07..1d3389b0 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 4defc94a..a02b44b1 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 79d89b26..d61cf361 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 2fc011b8..846fa6da 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 4b8ffbe6..cd24e43d 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 e36ebf8e..5dfa4aea 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 a7773ee1..041b81fc 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 f2aff83e..96076118 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 39aa229d..f19c47c1 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 df2d005d..bbf3f151 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 d4ee8b41..b155b97c 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 2a3c0966..63c78a0f 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 32dd41f6..fce18cdf 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 1addc1ee..08370e1f 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 02c1e992..1a520223 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 e85390ca..58c4a27d 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 36eb7882..ed16e05e 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 8124be9f..453b4f60 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 f3a62387..639b71ce 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 159bcd2d..fe32300e 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 3bba10e7..05663c8d 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 fc596fa9..9d29cc96 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 56c111e8..46a89333 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 03f544aa..1fbeb102 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 01853120..62bd0b1a 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 7e3e8aa8..4a464f9c 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 0cef2c39..4e8b9311 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 e406d867..637b050a 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 b40d2d5d..29086aec 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 0cd048ec..1dd5f47f 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 db9b17e6..fecdbfa6 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 c5e62026..7a4d421a 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 2bac2de3..c9cefad7 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 e4d23244..fad0b671 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 d0dfbc06..fd3a0ada 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 c9e08c32..5aa4d2fb 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 00000000..2a595f98 --- /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 ffe9b95f..91d25b29 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 ed16e05e..1bb6263a 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 91d25b29..017b2e15 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 0e3b9a36..843dde15 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 843dde15..374d8973 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 374d8973..05cb4f83 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 1bb6263a..7a291f7e 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 017b2e15..fb3c8311 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 fb3c8311..2aff6b30 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 05cb4f83..c3a4afe3 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 7a291f7e..aea3e737 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 2aff6b30..1c022a14 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 c3a4afe3..cf9ae10b 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 041b81fc..d6a1f37b 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 00000000..07f0bde1 --- /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 aea3e737..765c9a15 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 658428f9..7aa4768c 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 07f0bde1..f8507bf0 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 fad0b671..2aa37013 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 f8507bf0..0f2e0962 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 d61cf361..ee673b8f 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 73c79378..419e72cc 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 0f2e0962..48893e4c 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 4e8b9311..5e53acb8 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 a8af5c5a..dd39ea1d 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 5e53acb8..e0f2b598 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 dd39ea1d..81e4e359 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 637b050a..c7bb6466 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 fd3a0ada..0c296abb 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 b155b97c..ccb503ac 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 12f95707..273f7708 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 419e72cc..a7948d19 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 722495b8..4ab78c57 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 30dbd29a..bce94136 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 ee673b8f..9f72cec5 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 846fa6da..ef3a5932 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 ccb503ac..dd04e6b8 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 63c78a0f..25966872 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 fce18cdf..2d61569c 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 58c4a27d..613842f9 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 fe32300e..f2611acc 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 9d29cc96..a4e60a4d 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 46a89333..8232baf0 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 4a464f9c..e7d2a55d 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 e0f2b598..0c616ab6 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 81e4e359..cde7e775 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 fecdbfa6..8f63f092 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 fc8e7377..1f48615e 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 7a4d421a..d254f6b4 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 2aa37013..b3832345 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 cf9ae10b..8aab21dc 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 a7948d19..1a0d5769 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 ef3a5932..352881f1 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 f19c47c1..c98c9093 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 48893e4c..6e2d3ce0 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 273f7708..b1c59b0e 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 6e2d4e3d..9a1ed3e3 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 a02b44b1..80f92edd 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 9f72cec5..43c15903 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 cd24e43d..20623ff2 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 d6a1f37b..c097d034 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 96076118..aa1e07d3 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 bbf3f151..89b62c05 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 25966872..b891ab7c 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 2d61569c..67c055e2 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 08370e1f..019f0342 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 613842f9..1f3a3ee6 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 453b4f60..b68dabb2 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 639b71ce..259e9d12 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 05663c8d..b2600e0b 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 a4e60a4d..898af678 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 8232baf0..73e910ae 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 c7bb6466..5093b3e1 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 c97d3e11..e6090040 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 29086aec..57d440e9 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 8f63f092..9d35072e 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 1f48615e..661d05b9 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 c9cefad7..dd8ed9e0 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 fa416fd8..a77a73ea 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 0c296abb..74e4009f 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 10693ca7..03a1d040 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 2b021185..74e84bc6 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 1a0d5769..452fa6d5 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 9a1ed3e3..296614e9 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 43c15903..0acdcb86 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 dd04e6b8..e62a4cd8 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 b891ab7c..2c6c3fb8 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 67c055e2..3cd9e00a 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 1a520223..8561a649 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 1f3a3ee6..02b97361 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 b68dabb2..cc606923 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 f2611acc..29708c32 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 73e910ae..8965529f 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 e7d2a55d..2bf09d04 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 0c616ab6..8f6951d9 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 1dd5f47f..6b65e3b4 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 9d35072e..ec84fe6c 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 d254f6b4..3488b9c1 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 74e4009f..a67afe1f 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 e62a4cd8..bca785e9 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 0acdcb86..b958cce5 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 2bf09d04..fba1773c 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 3488b9c1..4c09eea7 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 00000000..1ab50d4d --- /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 765c9a15..773c1202 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 7aa4768c..f825d4dc 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 773c1202..fa1153fe 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 f825d4dc..321c6fa8 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 00000000..5713c2e6 --- /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 fa1153fe..ca7840b1 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 b958cce5..87145208 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 a67afe1f..bc2b8342 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 | 64 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index 2c6c3fb8..9edb318f 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,37 +372,38 @@ 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) { + 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.", + )); + } + } 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.")); } - 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, - 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 4c09eea7..fdc0d522 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 fdc0d522..fd0522d7 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 fd0522d7..dcaf5cb8 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 1ab50d4d..95194edc 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 bc2b8342..f4b08335 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 5713c2e6..8cb47da8 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 8cb47da8..40a07a62 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 95194edc..366cfeec 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 c097d034..e1be62b1 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 00000000..29b64173 --- /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 ca7840b1..2a6c3abb 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 321c6fa8..1897dbb4 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 b3832345..b7bdc844 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 40a07a62..3b386705 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 352881f1..36e55c2c 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 00000000..f47715c1 --- /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 29b64173..84f64650 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 1c022a14..3a608458 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 8aab21dc..f99e9a5c 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 2a6c3abb..eb3cc3b4 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 1897dbb4..4f3d2d29 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 00000000..cf1b75c4 --- /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 cf1b75c4..6f18c7c9 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 6f18c7c9..53cef383 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 3a608458..12a14a01 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 f99e9a5c..6ddde6f7 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 fba1773c..c6a1a102 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 6e2d3ce0..7693c05c 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 6ddde6f7..36ed320c 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 f47715c1..80ba863f 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 1910c8c1..a2af3a9c 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 7693c05c..e5c09d2a 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 8ded3e6a..1d509638 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 74e84bc6..b44c4111 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 b1c59b0e..d8b5a9cc 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 452fa6d5..e5e998b1 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 2bf2d1b1..d7ae1e37 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 4ab78c57..a0588177 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 296614e9..323e91ed 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 80f92edd..97a32d25 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 87145208..9d8781dc 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 36e55c2c..d408b28d 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 20623ff2..623a0cd1 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 5dfa4aea..4adb4796 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 e1be62b1..f4b19faa 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 aa1e07d3..8a81b651 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 c98c9093..911b8002 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 89b62c05..cba3698b 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 bca785e9..c5a195d4 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 9edb318f..ed4fe4a8 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 84f64650..e65c3681 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 3cd9e00a..f0b7cf32 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 019f0342..d1fe61f9 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 3b386705..32301438 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 8561a649..bf0478cf 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 366cfeec..108010d0 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 02b97361..0aa79721 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 eb3cc3b4..38546ace 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 cc606923..bd97ed99 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 259e9d12..f632e48c 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 29708c32..582c7f1a 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 b2600e0b..1055af9b 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 da07bf50..8d27c4f0 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 898af678..5428a602 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 8965529f..bc8012d6 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 1fbeb102..07f07f77 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 62bd0b1a..f110088b 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 c6a1a102..de2b77c3 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 8f6951d9..5d65eb78 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 5093b3e1..0e166bd3 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 e6090040..3b54d3cd 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 57d440e9..65c1fdf5 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 6b65e3b4..a4f97365 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 ec84fe6c..3deed9e1 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 53cef383..3ad9e000 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 661d05b9..b4835140 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 dcaf5cb8..f2ae500e 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 dd8ed9e0..2178d7a9 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 a77a73ea..1d4715f2 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 f4b08335..010cde52 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 5aa4d2fb..afc61789 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 03a1d040..b8c02e2b 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 36ed320c..08e98139 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 12a14a01..f0129488 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 f0129488..82253779 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 1d3389b0..f50a1c4f 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 38546ace..ebeddf2f 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 4f3d2d29..978a8523 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 00000000..c1073938 --- /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 f50a1c4f..73dd3bd7 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 c1073938..91866660 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 08e98139..69b94a57 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 911b8002..2d41a6fa 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 978a8523..9d59435d 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 69b94a57..b1e84c36 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 1d509638..408c08bb 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 b44c4111..f218b21d 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 d8b5a9cc..1424686b 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 ebeddf2f..6863afec 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 bc8012d6..3621450a 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 65c1fdf5..dc615c78 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 1d4715f2..021b6425 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 b8c02e2b..dc2670f7 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 1424686b..b40482c6 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 97a32d25..892578c2 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 9d8781dc..2cacc2a7 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 4adb4796..906f016e 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 c5a195d4..68d585d0 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 ed4fe4a8..14574fdb 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 0aa79721..a34b5e31 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 bd97ed99..f0bd0448 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 582c7f1a..fb11e91d 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 de2b77c3..b4e1a1e0 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 dc615c78..073ecfa7 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 f2ae500e..18087baf 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 2178d7a9..eac29085 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 021b6425..112f1b94 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 dc2670f7..d94d81fd 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 b40482c6..c00195a5 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 892578c2..e987a4f0 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 2cacc2a7..abf0e238 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 906f016e..5286323c 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 68d585d0..c8d62257 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 14574fdb..98e5fcbb 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 a34b5e31..969b83f3 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 f0bd0448..6889f2ed 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 fb11e91d..f9bae2b1 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 b4e1a1e0..e501f883 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 073ecfa7..6c871ba6 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 18087baf..db9230eb 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 eac29085..bbbfb48f 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 112f1b94..e6f5be00 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 d94d81fd..1092423a 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 c8d62257..f3aa7f5d 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 a12bd565..5875e3ba 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 5428a602..a4d13f8a 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 3621450a..44af3a9c 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 9d59435d..e89ad67a 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 f110088b..be8ac48c 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 e501f883..b53b6714 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 b53b6714..ffb734c5 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 b1e84c36..fba43cb4 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 82253779..5637b0b2 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 00000000..d4d449fc --- /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 d4d449fc..241965cd 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 00000000..b40c1e2e --- /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 6863afec..607ea1be 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 e89ad67a..c1a5e96c 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 5637b0b2..7a2b3444 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 7a2b3444..a3b472e3 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 607ea1be..634cfd4f 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 cde7e775..4b998b9f 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 010cde52..5b54cf65 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 f4b19faa..8e96d517 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 73dd3bd7..e5fd67e8 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 91866660..cbedb988 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 241965cd..c28d0f93 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 408c08bb..35d22045 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 2d41a6fa..8ddca59d 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 8e96d517..e3f73ca8 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 a3b472e3..a855cb74 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 c28d0f93..b68a7167 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 00000000..704ed0c9 --- /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 634cfd4f..3a4a5dc3 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 c1a5e96c..f5775d53 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 a855cb74..ea8f73c7 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 35d22045..357a7326 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 ea8f73c7..eec3bbb8 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 b68a7167..a3165dc0 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 3a4a5dc3..af2fc129 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 f5775d53..5c59ec03 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 00000000..c4fc0c05 --- /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 c4fc0c05..43ea9e61 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 43ea9e61..76610936 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 76610936..8b9635e6 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 8b9635e6..224e0bb5 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 224e0bb5..1ec6a047 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 1ec6a047..9c43181b 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 8d27c4f0..f4b529c5 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 44af3a9c..d785d235 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 5b54cf65..c4b5c4e0 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 eec3bbb8..5ecea28e 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 a3165dc0..561f49dd 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 af2fc129..d9898123 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 5c59ec03..2c932c3c 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 00000000..7a0b9f3a --- /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 7a0b9f3a..1bbb436a 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 fba43cb4..6690ccf6 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 357a7326..17e1e124 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 f218b21d..f9e6a821 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 323e91ed..f252bb71 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 e5fd67e8..bfcf4e16 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 abf0e238..3c2cde13 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 8ddca59d..d539f2ad 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 98e5fcbb..55a493ef 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 f0b7cf32..b3da6f8b 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 969b83f3..0f246691 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 6889f2ed..edc0116c 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 f9bae2b1..4a78c6c0 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 ffb734c5..4aad1622 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 5d65eb78..9327c50b 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 0e166bd3..c49d5fbf 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 a4f97365..691e5e98 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 3deed9e1..038e9e4f 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 db9230eb..e871a12b 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 1bbb436a..df9b6679 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 b7bdc844..b51b18a9 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 c4b5c4e0..6843369d 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 9c43181b..0216e7c9 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 17e1e124..22beccd1 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 6690ccf6..91f5fccc 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 5ecea28e..05db5eda 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 05db5eda..ed5aae07 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 d9898123..ea09f2f0 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 2c932c3c..e5242b25 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 00000000..86f4829a --- /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 561f49dd..b8099544 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 b8099544..b05c4c42 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 ed5aae07..372ebdde 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 4aad1622..14ae559c 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 22beccd1..16fb487b 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 f9e6a821..741d4340 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 f252bb71..80c9131b 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 3c2cde13..857afaf8 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 d539f2ad..8ced963f 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 55a493ef..4a8e5ac3 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 b3da6f8b..9902fed7 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 0f246691..17c5d996 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 edc0116c..28362bf9 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 9327c50b..30c118de 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 c49d5fbf..727ef2b8 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 691e5e98..8299c9b6 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 038e9e4f..e8f3aa6e 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 e871a12b..1f4dbbe1 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 cbedb988..601b3a4c 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 6843369d..f5fda080 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 0216e7c9..080fb4a2 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 f2378a19..408c649b 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 408c649b..b103711a 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 b103711a..33b2a699 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 e5e998b1..32f8f573 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 d408b28d..8722d065 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 e3f73ca8..9987407a 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 9902fed7..f3fc9a33 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 b51b18a9..a455a139 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 372ebdde..825c7a40 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 00000000..0465f94b --- /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 ea09f2f0..da1af26c 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 e5242b25..f5941258 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 372ebdde..825c7a40 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 b05c4c42..713ca9f5 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 00000000..0465f94b --- /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 ea09f2f0..da1af26c 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 e5242b25..f5941258 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 da1af26c..06048c8e 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 f5941258..ef785082 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 00000000..73065294 --- /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 825c7a40..275600e1 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 713ca9f5..6b8bf5a1 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 275600e1..f1f253dc 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 6b8bf5a1..b9fc9965 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 00000000..a68909eb --- /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 06048c8e..9f6053cc 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 ef785082..328ed482 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 f5fda080..fd7f58cc 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 f1f253dc..43367a60 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 b9fc9965..f591bfff 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 00000000..9ecff61a --- /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 9f6053cc..b69e56ad 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 328ed482..70d68cff 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 a455a139..a78fa7f6 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 9ecff61a..d151f6c0 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 a78fa7f6..7bf3feb1 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 4a8e5ac3..7e27ebbc 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 727ef2b8..875c1989 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 | 66 +++++++++++++++++++++++++++++++++----------------- ChangeLog | 2 ++ 2 files changed, 46 insertions(+), 22 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9f74e6dc..cd0be079 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,37 +4,59 @@ stages: variables: FEATURES: "" + RUST_BACKTRACE: "full" + +.stable: + image: rust:latest + cache: + key: stable + paths: + - target/ + +.nightly: + image: rustlang/rust:nightly + cache: + key: nightly + paths: + - target/ + +.build: + stage: build + script: + - cargo build --verbose --no-default-features --features=$FEATURES + +.test: + stage: test + script: + - cargo test --verbose --no-default-features --features=$FEATURES rust-latest-build: - stage: build - image: rust:latest - script: - - cargo build --verbose --no-default-features --features=$FEATURES + extends: + - .build + - .stable rust-nightly-build: - stage: build - image: rustlang/rust:nightly - script: - - cargo build --verbose --no-default-features --features=$FEATURES + extends: + - .build + - .nightly + rust-latest-test: - stage: test - image: rust:latest - script: - - cargo test --verbose --no-default-features --features=$FEATURES + 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": +rust-latest-build with features=disable-validation: + extends: rust-latest-build + variables: + FEATURES: "disable-validation" + +rust-latest-test with features=disable-validation: extends: rust-latest-test variables: FEATURES: "disable-validation" - -"rust-nightly-test with features=disable-validation": - extends: rust-nightly-test - variables: - FEATURES: "disable-validation" diff --git a/ChangeLog b/ChangeLog index 43367a60..381e8933 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 704ed0c9..edb0c891 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 a68909eb..7ed5ca7a 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 d151f6c0..ba8c9d84 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 f591bfff..3320bfa7 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 e5c09d2a..0940c33a 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 3b54d3cd..af40f641 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 3320bfa7..e75603dc 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 a2af3a9c..83a871cc 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 0940c33a..c475db82 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 16fb487b..4da35641 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 741d4340..1ddfa5c9 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 edb0c891..c78f6c34 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 c00195a5..900a3a91 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 32f8f573..96ffa22a 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 b40c1e2e..43047c2b 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 7ed5ca7a..817a01bb 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 d7ae1e37..4d8f9a4d 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 a0588177..b8120ffd 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 0465f94b..f3a9130d 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 80c9131b..37f94dfa 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 e987a4f0..e7c7449a 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 857afaf8..040ce8c4 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 8722d065..cfcb340f 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 623a0cd1..1014318e 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 5286323c..6517d22f 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 9987407a..ae7b61bc 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 8a81b651..719fd853 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 8ced963f..b1b15286 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 cba3698b..47d1c183 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 f3aa7f5d..64492616 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 ba8c9d84..5fbc0a0c 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 7e27ebbc..4c568bfc 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 e65c3681..150388f6 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 d1fe61f9..f0fb90f0 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 32301438..51a725ee 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 bf0478cf..78bf1db2 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 108010d0..5e686ca9 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 17c5d996..b6df19ed 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 f632e48c..099dadae 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 4a78c6c0..2c7eb299 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 1055af9b..374900f4 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 f4b529c5..4270c588 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 a4d13f8a..01688ec3 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 d785d235..31157f76 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 07f07f77..20ae7a94 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 73065294..418a878b 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 be8ac48c..70663fa3 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 30c118de..2228a067 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 875c1989..8faf9c43 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 af40f641..e837a17f 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 6c871ba6..730da52d 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 8299c9b6..169681d1 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 e8f3aa6e..e3f09005 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 b4835140..a78320f2 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 1f4dbbe1..0bdc2887 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 bbbfb48f..1ba48f85 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 e6f5be00..c1b5e1ea 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 601b3a4c..363cd210 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 df9b6679..ed35357d 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 df2e4157..7494be7b 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 fd7f58cc..107083a4 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 afc61789..028ce968 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 1092423a..fbc1fb2b 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 381e8933..182d5597 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 e75603dc..40b28e3b 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 00000000..2a182f12 --- /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 b69e56ad..2f856e0a 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 70d68cff..5aea7a65 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 107083a4..d63591aa 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 ae7b61bc..56b61355 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 150388f6..e6a3ee23 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 51a725ee..be5dbc97 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 4c568bfc..fbccce0d 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 8faf9c43..39b30c74 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 5e686ca9..7e5557b2 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 40b28e3b..f3f43b13 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 7e5557b2..7f9e5a1a 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 00000000..4d966b86 --- /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 2f856e0a..7e4f034e 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 5aea7a65..46090f09 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 f3f43b13..86c9fdb5 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 00000000..da52b887 --- /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 7f9e5a1a..0e9dcd85 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 7e4f034e..4977a8bc 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 46090f09..231d78ed 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 91f5fccc..203a3cd2 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 86c9fdb5..80dc172d 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 80dc172d..101eddad 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 203a3cd2..2b48af29 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 182d5597..9f9ea7df 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 101eddad..87e6fe95 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 2b48af29..e02950d4 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 0e9dcd85..4fa596a9 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 d63591aa..d703e258 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 fbccce0d..82875686 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 be5dbc97..b7f9e777 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