From d0b28a94b019496dc4ee93293666c3585262a0ef Mon Sep 17 00:00:00 2001 From: Link Mauve Date: Wed, 6 May 2026 20:01:44 +0200 Subject: [PATCH] Implement XEP-0463: MUC Affiliations Versioning --- parsers/ChangeLog | 1 + parsers/doap.xml | 8 +++++ parsers/src/muc/mav.rs | 69 +++++++++++++++++++++++++++++++++++++++++ parsers/src/muc/mod.rs | 3 ++ parsers/src/muc/user.rs | 9 ++++-- parsers/src/ns.rs | 3 ++ 6 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 parsers/src/muc/mav.rs diff --git a/parsers/ChangeLog b/parsers/ChangeLog index f5b167fe..fadf020a 100644 --- a/parsers/ChangeLog +++ b/parsers/ChangeLog @@ -1,6 +1,7 @@ Version NEXT: XXXX-YY-ZZ RELEASER * New parsers/serialisers: + - MUC Affiliations Versioning (XEP-0463) (!668) - Client Access Management (XEP-0494) (!641) - Jingle Content Category (XEP-0507) - Displayed markers (XEP-0333) (!669) diff --git a/parsers/doap.xml b/parsers/doap.xml index fe9d294f..8c0c44ec 100644 --- a/parsers/doap.xml +++ b/parsers/doap.xml @@ -690,6 +690,14 @@ 0.20.0 + + + + complete + 0.2.0 + NEXT + + diff --git a/parsers/src/muc/mav.rs b/parsers/src/muc/mav.rs new file mode 100644 index 00000000..595c6f46 --- /dev/null +++ b/parsers/src/muc/mav.rs @@ -0,0 +1,69 @@ +// Copyright (c) 2026 Link Mauve +// +// 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 xso::{AsXml, FromXml}; + +use crate::ns; + +generate_id!( + /// Unique and opaque string, indicating the last affiliation version sent by the server that + /// the client has seen, and cached. + Version +); + +/// A `` element sent by the client on join, if it wants to keep track of the affiliations in +/// the room. +#[derive(FromXml, AsXml, Debug, Clone, PartialEq)] +#[xml(namespace = ns::MAV, name = "mav")] +pub struct AffiliationsVersioning { + /// Unique and opaque string, indicating the last affiliation version sent by the server that + /// the client has seen, and cached. Sending the mav element without a since attribute is a + /// called bootstrap request, which asks the server for a full response. + #[xml(attribute(default))] + since: Option, + + /// The latest version the server has. + #[xml(attribute(default))] + until: Option, +} + +#[cfg(test)] +mod tests { + use super::*; + use minidom::Element; + + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Version, 12); + assert_size!(AffiliationsVersioning, 24); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(Version, 24); + assert_size!(AffiliationsVersioning, 48); + } + + #[test] + fn test_simple() { + let elem: Element = "" + .parse() + .unwrap(); + let ver = AffiliationsVersioning::try_from(elem).unwrap(); + assert!(ver.since.is_none()); + assert!(ver.until.is_none()); + + let elem: Element = + "" + .parse() + .unwrap(); + let ver = AffiliationsVersioning::try_from(elem).unwrap(); + assert_eq!(ver.since.unwrap().0, "9pacabr2q1"); + assert_eq!(ver.until.unwrap().0, "ruz41312vw"); + } +} diff --git a/parsers/src/muc/mod.rs b/parsers/src/muc/mod.rs index faabf38a..90e28a19 100644 --- a/parsers/src/muc/mod.rs +++ b/parsers/src/muc/mod.rs @@ -11,5 +11,8 @@ pub mod muc; /// The `http://jabber.org/protocol/muc#user` protocol. pub mod user; +/// The `urn:xmpp:muc:affiliations:1` protocol. +pub mod mav; + pub use self::muc::Muc; pub use self::user::MucUser; diff --git a/parsers/src/muc/user.rs b/parsers/src/muc/user.rs index 60ee99d4..869ab805 100644 --- a/parsers/src/muc/user.rs +++ b/parsers/src/muc/user.rs @@ -8,6 +8,7 @@ use xso::{AsXml, FromXml}; use crate::message::MessagePayload; +use crate::muc::mav::AffiliationsVersioning; use crate::ns; use crate::presence::PresencePayload; @@ -302,6 +303,10 @@ pub struct MucUser { /// A mediated invite rejection #[xml(child(default))] pub decline: Option, + + /// From XEP-0463: MUC Affiliations Versioning + #[xml(child(default))] + pub affiliations: Option, } impl MucUser { @@ -347,7 +352,7 @@ mod tests { assert_size!(Item, 84); assert_size!(Invite, 44); assert_size!(Decline, 44); - assert_size!(MucUser, 112); + assert_size!(MucUser, 136); } #[cfg(target_pointer_width = "64")] @@ -362,7 +367,7 @@ mod tests { assert_size!(Item, 168); assert_size!(Invite, 88); assert_size!(Decline, 88); - assert_size!(MucUser, 224); + assert_size!(MucUser, 272); } #[test] diff --git a/parsers/src/ns.rs b/parsers/src/ns.rs index 9e323c76..c567af60 100644 --- a/parsers/src/ns.rs +++ b/parsers/src/ns.rs @@ -304,6 +304,9 @@ pub const SASL_CB: &str = "urn:xmpp:sasl-cb:0"; /// XEP-0444: Message Reactions pub const REACTIONS: &str = "urn:xmpp:reactions:0"; +/// XEP-0463: MUC Affiliations Versioning +pub const MAV: &str = "urn:xmpp:muc:affiliations:1"; + /// XEP-0478: Stream Limits Advertisement pub const STREAM_LIMITS: &str = "urn:xmpp:stream-limits:0";