Support <stream:features> in parsers
This commit is contained in:
parent
cf617e4d7e
commit
5110d5fa96
3 changed files with 174 additions and 0 deletions
|
@ -1,3 +1,8 @@
|
||||||
|
Version NEXT:
|
||||||
|
XXXX-YY-ZZ RELEASER <admin@example.com>
|
||||||
|
* New parsers/serialisers:
|
||||||
|
- Stream Features (RFC 6120) (!400)
|
||||||
|
|
||||||
Version 0.21.0:
|
Version 0.21.0:
|
||||||
2024-07-25 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
|
2024-07-25 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
|
||||||
* New parsers/serialisers:
|
* New parsers/serialisers:
|
||||||
|
|
|
@ -55,6 +55,8 @@ pub mod sasl;
|
||||||
pub mod stanza_error;
|
pub mod stanza_error;
|
||||||
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
|
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
|
||||||
pub mod stream;
|
pub mod stream;
|
||||||
|
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
|
||||||
|
pub mod stream_features;
|
||||||
|
|
||||||
/// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence
|
/// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence
|
||||||
pub mod roster;
|
pub mod roster;
|
||||||
|
|
167
parsers/src/stream_features.rs
Normal file
167
parsers/src/stream_features.rs
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
// Copyright (c) 2024 xmpp-rs contributors
|
||||||
|
//
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
/// Wraps `<stream:features/>`, usually the very first nonza of a
|
||||||
|
/// XMPP stream. Indicates which features are supported.
|
||||||
|
#[derive(FromXml, AsXml, PartialEq, Debug, Default, Clone)]
|
||||||
|
#[xml(namespace = ns::STREAM, name = "features")]
|
||||||
|
pub struct StreamFeatures {
|
||||||
|
/// StartTLS is supported, and may be mandatory.
|
||||||
|
#[xml(child(default))]
|
||||||
|
pub starttls: Option<StartTls>,
|
||||||
|
|
||||||
|
/// Bind is supported.
|
||||||
|
#[xml(child(default))]
|
||||||
|
pub bind: Option<Bind>,
|
||||||
|
|
||||||
|
/// List of supported SASL mechanisms
|
||||||
|
#[xml(child(default))]
|
||||||
|
pub sasl_mechanisms: SaslMechanisms,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// StartTLS is supported, and may be mandatory.
|
||||||
|
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
|
||||||
|
#[xml(namespace = ns::TLS, name = "starttls")]
|
||||||
|
pub struct StartTls {
|
||||||
|
/// Marker for mandatory StartTLS.
|
||||||
|
#[xml(child(default))]
|
||||||
|
pub required: Option<RequiredStartTls>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Marker for mandatory StartTLS.
|
||||||
|
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
|
||||||
|
#[xml(namespace = ns::TLS, name = "required")]
|
||||||
|
pub struct RequiredStartTls;
|
||||||
|
|
||||||
|
/// Bind is supported.
|
||||||
|
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
|
||||||
|
#[xml(namespace = ns::BIND, name = "bind")]
|
||||||
|
pub struct Bind;
|
||||||
|
|
||||||
|
generate_element!(
|
||||||
|
/// List of supported SASL mechanisms
|
||||||
|
#[derive(Default)]
|
||||||
|
SaslMechanisms, "mechanisms", SASL,
|
||||||
|
children: [
|
||||||
|
/// List of information elements describing this avatar.
|
||||||
|
mechanisms: Vec<SaslMechanism> = ("mechanism", SASL) => SaslMechanism,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: Uncomment me when xso supports collections, see
|
||||||
|
// https://gitlab.com/xmpp-rs/xmpp-rs/-/issues/136
|
||||||
|
// #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
|
||||||
|
// #[xml(namespace = ns::SASL, name = "mechanisms")]
|
||||||
|
// pub struct SaslMechanisms {
|
||||||
|
// #[xml(child(default))]
|
||||||
|
// mechanisms: Vec<SaslMechanism>,
|
||||||
|
// }
|
||||||
|
|
||||||
|
/// The name of a SASL mechanism.
|
||||||
|
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
|
||||||
|
#[xml(namespace = ns::SASL, name = "mechanism")]
|
||||||
|
pub struct SaslMechanism {
|
||||||
|
/// The stringy name of the mechanism.
|
||||||
|
#[xml(text)]
|
||||||
|
pub mechanism: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StreamFeatures {
|
||||||
|
/// Can initiate TLS session with this server?
|
||||||
|
pub fn can_starttls(&self) -> bool {
|
||||||
|
self.starttls.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Does server support user resource binding?
|
||||||
|
pub fn can_bind(&self) -> bool {
|
||||||
|
self.bind.is_some()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use minidom::Element;
|
||||||
|
|
||||||
|
#[cfg(target_pointer_width = "32")]
|
||||||
|
#[test]
|
||||||
|
fn test_size() {
|
||||||
|
assert_size!(SaslMechanism, 12);
|
||||||
|
assert_size!(SaslMechanisms, 12);
|
||||||
|
assert_size!(Bind, 0);
|
||||||
|
assert_size!(RequiredStartTls, 0);
|
||||||
|
assert_size!(StartTls, 1);
|
||||||
|
assert_size!(StreamFeatures, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_pointer_width = "64")]
|
||||||
|
#[test]
|
||||||
|
fn test_size() {
|
||||||
|
assert_size!(SaslMechanism, 24);
|
||||||
|
assert_size!(SaslMechanisms, 24);
|
||||||
|
assert_size!(Bind, 0);
|
||||||
|
assert_size!(RequiredStartTls, 0);
|
||||||
|
assert_size!(StartTls, 1);
|
||||||
|
assert_size!(StreamFeatures, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_required_starttls() {
|
||||||
|
let elem: Element = "<stream:features xmlns:stream='http://etherx.jabber.org/streams'>
|
||||||
|
<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'>
|
||||||
|
<required/>
|
||||||
|
</starttls>
|
||||||
|
</stream:features>"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let features = StreamFeatures::try_from(elem).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(features.can_bind(), false);
|
||||||
|
assert_eq!(features.sasl_mechanisms.mechanisms.len(), 0);
|
||||||
|
assert_eq!(features.can_starttls(), true);
|
||||||
|
assert_eq!(features.starttls.unwrap().required.is_some(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// TODO: Unignore me when xso supports collections of unknown children, see
|
||||||
|
// https://gitlab.com/xmpp-rs/xmpp-rs/-/issues/136
|
||||||
|
#[ignore]
|
||||||
|
fn test_deprecated_compression() {
|
||||||
|
let elem: Element = "<stream:features xmlns:stream='http://etherx.jabber.org/streams'>
|
||||||
|
<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
|
||||||
|
<compression xmlns='http://jabber.org/features/compress'>
|
||||||
|
<method>zlib</method>
|
||||||
|
<method>lzw</method>
|
||||||
|
</compression>
|
||||||
|
</stream:features>"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let features = StreamFeatures::try_from(elem).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(features.can_bind(), true);
|
||||||
|
assert_eq!(features.sasl_mechanisms.mechanisms.len(), 0);
|
||||||
|
assert_eq!(features.can_starttls(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_empty_features() {
|
||||||
|
let elem: Element = "<stream:features xmlns:stream='http://etherx.jabber.org/streams'/>"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let features = StreamFeatures::try_from(elem).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(features.can_bind(), false);
|
||||||
|
assert_eq!(features.sasl_mechanisms.mechanisms.len(), 0);
|
||||||
|
assert_eq!(features.can_starttls(), false);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue