// Copyright (c) 2018 Emmanuel Gil Peyrot // // 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; use crate::stanza_error::DefinedCondition; /// Acknowledgement of the currently received stanzas. #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::SM, name = "a")] pub struct A { /// The last handled stanza. #[xml(attribute)] pub h: u32, } impl A { /// Generates a new `` element. pub fn new(h: u32) -> A { A { h } } } /// 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, /// Whether the client wants to be allowed to resume the stream. #[xml(attribute(default))] pub resume: bool, } impl Enable { /// Generates a new `` element. pub fn new() -> Self { Enable::default() } /// Sets the preferred resumption time in seconds. pub fn with_max(mut self, max: u32) -> Self { self.max = Some(max); self } /// Asks for resumption to be possible. pub fn with_resume(mut self) -> Self { self.resume = true; self } } generate_id!( /// A random identifier used for stream resumption. StreamId ); /// 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, /// The preferred IP, domain, IP:port or domain:port location for /// resumption. #[xml(attribute(default))] pub location: Option, /// The preferred resumption time in seconds by the server. // TODO: should be the infinite integer set ≥ 1. #[xml(attribute(default))] pub max: Option, /// Whether stream resumption is allowed. #[xml(attribute(default))] pub resume: bool, } /// 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, /// The error returned. // XXX: implement the * handling. #[xml(child(default))] pub error: Option, } /// Requests the currently received stanzas by the other party. #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::SM, name = "r")] pub struct R; /// Requests a stream resumption. #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::SM, name = "resume")] pub struct Resume { /// The last handled stanza. #[xml(attribute)] pub h: u32, /// The previous id given by the server on /// [enabled](struct.Enabled.html). #[xml(attribute)] pub previd: StreamId, } /// The response by the server for a successfully resumed stream. #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::SM, name = "resumed")] pub struct Resumed { /// The last handled stanza. #[xml(attribute)] pub h: u32, /// The previous id given by the server on /// [enabled](struct.Enabled.html). #[xml(attribute)] pub previd: StreamId, } /// Represents availability of Stream Management in ``. #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::SM, name = "sm")] pub struct StreamManagement { /// Undocumented `` flag. // TODO: Remove this flag once servers in the wild have been updated to not send it, as it is // completely undocumented in XEP-0198, only appearing in the XML Schema before 1.6.3. #[xml(flag)] pub optional: bool, } /// 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 for crate::stream_error::StreamError { fn from(other: HandledCountTooHigh) -> Self { 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()]) } } /// 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), } #[cfg(test)] mod tests { use super::*; use minidom::Element; #[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); assert_size!(Failed, 24); assert_size!(R, 0); assert_size!(Resume, 16); assert_size!(Resumed, 16); assert_size!(StreamManagement, 1); assert_size!(HandledCountTooHigh, 8); } #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(A, 4); assert_size!(Enable, 12); assert_size!(StreamId, 24); assert_size!(Enabled, 64); assert_size!(Failed, 40); assert_size!(R, 0); assert_size!(Resume, 32); assert_size!(Resumed, 32); assert_size!(StreamManagement, 1); assert_size!(HandledCountTooHigh, 8); } #[test] fn a() { let elem: Element = "".parse().unwrap(); let a = A::try_from(elem).unwrap(); assert_eq!(a.h, 5); } #[test] fn stream_feature() { let elem: Element = "".parse().unwrap(); StreamManagement::try_from(elem).unwrap(); } #[test] fn handle_count_too_high() { let elem: Element = "" .parse() .unwrap(); let elem = HandledCountTooHigh::try_from(elem).unwrap(); assert_eq!(elem.h, 10); assert_eq!(elem.send_count, 8); } #[test] fn resume() { let elem: Element = "" .parse() .unwrap(); let enable = Enable::try_from(elem).unwrap(); assert_eq!(enable.max, None); assert_eq!(enable.resume, true); let elem: Element = "" .parse() .unwrap(); let enabled = Enabled::try_from(elem).unwrap(); let previd = enabled.id.unwrap(); assert_eq!(enabled.resume, true); assert_eq!(previd, StreamId(String::from("coucou"))); assert_eq!(enabled.max, Some(600)); assert_eq!(enabled.location, None); let elem: Element = "" .parse() .unwrap(); let resume = Resume::try_from(elem).unwrap(); assert_eq!(resume.h, 5); assert_eq!(resume.previd, previd); let elem: Element = "" .parse() .unwrap(); let resumed = Resumed::try_from(elem).unwrap(); assert_eq!(resumed.h, 5); assert_eq!(resumed.previd, previd); } #[test] fn test_serialize_failed() { let reference: Element = "" .parse() .unwrap(); let elem: Element = "" .parse() .unwrap(); let error = DefinedCondition::try_from(elem).unwrap(); let failed = Failed { h: None, error: Some(error), }; let serialized: Element = failed.into(); assert_eq!(serialized, reference); } }