Compare commits

..
26 changed files with 43 additions and 166 deletions

View file

@ -1,6 +1,4 @@
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()`.

View file

@ -1,6 +1,6 @@
[package]
name = "jid"
version = "0.12.3"
version = "0.12.2"
authors = [
"lumi <lumi@pew.im>",
"Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>",

View file

@ -1,6 +1,6 @@
[package]
name = "xmpp-parsers"
version = "0.23.0"
version = "0.22.0"
authors = [
"Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>",
"Maxime “pep” Buquet <pep@bouah.net>",
@ -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.4", path = "../xso", features = ["macros", "minidom", "panicking-into-impl", "jid", "uuid", "base64", "serde_json"] }
xso = { version = "0.3", 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"] }

View file

@ -1,10 +1,6 @@
Version NEXT:
XXXX-YY-ZZ RELEASER <releaser@domain.example>
Version 0.23.0:
2026-06-11 Jonas Schäfer <jonas@zombofant.net>
XXXX-YY-ZZ RELEASER <admin@example.com>
* 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)
@ -37,7 +33,6 @@ Version 0.23.0:
- 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 <pep@bouah.net>

View file

@ -690,14 +690,6 @@
<xmpp:since>0.20.0</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0463.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:version>0.2.0</xmpp:version>
<xmpp:since>NEXT</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0478.html"/>

View file

@ -6,7 +6,6 @@
use alloc::borrow::Cow;
use core::{
fmt::Write,
num::ParseIntError,
ops::{Deref, DerefMut},
str::FromStr,
@ -182,11 +181,11 @@ impl Hash {
/// Formats this hash into hexadecimal.
pub fn to_hex(&self) -> String {
let mut hex = String::with_capacity(self.hash.len() * 2);
for byte in &self.hash {
write!(&mut hex, "{:02x}", byte).unwrap();
}
hex
self.hash
.iter()
.map(|byte| format!("{:02x}", byte))
.collect::<Vec<_>>()
.join("")
}
/// Formats this hash into colon-separated hexadecimal.

View file

@ -1,69 +0,0 @@
// Copyright (c) 2026 Link Mauve <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 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 `<mav/>` 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<Version>,
/// The latest version the server has.
#[xml(attribute(default))]
until: Option<Version>,
}
#[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 = "<mav xmlns='urn:xmpp:muc:affiliations:1'/>"
.parse()
.unwrap();
let ver = AffiliationsVersioning::try_from(elem).unwrap();
assert!(ver.since.is_none());
assert!(ver.until.is_none());
let elem: Element =
"<mav xmlns='urn:xmpp:muc:affiliations:1' since='9pacabr2q1' until='ruz41312vw'/>"
.parse()
.unwrap();
let ver = AffiliationsVersioning::try_from(elem).unwrap();
assert_eq!(ver.since.unwrap().0, "9pacabr2q1");
assert_eq!(ver.until.unwrap().0, "ruz41312vw");
}
}

View file

@ -11,8 +11,5 @@ 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;

View file

@ -8,7 +8,6 @@
use xso::{AsXml, FromXml};
use crate::message::MessagePayload;
use crate::muc::mav::AffiliationsVersioning;
use crate::ns;
use crate::presence::PresencePayload;
@ -303,10 +302,6 @@ pub struct MucUser {
/// A mediated invite rejection
#[xml(child(default))]
pub decline: Option<Decline>,
/// From XEP-0463: MUC Affiliations Versioning
#[xml(child(default))]
pub affiliations: Option<AffiliationsVersioning>,
}
impl MucUser {
@ -352,7 +347,7 @@ mod tests {
assert_size!(Item, 84);
assert_size!(Invite, 44);
assert_size!(Decline, 44);
assert_size!(MucUser, 136);
assert_size!(MucUser, 112);
}
#[cfg(target_pointer_width = "64")]
@ -367,7 +362,7 @@ mod tests {
assert_size!(Item, 168);
assert_size!(Invite, 88);
assert_size!(Decline, 88);
assert_size!(MucUser, 272);
assert_size!(MucUser, 224);
}
#[test]

View file

@ -304,9 +304,6 @@ 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";

View file

@ -71,10 +71,6 @@ pub struct Item {
/// Groups this contact is part of.
#[xml(child(n = ..))]
pub groups: Vec<Group>,
/// Subscription pre-approval
#[xml(attribute(default))]
pub approved: Option<bool>,
}
/// The contact list of the user.
@ -162,7 +158,6 @@ mod tests {
<item jid='contact@example.org'
subscription='none'
ask='subscribe'
approved='true'
name='MyContact'>
<group>MyBuddies</group>
</item>
@ -180,7 +175,6 @@ 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())
@ -193,7 +187,6 @@ 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())

View file

@ -1,6 +1,6 @@
[package]
name = "tokio-xmpp"
version = "6.0.0"
version = "5.0.0"
authors = ["Astro <astro@spaceboyz.net>", "Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>", "pep <pep+code@bouah.net>", "O01eg <o01eg@yandex.ru>", "SonnyX <randy@vonderweide.nl>", "Paul Fariello <paul@fariello.eu>"]
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.23", path = "../parsers", features = [ "log" ] }
xso = { version = "0.4", path = "../xso" }
xmpp-parsers = { version = "0.22", path = "../parsers", features = [ "log" ] }
xso = { version = "0.3", path = "../xso" }
# these are only needed for starttls ServerConnector support
hickory-resolver = { version = "0.26", optional = true}

View file

@ -1,17 +1,13 @@
Version NEXT:
XXXX-XX-XX RELEASER <releaser@domain.example>
Version 6.0.0:
2026-06-10 Jonas Schäfer <jonas@zombofant.net>
0000-00-00 RELEASER <releaser@domain>
* 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)
- `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.
@ -32,9 +28,10 @@ Version 6.0.0:
- 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 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 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).
Version 5.0.0:
2025-10-28 pep <pep@bouah.net>

View file

@ -15,10 +15,7 @@ use std::io;
use std::sync::Arc;
use tokio::sync::{mpsc, oneshot, Mutex};
use tokio::task::JoinHandle;
use xmpp_parsers::{
jid::{FullJid, Jid},
stream_features::StreamFeatures,
};
use xmpp_parsers::{jid::Jid, stream_features::StreamFeatures};
#[cfg(feature = "direct-tls")]
use crate::connect::DirectTlsServerConnector;
@ -57,7 +54,7 @@ pub struct Client {
// Client worker task
worker: JoinHandle<stanzastream::StanzaReceiver>,
// JID of the logged-in client
bound_jid: Option<FullJid>,
bound_jid: Option<Jid>,
// Stream features of the currently connected stream
features: Option<StreamFeatures>,
// Response tracker for IQs
@ -67,7 +64,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<&FullJid> {
pub fn bound_jid(&self) -> Option<&Jid> {
self.bound_jid.as_ref()
}

View file

@ -11,7 +11,7 @@ use futures::StreamExt;
use futures::{task::Poll, Stream};
use std::sync::Arc;
use tokio::sync::Mutex;
use xmpp_parsers::{jid::FullJid, stream_features::StreamFeatures};
use xmpp_parsers::{jid::Jid, 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<FullJid> {
pub async fn bound_jid(&self) -> Option<Jid> {
self.0.lock().await.bound_jid.clone()
}

View file

@ -36,8 +36,7 @@ impl Stream for Client {
..
} = event
{
// 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.bound_jid = Some(bound_jid.clone());
self.features = Some(features.clone());
}

View file

@ -12,7 +12,7 @@ use core::ops::ControlFlow;
use futures::StreamExt;
use tokio::sync::mpsc;
use tokio::sync::oneshot;
use xmpp_parsers::jid::FullJid;
use xmpp_parsers::jid::Jid;
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<FullJid>,
bound_jid: Option<Jid>,
// Stream features of the currently connected stream
features: Option<StreamFeatures>,
// Response tracker for IQs
@ -81,9 +81,6 @@ 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());
@ -94,7 +91,7 @@ impl ClientWorker {
}
}
StanzaStreamEvent::Stream(StreamEvent::Resumed) => Event::Online {
bound_jid: self.bound_jid.as_ref().unwrap().clone().into(),
bound_jid: self.bound_jid.as_ref().unwrap().clone(),
features: self.features.as_ref().unwrap().clone(),
resumed: true,
},

View file

@ -2,7 +2,7 @@ use std::io;
use futures::{SinkExt, StreamExt};
use tokio::io::{AsyncBufRead, AsyncWrite};
use xmpp_parsers::{component::Handshake, jid::BareJid, ns};
use xmpp_parsers::{component::Handshake, jid::Jid, 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<C: ServerConnector>(
connector: C,
jid: BareJid,
jid: Jid,
password: &str,
timeouts: Timeouts,
) -> Result<XmppStream<C::Stream>, Error> {

View file

@ -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::BareJid;
use xmpp_parsers::jid::Jid;
use crate::{
component::login::component_login,
@ -27,7 +27,7 @@ mod stream;
/// (stanzas). Connection handling however is up to the user.
pub struct Component<C: ServerConnector> {
/// The component's Jabber-Id
pub jid: BareJid,
pub jid: Jid,
stream: XmppStream<C::Stream>,
}
@ -87,7 +87,7 @@ impl<C: ServerConnector> Component<C> {
connector: C,
timeouts: Timeouts,
) -> Result<Self, Error> {
let jid = BareJid::from_str(jid)?;
let jid = Jid::from_str(jid)?;
let stream = component_login(connector, jid.clone(), password, timeouts).await?;
Ok(Component { jid, stream })
}

View file

@ -1,6 +1,6 @@
[package]
name = "xmpp"
version = "0.7.0"
version = "0.6.0"
authors = [
"Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>",
"Maxime “pep” Buquet <pep@bouah.net>",
@ -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 = "6.0", path = "../tokio-xmpp", default-features = false }
tokio-xmpp = { version = "5.0", path = "../tokio-xmpp", default-features = false }
[dev-dependencies]
env_logger = { version = "0.11", default-features = false, features = ["auto-color", "humantime"] }
@ -50,5 +50,3 @@ escape-hatch = []
syntax-highlighting = [ "tokio-xmpp/syntax-highlighting" ]
# Enable serde support in jid crate
serde = [ "tokio-xmpp/serde" ]
vendored-openssl = [ "reqwest/native-tls-vendored" ]

View file

@ -1,8 +1,5 @@
Version NEXT
XXXX-XX-XX [ RELEASER <releaser@domain.example> ]
Version 0.7.0
2026-06-10 [ Jonas Schäfer <jonas@zombofant.net> ]
XXXX-YY-ZZ [ RELEASER <admin@localhost> ]
* Breaking:
- Agent::wait_for_events now return Vec<Event> and sets inner tokio_xmpp Client
auto-reconnect to true... It is still aware of Event::Disconnected but should
@ -31,7 +28,6 @@ Version 0.7.0
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.

View file

@ -13,7 +13,7 @@ use tokio::sync::RwLock;
use crate::{
Config, Error, Event, RoomNick, event_loop,
jid::{BareJid, FullJid, Jid},
jid::{BareJid, 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<&FullJid> {
pub fn bound_jid(&self) -> Option<&Jid> {
self.client.bound_jid()
}
}

View file

@ -1,6 +1,6 @@
[package]
name = "xso_proc"
version = "0.3.0"
version = "0.2.0"
authors = [
"Jonas Schäfer <jonas@zombofant.net>",
]

View file

@ -1,6 +1,6 @@
[package]
name = "xso"
version = "0.4.0"
version = "0.3.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.3", path = "../xso-proc", optional = true }
xso_proc = { version = "0.2", path = "../xso-proc", optional = true }
# optional dependencies to provide text conversion to/from types from/using
# these crates

View file

@ -1,6 +1,4 @@
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
@ -8,7 +6,6 @@ Version 0.4.0, release 2026-06-10:
* 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.

View file

@ -702,7 +702,6 @@ fn read_start_event_io(
))
}
#[cfg(feature = "std")]
fn from_reader_inner<T: FromXml, R: io::BufRead>(
mut reader: rxml::XmlLangTracker<rxml::Reader<R>>,
) -> io::Result<T> {