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, }); }