2018-05-18 19:04:02 +02:00
|
|
|
|
// Copyright (c) 2018 Emmanuel Gil Peyrot <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/.
|
|
|
|
|
|
|
2024-07-09 17:01:42 +02:00
|
|
|
|
use xso::{AsXml, FromXml};
|
2024-06-21 17:54:12 +02:00
|
|
|
|
|
|
|
|
|
|
use crate::ns;
|
2018-12-18 15:27:30 +01:00
|
|
|
|
use crate::stanza_error::DefinedCondition;
|
2018-05-18 19:04:02 +02:00
|
|
|
|
|
2024-06-26 13:13:02 +02:00
|
|
|
|
/// Acknowledgement of the currently received stanzas.
|
2024-07-09 17:01:42 +02:00
|
|
|
|
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
|
2024-06-26 13:13:02 +02:00
|
|
|
|
#[xml(namespace = ns::SM, name = "a")]
|
|
|
|
|
|
pub struct A {
|
|
|
|
|
|
/// The last handled stanza.
|
|
|
|
|
|
#[xml(attribute)]
|
|
|
|
|
|
pub h: u32,
|
|
|
|
|
|
}
|
2018-05-18 19:04:02 +02:00
|
|
|
|
|
|
|
|
|
|
impl A {
|
2018-08-08 20:43:49 +02:00
|
|
|
|
/// Generates a new `<a/>` element.
|
2018-05-18 19:04:02 +02:00
|
|
|
|
pub fn new(h: u32) -> A {
|
|
|
|
|
|
A { h }
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-07-24 18:07:06 +02:00
|
|
|
|
/// Client request for enabling stream management.
|
|
|
|
|
|
#[derive(FromXml, AsXml, PartialEq, Debug, Clone, Default)]
|
|
|
|
|
|
#[xml(namespace = ns::SM, name = "enable")]
|
|
|
|
|
|
pub struct Enable {
|
|
|
|
|
|
/// The preferred resumption time in seconds by the client.
|
|
|
|
|
|
// TODO: should be the infinite integer set ≥ 1.
|
|
|
|
|
|
#[xml(attribute(default))]
|
|
|
|
|
|
pub max: Option<u32>,
|
|
|
|
|
|
|
|
|
|
|
|
/// Whether the client wants to be allowed to resume the stream.
|
|
|
|
|
|
#[xml(attribute(default))]
|
2024-08-03 20:02:06 +02:00
|
|
|
|
pub resume: bool,
|
2024-07-24 18:07:06 +02:00
|
|
|
|
}
|
2018-05-18 19:04:02 +02:00
|
|
|
|
|
|
|
|
|
|
impl Enable {
|
2018-08-08 20:43:49 +02:00
|
|
|
|
/// Generates a new `<enable/>` element.
|
2018-05-18 19:04:02 +02:00
|
|
|
|
pub fn new() -> Self {
|
2019-02-21 21:00:58 +01:00
|
|
|
|
Enable::default()
|
2018-05-18 19:04:02 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-08-08 20:43:49 +02:00
|
|
|
|
/// Sets the preferred resumption time in seconds.
|
2018-05-18 19:04:02 +02:00
|
|
|
|
pub fn with_max(mut self, max: u32) -> Self {
|
|
|
|
|
|
self.max = Some(max);
|
|
|
|
|
|
self
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-08-08 20:43:49 +02:00
|
|
|
|
/// Asks for resumption to be possible.
|
2018-05-18 19:04:02 +02:00
|
|
|
|
pub fn with_resume(mut self) -> Self {
|
2024-08-03 20:02:06 +02:00
|
|
|
|
self.resume = true;
|
2018-05-18 19:04:02 +02:00
|
|
|
|
self
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-08-08 20:43:49 +02:00
|
|
|
|
generate_id!(
|
|
|
|
|
|
/// A random identifier used for stream resumption.
|
|
|
|
|
|
StreamId
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2024-07-24 18:07:06 +02:00
|
|
|
|
/// Server response once stream management is enabled.
|
|
|
|
|
|
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
|
|
|
|
|
|
#[xml(namespace = ns::SM, name = "enabled")]
|
|
|
|
|
|
pub struct Enabled {
|
|
|
|
|
|
/// A random identifier used for stream resumption.
|
|
|
|
|
|
#[xml(attribute(default))]
|
|
|
|
|
|
pub id: Option<StreamId>,
|
|
|
|
|
|
|
|
|
|
|
|
/// The preferred IP, domain, IP:port or domain:port location for
|
|
|
|
|
|
/// resumption.
|
|
|
|
|
|
#[xml(attribute(default))]
|
|
|
|
|
|
pub location: Option<String>,
|
|
|
|
|
|
|
|
|
|
|
|
/// The preferred resumption time in seconds by the server.
|
|
|
|
|
|
// TODO: should be the infinite integer set ≥ 1.
|
|
|
|
|
|
#[xml(attribute(default))]
|
|
|
|
|
|
pub max: Option<u32>,
|
|
|
|
|
|
|
|
|
|
|
|
/// Whether stream resumption is allowed.
|
|
|
|
|
|
#[xml(attribute(default))]
|
2024-08-03 20:02:06 +02:00
|
|
|
|
pub resume: bool,
|
2024-07-24 18:07:06 +02:00
|
|
|
|
}
|
2018-05-18 19:04:02 +02:00
|
|
|
|
|
2024-06-30 09:42:55 +02:00
|
|
|
|
/// A stream management error happened.
|
|
|
|
|
|
#[derive(FromXml, AsXml, Debug, PartialEq, Clone)]
|
|
|
|
|
|
#[xml(namespace = ns::SM, name = "failed")]
|
|
|
|
|
|
pub struct Failed {
|
|
|
|
|
|
/// The last handled stanza.
|
|
|
|
|
|
#[xml(attribute)]
|
|
|
|
|
|
pub h: Option<u32>,
|
|
|
|
|
|
|
|
|
|
|
|
/// The error returned.
|
|
|
|
|
|
// XXX: implement the * handling.
|
|
|
|
|
|
#[xml(child(default))]
|
|
|
|
|
|
pub error: Option<DefinedCondition>,
|
|
|
|
|
|
}
|
2018-05-18 19:04:02 +02:00
|
|
|
|
|
2024-06-21 17:54:12 +02:00
|
|
|
|
/// Requests the currently received stanzas by the other party.
|
2024-07-09 17:01:42 +02:00
|
|
|
|
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
|
2024-06-21 17:54:12 +02:00
|
|
|
|
#[xml(namespace = ns::SM, name = "r")]
|
|
|
|
|
|
pub struct R;
|
2018-05-18 19:04:02 +02:00
|
|
|
|
|
2024-06-29 16:34:27 +02:00
|
|
|
|
/// Requests a stream resumption.
|
2024-07-09 17:01:42 +02:00
|
|
|
|
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
|
2024-06-29 16:34:27 +02:00
|
|
|
|
#[xml(namespace = ns::SM, name = "resume")]
|
|
|
|
|
|
pub struct Resume {
|
|
|
|
|
|
/// The last handled stanza.
|
|
|
|
|
|
#[xml(attribute)]
|
|
|
|
|
|
pub h: u32,
|
2018-08-08 20:43:49 +02:00
|
|
|
|
|
2024-06-29 16:34:27 +02:00
|
|
|
|
/// The previous id given by the server on
|
|
|
|
|
|
/// [enabled](struct.Enabled.html).
|
|
|
|
|
|
#[xml(attribute)]
|
|
|
|
|
|
pub previd: StreamId,
|
|
|
|
|
|
}
|
2018-05-18 19:04:02 +02:00
|
|
|
|
|
2024-06-29 16:34:27 +02:00
|
|
|
|
/// The response by the server for a successfully resumed stream.
|
2024-07-09 17:01:42 +02:00
|
|
|
|
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
|
2024-06-29 16:34:27 +02:00
|
|
|
|
#[xml(namespace = ns::SM, name = "resumed")]
|
|
|
|
|
|
pub struct Resumed {
|
|
|
|
|
|
/// The last handled stanza.
|
|
|
|
|
|
#[xml(attribute)]
|
|
|
|
|
|
pub h: u32,
|
2018-08-08 20:43:49 +02:00
|
|
|
|
|
2024-06-29 16:34:27 +02:00
|
|
|
|
/// The previous id given by the server on
|
|
|
|
|
|
/// [enabled](struct.Enabled.html).
|
|
|
|
|
|
#[xml(attribute)]
|
|
|
|
|
|
pub previd: StreamId,
|
|
|
|
|
|
}
|
2018-05-18 19:04:02 +02:00
|
|
|
|
|
2024-06-21 17:54:12 +02:00
|
|
|
|
/// Represents availability of Stream Management in `<stream:features/>`.
|
2024-07-09 17:01:42 +02:00
|
|
|
|
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
|
2024-06-21 17:54:12 +02:00
|
|
|
|
#[xml(namespace = ns::SM, name = "sm")]
|
2025-07-28 15:21:43 +02:00
|
|
|
|
pub struct StreamManagement;
|
2018-05-18 19:04:02 +02:00
|
|
|
|
|
2024-08-18 15:06:35 +02:00
|
|
|
|
/// Application-specific error condition to use when the peer acknowledges
|
|
|
|
|
|
/// more stanzas than the local side has sent.
|
|
|
|
|
|
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
|
|
|
|
|
|
#[xml(namespace = ns::SM, name = "handled-count-too-high")]
|
|
|
|
|
|
pub struct HandledCountTooHigh {
|
|
|
|
|
|
/// The `h` value received by the peer.
|
|
|
|
|
|
#[xml(attribute)]
|
|
|
|
|
|
pub h: u32,
|
|
|
|
|
|
|
|
|
|
|
|
/// The number of stanzas which were in fact sent.
|
|
|
|
|
|
#[xml(attribute = "send-count")]
|
|
|
|
|
|
pub send_count: u32,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl From<HandledCountTooHigh> for crate::stream_error::StreamError {
|
|
|
|
|
|
fn from(other: HandledCountTooHigh) -> Self {
|
2025-07-18 14:39:07 +02:00
|
|
|
|
Self::new(
|
|
|
|
|
|
crate::stream_error::DefinedCondition::UndefinedCondition,
|
|
|
|
|
|
"en",
|
|
|
|
|
|
format!(
|
|
|
|
|
|
"You acknowledged {} stanza(s), while I only sent {} so far.",
|
|
|
|
|
|
other.h, other.send_count
|
|
|
|
|
|
),
|
|
|
|
|
|
)
|
|
|
|
|
|
.with_application_specific(vec![other.into()])
|
2024-08-18 15:06:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-09-02 17:26:01 +02:00
|
|
|
|
/// Enum which allows parsing/serialising any XEP-0198 element.
|
|
|
|
|
|
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
|
|
|
|
|
|
#[xml()]
|
|
|
|
|
|
pub enum Nonza {
|
|
|
|
|
|
/// Request to enable SM
|
|
|
|
|
|
#[xml(transparent)]
|
|
|
|
|
|
Enable(Enable),
|
|
|
|
|
|
|
|
|
|
|
|
/// Successful SM enablement response
|
|
|
|
|
|
#[xml(transparent)]
|
|
|
|
|
|
Enabled(Enabled),
|
|
|
|
|
|
|
|
|
|
|
|
/// Request to resume SM
|
|
|
|
|
|
#[xml(transparent)]
|
|
|
|
|
|
Resume(Resume),
|
|
|
|
|
|
|
|
|
|
|
|
/// Sucessful SM resumption response
|
|
|
|
|
|
#[xml(transparent)]
|
|
|
|
|
|
Resumed(Resumed),
|
|
|
|
|
|
|
|
|
|
|
|
/// Error response
|
|
|
|
|
|
#[xml(transparent)]
|
|
|
|
|
|
Failed(Failed),
|
|
|
|
|
|
|
|
|
|
|
|
/// Acknowledgement
|
|
|
|
|
|
#[xml(transparent)]
|
|
|
|
|
|
Ack(A),
|
|
|
|
|
|
|
|
|
|
|
|
/// Request for an acknowledgement
|
|
|
|
|
|
#[xml(transparent)]
|
|
|
|
|
|
Req(R),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-05-18 19:04:02 +02:00
|
|
|
|
#[cfg(test)]
|
|
|
|
|
|
mod tests {
|
|
|
|
|
|
use super::*;
|
2024-07-24 20:28:22 +02:00
|
|
|
|
use minidom::Element;
|
2018-05-18 19:04:02 +02:00
|
|
|
|
|
2018-10-28 13:10:48 +01:00
|
|
|
|
#[cfg(target_pointer_width = "32")]
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_size() {
|
|
|
|
|
|
assert_size!(A, 4);
|
|
|
|
|
|
assert_size!(Enable, 12);
|
|
|
|
|
|
assert_size!(StreamId, 12);
|
|
|
|
|
|
assert_size!(Enabled, 36);
|
2024-07-10 16:38:00 +02:00
|
|
|
|
assert_size!(Failed, 24);
|
2018-10-28 13:10:48 +01:00
|
|
|
|
assert_size!(R, 0);
|
|
|
|
|
|
assert_size!(Resume, 16);
|
|
|
|
|
|
assert_size!(Resumed, 16);
|
2025-07-28 15:21:43 +02:00
|
|
|
|
assert_size!(StreamManagement, 0);
|
2024-08-18 15:06:35 +02:00
|
|
|
|
assert_size!(HandledCountTooHigh, 8);
|
2018-10-28 13:10:48 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(target_pointer_width = "64")]
|
2018-10-26 14:26:16 +02:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_size() {
|
|
|
|
|
|
assert_size!(A, 4);
|
|
|
|
|
|
assert_size!(Enable, 12);
|
|
|
|
|
|
assert_size!(StreamId, 24);
|
|
|
|
|
|
assert_size!(Enabled, 64);
|
2024-07-10 16:38:00 +02:00
|
|
|
|
assert_size!(Failed, 40);
|
2018-10-26 14:26:16 +02:00
|
|
|
|
assert_size!(R, 0);
|
|
|
|
|
|
assert_size!(Resume, 32);
|
|
|
|
|
|
assert_size!(Resumed, 32);
|
2025-07-28 15:21:43 +02:00
|
|
|
|
assert_size!(StreamManagement, 0);
|
2024-08-18 15:06:35 +02:00
|
|
|
|
assert_size!(HandledCountTooHigh, 8);
|
2018-10-26 14:26:16 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-05-18 19:04:02 +02:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn a() {
|
2022-03-22 23:29:25 +01:00
|
|
|
|
let elem: Element = "<a xmlns='urn:xmpp:sm:3' h='5'/>".parse().unwrap();
|
2018-05-18 19:04:02 +02:00
|
|
|
|
let a = A::try_from(elem).unwrap();
|
|
|
|
|
|
assert_eq!(a.h, 5);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn stream_feature() {
|
|
|
|
|
|
let elem: Element = "<sm xmlns='urn:xmpp:sm:3'/>".parse().unwrap();
|
2025-06-03 23:18:10 +02:00
|
|
|
|
StreamManagement::try_from(elem).unwrap();
|
2018-05-18 19:04:02 +02:00
|
|
|
|
}
|
2018-08-08 20:43:49 +02:00
|
|
|
|
|
2024-08-18 15:06:35 +02:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn handle_count_too_high() {
|
|
|
|
|
|
let elem: Element = "<handled-count-too-high xmlns='urn:xmpp:sm:3' h='10' send-count='8'/>"
|
|
|
|
|
|
.parse()
|
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
let elem = HandledCountTooHigh::try_from(elem).unwrap();
|
|
|
|
|
|
assert_eq!(elem.h, 10);
|
|
|
|
|
|
assert_eq!(elem.send_count, 8);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-08-08 20:43:49 +02:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn resume() {
|
2018-12-18 15:32:05 +01:00
|
|
|
|
let elem: Element = "<enable xmlns='urn:xmpp:sm:3' resume='true'/>"
|
|
|
|
|
|
.parse()
|
|
|
|
|
|
.unwrap();
|
2018-08-08 20:43:49 +02:00
|
|
|
|
let enable = Enable::try_from(elem).unwrap();
|
|
|
|
|
|
assert_eq!(enable.max, None);
|
2024-08-03 20:02:06 +02:00
|
|
|
|
assert_eq!(enable.resume, true);
|
2018-08-08 20:43:49 +02:00
|
|
|
|
|
2018-12-18 15:32:05 +01:00
|
|
|
|
let elem: Element = "<enabled xmlns='urn:xmpp:sm:3' resume='true' id='coucou' max='600'/>"
|
|
|
|
|
|
.parse()
|
|
|
|
|
|
.unwrap();
|
2018-08-08 20:43:49 +02:00
|
|
|
|
let enabled = Enabled::try_from(elem).unwrap();
|
|
|
|
|
|
let previd = enabled.id.unwrap();
|
2024-08-03 20:02:06 +02:00
|
|
|
|
assert_eq!(enabled.resume, true);
|
2018-08-08 20:43:49 +02:00
|
|
|
|
assert_eq!(previd, StreamId(String::from("coucou")));
|
|
|
|
|
|
assert_eq!(enabled.max, Some(600));
|
|
|
|
|
|
assert_eq!(enabled.location, None);
|
|
|
|
|
|
|
2018-12-18 15:32:05 +01:00
|
|
|
|
let elem: Element = "<resume xmlns='urn:xmpp:sm:3' h='5' previd='coucou'/>"
|
|
|
|
|
|
.parse()
|
|
|
|
|
|
.unwrap();
|
2018-08-08 20:43:49 +02:00
|
|
|
|
let resume = Resume::try_from(elem).unwrap();
|
|
|
|
|
|
assert_eq!(resume.h, 5);
|
|
|
|
|
|
assert_eq!(resume.previd, previd);
|
|
|
|
|
|
|
2018-12-18 15:32:05 +01:00
|
|
|
|
let elem: Element = "<resumed xmlns='urn:xmpp:sm:3' h='5' previd='coucou'/>"
|
|
|
|
|
|
.parse()
|
|
|
|
|
|
.unwrap();
|
2018-08-08 20:43:49 +02:00
|
|
|
|
let resumed = Resumed::try_from(elem).unwrap();
|
|
|
|
|
|
assert_eq!(resumed.h, 5);
|
|
|
|
|
|
assert_eq!(resumed.previd, previd);
|
|
|
|
|
|
}
|
2019-11-28 18:00:40 +01:00
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_serialize_failed() {
|
2019-12-05 22:36:45 +01:00
|
|
|
|
let reference: Element = "<failed xmlns='urn:xmpp:sm:3'><unexpected-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></failed>"
|
|
|
|
|
|
.parse()
|
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
|
|
let elem: Element = "<unexpected-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>"
|
2019-12-30 07:35:32 +01:40
|
|
|
|
.parse()
|
|
|
|
|
|
.unwrap();
|
2019-12-05 22:36:45 +01:00
|
|
|
|
|
|
|
|
|
|
let error = DefinedCondition::try_from(elem).unwrap();
|
|
|
|
|
|
|
|
|
|
|
|
let failed = Failed {
|
2019-12-30 07:35:32 +01:40
|
|
|
|
h: None,
|
|
|
|
|
|
error: Some(error),
|
2019-12-05 22:36:45 +01:00
|
|
|
|
};
|
|
|
|
|
|
let serialized: Element = failed.into();
|
|
|
|
|
|
assert_eq!(serialized, reference);
|
2019-11-28 18:00:40 +01:00
|
|
|
|
}
|
2018-05-18 19:04:02 +02:00
|
|
|
|
}
|