From c0e45b6a0f3aeee3caaaa8a1a862d517a133c88a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 25 Jan 2025 16:15:11 +0100 Subject: [PATCH] xmpp-parsers: Migrate Caps to xso The hash member has been split into a hash and a ver members, to reflect the attributes. --- parsers/ChangeLog | 2 ++ parsers/src/caps.rs | 70 ++++++++++++++++----------------------------- 2 files changed, 27 insertions(+), 45 deletions(-) diff --git a/parsers/ChangeLog b/parsers/ChangeLog index 486dab65c3c24d34d28a603b7cd2ea0514c55502..1747f8a94724f0601b5d935645f2de1524139307 100644 --- a/parsers/ChangeLog +++ b/parsers/ChangeLog @@ -39,6 +39,8 @@ XXXX-YY-ZZ RELEASER - message::Message id field is now Option instead of Option (!504) - message_correction::Replace id field is now Id instead of String (!504) + - `Caps::hash` has been split into `Caps::hash` (the algo) and + `Caps::ver` (the actual hash), to reflect the attributes (!517) * New parsers/serialisers: - Stream Features (RFC 6120) (!400) - Spam Reporting (XEP-0377) (!506) diff --git a/parsers/src/caps.rs b/parsers/src/caps.rs index f9f6ba1ce547dbad854413df0538ff100d5fa163..7182b5a491ccb5137837a8e32fc3e35008f54f23 100644 --- a/parsers/src/caps.rs +++ b/parsers/src/caps.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 xso::{AsXml, FromXml}; + use crate::data_forms::DataForm; use crate::disco::{DiscoInfoQuery, DiscoInfoResult, Feature, Identity}; use crate::hashes::{Algo, Hash}; @@ -12,70 +14,46 @@ use crate::presence::PresencePayload; use base64::{engine::general_purpose::STANDARD as Base64, Engine}; use blake2::Blake2bVar; use digest::{Digest, Update, VariableOutput}; -use minidom::Element; use sha1::Sha1; use sha2::{Sha256, Sha512}; use sha3::{Sha3_256, Sha3_512}; -use xso::error::{Error, FromElementError}; /// Represents a capability hash for a given client. -#[derive(Debug, Clone)] +/// +/// Warning: This protocol is insecure, you may want to switch to +/// [ecaps2](../ecaps2/index.html) instead, see [this +/// email](https://mail.jabber.org/pipermail/security/2009-July/000812.html). +#[derive(FromXml, AsXml, Debug, Clone)] +#[xml(namespace = ns::CAPS, name = "c")] pub struct Caps { /// Deprecated list of additional feature bundles. + #[xml(attribute(default))] pub ext: Option, /// A URI identifying an XMPP application. + #[xml(attribute)] pub node: String, + /// The algorithm of the hash of these caps. + #[xml(attribute)] + pub hash: Algo, + /// The hash of that application’s /// [disco#info](../disco/struct.DiscoInfoResult.html). - /// - /// Warning: This protocol is insecure, you may want to switch to - /// [ecaps2](../ecaps2/index.html) instead, see [this - /// email](https://mail.jabber.org/pipermail/security/2009-July/000812.html). - pub hash: Hash, + #[xml(attribute(codec = Base64))] + pub ver: Vec, } impl PresencePayload for Caps {} -impl TryFrom for Caps { - type Error = FromElementError; - - fn try_from(elem: Element) -> Result { - 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 hash = Hash { - algo: get_attr!(elem, "hash", Required), - hash: Base64.decode(ver).map_err(Error::text_parse_error)?, - }; - Ok(Caps { - ext: get_attr!(elem, "ext", Option), - node: get_attr!(elem, "node", Required), - hash, - }) - } -} - -impl From for Element { - fn from(caps: Caps) -> Element { - Element::builder("c", ns::CAPS) - .attr("ext", caps.ext) - .attr("hash", caps.hash.algo) - .attr("node", caps.node) - .attr("ver", Base64.encode(&caps.hash.hash)) - .build() - } -} - 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, + hash: hash.algo, + ver: hash.hash, } } } @@ -207,7 +185,7 @@ pub fn hash_caps(data: &[u8], algo: Algo) -> Result { /// caps hash. pub fn query_caps(caps: Caps) -> DiscoInfoQuery { DiscoInfoQuery { - node: Some(format!("{}#{}", caps.node, Base64.encode(&caps.hash.hash))), + node: Some(format!("{}#{}", caps.node, Base64.encode(&caps.ver))), } } @@ -215,6 +193,8 @@ pub fn query_caps(caps: Caps) -> DiscoInfoQuery { mod tests { use super::*; use crate::caps; + use minidom::Element; + use xso::error::{Error, FromElementError}; #[cfg(target_pointer_width = "32")] #[test] @@ -233,9 +213,9 @@ mod tests { 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, Algo::Sha_256); assert_eq!( - caps.hash.hash, + caps.ver, Base64 .decode("K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=") .unwrap() @@ -245,13 +225,13 @@ mod tests { #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_child() { - let elem: Element = "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=".parse().unwrap(); + let elem: Element = "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=".parse().unwrap(); let error = Caps::try_from(elem).unwrap_err(); let message = match error { FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; - assert_eq!(message, "Unknown child in caps element."); + assert_eq!(message, "Unknown child in Caps element."); } #[test]