diff --git a/jid/CHANGELOG.md b/jid/CHANGELOG.md index a02642b3..65142399 100644 --- a/jid/CHANGELOG.md +++ b/jid/CHANGELOG.md @@ -1,4 +1,6 @@ Version NEXT, release XXXX-XX-XX: + +Version 0.12.3, release 2026-06-11: * Changes: - Add a `DomainRef::with_node_str()` method, matching `with_resource_str()` but for `with_node()`. diff --git a/jid/Cargo.toml b/jid/Cargo.toml index 8b620cef..a815fa0e 100644 --- a/jid/Cargo.toml +++ b/jid/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jid" -version = "0.12.2" +version = "0.12.3" authors = [ "lumi ", "Emmanuel Gil Peyrot ", diff --git a/parsers/Cargo.toml b/parsers/Cargo.toml index 97021a60..6a7760ff 100644 --- a/parsers/Cargo.toml +++ b/parsers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.22.0" +version = "0.23.0" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", @@ -25,7 +25,7 @@ log = { version = "0.4", optional = true } # same repository dependencies jid = { version = "0.12", path = "../jid", features = ["minidom"] } minidom = { version = "0.19", path = "../minidom" } -xso = { version = "0.3", path = "../xso", features = ["macros", "minidom", "panicking-into-impl", "jid", "uuid", "base64", "serde_json"] } +xso = { version = "0.4", path = "../xso", features = ["macros", "minidom", "panicking-into-impl", "jid", "uuid", "base64", "serde_json"] } uuid = { version = "1.9.1", features = ["v4"] } serde_json = { version = "1.0", default-features = false, features = ["alloc"] } diff --git a/parsers/ChangeLog b/parsers/ChangeLog index ca69c307..16bef91e 100644 --- a/parsers/ChangeLog +++ b/parsers/ChangeLog @@ -1,6 +1,10 @@ Version NEXT: -XXXX-YY-ZZ RELEASER +XXXX-YY-ZZ RELEASER + +Version 0.23.0: +2026-06-11 Jonas Schäfer * 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) @@ -33,6 +37,7 @@ XXXX-YY-ZZ RELEASER - jingle_rtp::Description gained a bandwidth child. - Fix cargo test for the disable-validation and component features. - Structs of the bind module now have accessible inner pieces (!675). + - Add 'approved' field to roster item (!683). Version 0.22.0: 2025-10-28 pep 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/hashes.rs b/parsers/src/hashes.rs index f116a481..6f6592b6 100644 --- a/parsers/src/hashes.rs +++ b/parsers/src/hashes.rs @@ -6,6 +6,7 @@ use alloc::borrow::Cow; use core::{ + fmt::Write, num::ParseIntError, ops::{Deref, DerefMut}, str::FromStr, @@ -181,11 +182,11 @@ impl Hash { /// Formats this hash into hexadecimal. pub fn to_hex(&self) -> String { - self.hash - .iter() - .map(|byte| format!("{:02x}", byte)) - .collect::>() - .join("") + let mut hex = String::with_capacity(self.hash.len() * 2); + for byte in &self.hash { + write!(&mut hex, "{:02x}", byte).unwrap(); + } + hex } /// Formats this hash into colon-separated hexadecimal. 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"; diff --git a/parsers/src/roster.rs b/parsers/src/roster.rs index b3d6640f..94e1191d 100644 --- a/parsers/src/roster.rs +++ b/parsers/src/roster.rs @@ -71,6 +71,10 @@ pub struct Item { /// Groups this contact is part of. #[xml(child(n = ..))] pub groups: Vec, + + /// Subscription pre-approval + #[xml(attribute(default))] + pub approved: Option, } /// The contact list of the user. @@ -158,6 +162,7 @@ mod tests { MyBuddies @@ -175,6 +180,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, Ask::None); + assert_eq!(roster.items[0].approved, None); assert_eq!( roster.items[0].groups, vec!(Group::from_str("Friends").unwrap()) @@ -187,6 +193,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, Ask::Subscribe); + assert_eq!(roster.items[3].approved, Some(true)); assert_eq!( roster.items[3].groups, vec!(Group::from_str("MyBuddies").unwrap()) diff --git a/tokio-xmpp/Cargo.toml b/tokio-xmpp/Cargo.toml index 07bb9ff9..74de023b 100644 --- a/tokio-xmpp/Cargo.toml +++ b/tokio-xmpp/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tokio-xmpp" -version = "5.0.0" +version = "6.0.0" authors = ["Astro ", "Emmanuel Gil Peyrot ", "pep ", "O01eg ", "SonnyX ", "Paul Fariello "] description = "Asynchronous XMPP for Rust with tokio" license = "MPL-2.0" @@ -26,8 +26,8 @@ pin-project-lite = { version = "0.2" } thiserror = "2.0" # same repository dependencies sasl = { version = "0.5", path = "../sasl" } -xmpp-parsers = { version = "0.22", path = "../parsers", features = [ "log" ] } -xso = { version = "0.3", path = "../xso" } +xmpp-parsers = { version = "0.23", path = "../parsers", features = [ "log" ] } +xso = { version = "0.4", path = "../xso" } # these are only needed for starttls ServerConnector support hickory-resolver = { version = "0.26", optional = true} diff --git a/tokio-xmpp/ChangeLog b/tokio-xmpp/ChangeLog index c1d3fef6..bb704430 100644 --- a/tokio-xmpp/ChangeLog +++ b/tokio-xmpp/ChangeLog @@ -1,13 +1,17 @@ Version NEXT: -0000-00-00 RELEASER +XXXX-XX-XX RELEASER + +Version 6.0.0: +2026-06-10 Jonas Schäfer * Breaking: - Removed the `disable-validation` feature and made the behaviour the new default. The newly-introduced `pedantic` feature can be used to opt into the previous default behaviour of rejecting all unknown child elements and attributes. - * Breaking: - Add `xmpp_parsers::stream_features::StreamFeatures` to - `tokio_xmpp::event::Online` (!631) + `tokio_xmpp::event::Online` (!631) + - `Component.jid` is now a `BareJid` and not a raw `Jid` (!684) + - `Client.bound_jid` is now a `FullJid` and not a raw `Jid` (!687) * Added: - Expose `client_auth` method to allow manual stream setups for advanced use cases. @@ -28,10 +32,9 @@ Version NEXT: - Drive the XMPP client stream in the background (!631) - Add option to split XMPP client into read and write half (!631) - Remove T: AsXml bound from XmlStream (!675) - - Invalid stanzas are now handled dropped (after increasing the SM - counters) instead of failing the stream. Previously, that might have - caused continuous reconnect loops esp. with stream management (#172, - !675). + - Invalid stanzas are now dropped (after increasing the SM counters) + instead of failing the stream. Previously, that might have caused + continuous reconnect loops esp. with stream management (#172, !675). Version 5.0.0: 2025-10-28 pep diff --git a/tokio-xmpp/src/client/mod.rs b/tokio-xmpp/src/client/mod.rs index 8ba65967..72f9b34a 100644 --- a/tokio-xmpp/src/client/mod.rs +++ b/tokio-xmpp/src/client/mod.rs @@ -15,7 +15,10 @@ use std::io; use std::sync::Arc; use tokio::sync::{mpsc, oneshot, Mutex}; use tokio::task::JoinHandle; -use xmpp_parsers::{jid::Jid, stream_features::StreamFeatures}; +use xmpp_parsers::{ + jid::{FullJid, Jid}, + stream_features::StreamFeatures, +}; #[cfg(feature = "direct-tls")] use crate::connect::DirectTlsServerConnector; @@ -54,7 +57,7 @@ pub struct Client { // Client worker task worker: JoinHandle, // JID of the logged-in client - bound_jid: Option, + bound_jid: Option, // Stream features of the currently connected stream features: Option, // Response tracker for IQs @@ -64,7 +67,7 @@ pub struct Client { impl Client { /// Get the client's bound JID (the one reported by the XMPP /// server). - pub fn bound_jid(&self) -> Option<&Jid> { + pub fn bound_jid(&self) -> Option<&FullJid> { self.bound_jid.as_ref() } diff --git a/tokio-xmpp/src/client/receiver.rs b/tokio-xmpp/src/client/receiver.rs index ec78adb0..9495db49 100644 --- a/tokio-xmpp/src/client/receiver.rs +++ b/tokio-xmpp/src/client/receiver.rs @@ -11,7 +11,7 @@ use futures::StreamExt; use futures::{task::Poll, Stream}; use std::sync::Arc; use tokio::sync::Mutex; -use xmpp_parsers::{jid::Jid, stream_features::StreamFeatures}; +use xmpp_parsers::{jid::FullJid, stream_features::StreamFeatures}; /// Read half of a [`Client`](crate::Client). #[derive(Debug)] @@ -22,7 +22,7 @@ impl ClientReceiver { /// /// See the documentation of [`Client::bound_jid`](crate::Client::bound_jid) for more /// information. - pub async fn bound_jid(&self) -> Option { + pub async fn bound_jid(&self) -> Option { self.0.lock().await.bound_jid.clone() } diff --git a/tokio-xmpp/src/client/stream.rs b/tokio-xmpp/src/client/stream.rs index 4e1c4e32..a62343b2 100644 --- a/tokio-xmpp/src/client/stream.rs +++ b/tokio-xmpp/src/client/stream.rs @@ -36,7 +36,8 @@ impl Stream for Client { .. } = event { - self.bound_jid = Some(bound_jid.clone()); + // This unwrap() will never fail because the server MUST send us a full JID. + self.bound_jid = Some(bound_jid.try_as_full().unwrap().clone()); self.features = Some(features.clone()); } diff --git a/tokio-xmpp/src/client/worker.rs b/tokio-xmpp/src/client/worker.rs index 9f26ad53..ca35765e 100644 --- a/tokio-xmpp/src/client/worker.rs +++ b/tokio-xmpp/src/client/worker.rs @@ -12,7 +12,7 @@ use core::ops::ControlFlow; use futures::StreamExt; use tokio::sync::mpsc; use tokio::sync::oneshot; -use xmpp_parsers::jid::Jid; +use xmpp_parsers::jid::FullJid; use xmpp_parsers::stream_features::StreamFeatures; /// Worker to drive the [`crate::stanzastream`] of a client in the background and continue to @@ -25,7 +25,7 @@ pub struct ClientWorker { // Shutdown signal receiver from frontend shutdown_rx: oneshot::Receiver<()>, // JID of the logged-in client - bound_jid: Option, + bound_jid: Option, // Stream features of the currently connected stream features: Option, // Response tracker for IQs @@ -81,6 +81,9 @@ impl ClientWorker { bound_jid, features, }) => { + // This unwrap() will never fail, because the server always uses our own bound JID. + self.bound_jid = Some(bound_jid.try_as_full().unwrap().clone()); + self.features = Some(features.clone()); self.iq_response_tracker .set_account_jid(bound_jid.to_bare()); @@ -91,7 +94,7 @@ impl ClientWorker { } } StanzaStreamEvent::Stream(StreamEvent::Resumed) => Event::Online { - bound_jid: self.bound_jid.as_ref().unwrap().clone(), + bound_jid: self.bound_jid.as_ref().unwrap().clone().into(), features: self.features.as_ref().unwrap().clone(), resumed: true, }, diff --git a/tokio-xmpp/src/component/login.rs b/tokio-xmpp/src/component/login.rs index d09638c6..a19ccc4b 100644 --- a/tokio-xmpp/src/component/login.rs +++ b/tokio-xmpp/src/component/login.rs @@ -2,7 +2,7 @@ use std::io; use futures::{SinkExt, StreamExt}; use tokio::io::{AsyncBufRead, AsyncWrite}; -use xmpp_parsers::{component::Handshake, jid::Jid, ns}; +use xmpp_parsers::{component::Handshake, jid::BareJid, ns}; use crate::component::ServerConnector; use crate::error::{AuthError, Error}; @@ -11,7 +11,7 @@ use crate::xmlstream::{ReadError, Timeouts, XmppStream, XmppStreamElement}; /// Log into an XMPP server as a client with a jid+pass pub async fn component_login( connector: C, - jid: Jid, + jid: BareJid, password: &str, timeouts: Timeouts, ) -> Result, Error> { diff --git a/tokio-xmpp/src/component/mod.rs b/tokio-xmpp/src/component/mod.rs index 33ebe94d..bd9edf3c 100644 --- a/tokio-xmpp/src/component/mod.rs +++ b/tokio-xmpp/src/component/mod.rs @@ -3,7 +3,7 @@ //! allowed to use any user and resource identifiers in their stanzas. use futures::sink::SinkExt; use std::str::FromStr; -use xmpp_parsers::jid::Jid; +use xmpp_parsers::jid::BareJid; use crate::{ component::login::component_login, @@ -27,7 +27,7 @@ mod stream; /// (stanzas). Connection handling however is up to the user. pub struct Component { /// The component's Jabber-Id - pub jid: Jid, + pub jid: BareJid, stream: XmppStream, } @@ -87,7 +87,7 @@ impl Component { connector: C, timeouts: Timeouts, ) -> Result { - let jid = Jid::from_str(jid)?; + let jid = BareJid::from_str(jid)?; let stream = component_login(connector, jid.clone(), password, timeouts).await?; Ok(Component { jid, stream }) } diff --git a/xmpp/Cargo.toml b/xmpp/Cargo.toml index e5cbc067..9a5d5562 100644 --- a/xmpp/Cargo.toml +++ b/xmpp/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp" -version = "0.6.0" +version = "0.7.0" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", @@ -21,7 +21,7 @@ log = "0.4" reqwest = { version = "0.13", features = ["stream"], default-features = false } tokio-util = { version = "0.7", features = ["codec"] } # same repository dependencies -tokio-xmpp = { version = "5.0", path = "../tokio-xmpp", default-features = false } +tokio-xmpp = { version = "6.0", path = "../tokio-xmpp", default-features = false } [dev-dependencies] env_logger = { version = "0.11", default-features = false, features = ["auto-color", "humantime"] } @@ -50,3 +50,5 @@ escape-hatch = [] syntax-highlighting = [ "tokio-xmpp/syntax-highlighting" ] # Enable serde support in jid crate serde = [ "tokio-xmpp/serde" ] + +vendored-openssl = [ "reqwest/native-tls-vendored" ] diff --git a/xmpp/ChangeLog b/xmpp/ChangeLog index 03a104a3..4bf4e75c 100644 --- a/xmpp/ChangeLog +++ b/xmpp/ChangeLog @@ -1,5 +1,8 @@ Version NEXT -XXXX-YY-ZZ [ RELEASER ] +XXXX-XX-XX [ RELEASER ] + +Version 0.7.0 +2026-06-10 [ Jonas Schäfer ] * Breaking: - Agent::wait_for_events now return Vec and sets inner tokio_xmpp Client auto-reconnect to true... It is still aware of Event::Disconnected but should @@ -28,6 +31,7 @@ XXXX-YY-ZZ [ RELEASER ] Please refer to the crate docs for details. (!581) - Allow joining an already joined room with `Agent::join_room` to enable resyncs. Adds a parameter to `muc::room::JoinRoomSettings`. + - Make bound_jid a FullJid. (!687) * Changed: - Replaced unimplemented! with info! calls. - Only send `Event::RoomSubject` when there's no body as per 0045. diff --git a/xmpp/src/agent.rs b/xmpp/src/agent.rs index 6b526822..8f81a00a 100644 --- a/xmpp/src/agent.rs +++ b/xmpp/src/agent.rs @@ -13,7 +13,7 @@ use tokio::sync::RwLock; use crate::{ Config, Error, Event, RoomNick, event_loop, - jid::{BareJid, Jid}, + jid::{BareJid, FullJid, Jid}, message, muc, parsers::disco::DiscoInfoResult, upload, @@ -111,7 +111,7 @@ impl Agent { /// Get the bound jid of the client. /// /// If the client is not connected, this will be None. - pub fn bound_jid(&self) -> Option<&Jid> { + pub fn bound_jid(&self) -> Option<&FullJid> { self.client.bound_jid() } } diff --git a/xso-proc/Cargo.toml b/xso-proc/Cargo.toml index 41dc87e7..3a4f53f0 100644 --- a/xso-proc/Cargo.toml +++ b/xso-proc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xso_proc" -version = "0.2.0" +version = "0.3.0" authors = [ "Jonas Schäfer ", ] diff --git a/xso/Cargo.toml b/xso/Cargo.toml index e0d40075..fb5b4d97 100644 --- a/xso/Cargo.toml +++ b/xso/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xso" -version = "0.3.0" +version = "0.4.0" edition = "2021" description = "XML Streamed Objects: similar to serde, but XML-native." homepage = "https://xmpp.rs" @@ -13,7 +13,7 @@ license = "MPL-2.0" bytes = { version = "1" } rxml = { version = "0.14.0", default-features = false } minidom = { version = "0.19", path = "../minidom" } -xso_proc = { version = "0.2", path = "../xso-proc", optional = true } +xso_proc = { version = "0.3", path = "../xso-proc", optional = true } # optional dependencies to provide text conversion to/from types from/using # these crates diff --git a/xso/ChangeLog b/xso/ChangeLog index 3ca914a1..9e2c0f01 100644 --- a/xso/ChangeLog +++ b/xso/ChangeLog @@ -1,4 +1,6 @@ Version NEXT: + +Version 0.4.0, release 2026-06-10: * Breaking - Removed the `non-pedantic` feature and made the behaviour the default. - Added the `pedantic` feature, opting into the previous default @@ -6,6 +8,7 @@ Version NEXT: * Changes - Fix some Clippy warnings - Fix build with minidom and without std (!661) + - Fix no_std build * Added - xso::from_bytes_with_options and xso::from_reader_with_options to allow passing custom parser configuration. diff --git a/xso/src/lib.rs b/xso/src/lib.rs index a2eb3fdd..299c5873 100644 --- a/xso/src/lib.rs +++ b/xso/src/lib.rs @@ -702,6 +702,7 @@ fn read_start_event_io( )) } +#[cfg(feature = "std")] fn from_reader_inner( mut reader: rxml::XmlLangTracker>, ) -> io::Result {