xmpp_parsers/
stream_features.rs1use minidom::Element;
8use xso::{AsXml, FromXml};
9
10use crate::bind::BindFeature;
11use crate::ns;
12use crate::sasl2::Authentication;
13use crate::sasl_cb::SaslChannelBinding;
14use crate::starttls::StartTls;
15use crate::stream_limits::Limits;
16
17#[derive(FromXml, AsXml, PartialEq, Debug, Default, Clone)]
20#[xml(namespace = ns::STREAM, name = "features")]
21pub struct StreamFeatures {
22 #[xml(child(default))]
24 pub starttls: Option<StartTls>,
25
26 #[xml(child(default))]
28 pub bind: Option<BindFeature>,
29
30 #[xml(child(default))]
32 pub sasl_mechanisms: SaslMechanisms,
33
34 #[xml(child(default))]
36 pub limits: Option<Limits>,
37
38 #[xml(child(default))]
40 pub sasl2: Option<Authentication>,
41
42 #[xml(child(default))]
44 pub sasl_cb: Option<SaslChannelBinding>,
45
46 #[xml(child(default))]
48 pub stream_management: Option<crate::sm::StreamManagement>,
49
50 #[xml(element(n = ..))]
55 pub others: Vec<Element>,
56}
57
58#[derive(FromXml, AsXml, PartialEq, Debug, Clone, Default)]
60#[xml(namespace = ns::SASL, name = "mechanisms")]
61pub struct SaslMechanisms {
62 #[xml(extract(n = .., name = "mechanism", fields(text(type_ = String))))]
64 pub mechanisms: Vec<String>,
65}
66
67impl StreamFeatures {
68 pub fn can_starttls(&self) -> bool {
70 self.starttls.is_some()
71 }
72
73 pub fn can_bind(&self) -> bool {
75 self.bind.is_some()
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82 use minidom::Element;
83
84 #[cfg(target_pointer_width = "32")]
85 #[test]
86 fn test_size() {
87 assert_size!(SaslMechanisms, 12);
88 assert_size!(StreamFeatures, 92);
89 }
90
91 #[cfg(target_pointer_width = "64")]
92 #[test]
93 fn test_size() {
94 assert_size!(SaslMechanisms, 24);
95 assert_size!(StreamFeatures, 168);
96 }
97
98 #[test]
99 fn test_sasl_mechanisms() {
100 let elem: Element = "<stream:features xmlns:stream='http://etherx.jabber.org/streams'>
101 <mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
102 <mechanism>PLAIN</mechanism>
103 <mechanism>SCRAM-SHA-1</mechanism>
104 <mechanism>SCRAM-SHA-1-PLUS</mechanism>
105 </mechanisms>
106 </stream:features>"
107 .parse()
108 .unwrap();
109
110 let features = StreamFeatures::try_from(elem).unwrap();
111 assert_eq!(
112 features.sasl_mechanisms.mechanisms,
113 ["PLAIN", "SCRAM-SHA-1", "SCRAM-SHA-1-PLUS"]
114 );
115 }
116
117 #[test]
118 fn test_required_starttls() {
119 let elem: Element = "<stream:features xmlns:stream='http://etherx.jabber.org/streams'>
120 <starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'>
121 <required/>
122 </starttls>
123 </stream:features>"
124 .parse()
125 .unwrap();
126
127 let features = StreamFeatures::try_from(elem).unwrap();
128
129 assert_eq!(features.can_bind(), false);
130 assert_eq!(features.sasl_mechanisms.mechanisms.len(), 0);
131 assert_eq!(features.can_starttls(), true);
132 assert_eq!(features.starttls.unwrap().required, true);
133 }
134
135 #[test]
136 fn test_deprecated_compression() {
137 let elem: Element = "<stream:features xmlns:stream='http://etherx.jabber.org/streams'>
138 <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
139 <compression xmlns='http://jabber.org/features/compress'>
140 <method>zlib</method>
141 <method>lzw</method>
142 </compression>
143 </stream:features>"
144 .parse()
145 .unwrap();
146
147 let features = StreamFeatures::try_from(elem).unwrap();
148
149 assert_eq!(features.can_bind(), true);
150 assert_eq!(features.sasl_mechanisms.mechanisms.len(), 0);
151 assert_eq!(features.can_starttls(), false);
152 assert_eq!(features.others.len(), 1);
153
154 let compression = &features.others[0];
155 assert!(compression.is("compression", "http://jabber.org/features/compress"));
156 let mut children = compression.children();
157
158 let child = children.next().expect("zlib not found");
159 assert_eq!(child.name(), "method");
160 let mut texts = child.texts();
161 assert_eq!(texts.next().unwrap(), "zlib");
162 assert_eq!(texts.next(), None);
163
164 let child = children.next().expect("lzw not found");
165 assert_eq!(child.name(), "method");
166 let mut texts = child.texts();
167 assert_eq!(texts.next().unwrap(), "lzw");
168 assert_eq!(texts.next(), None);
169 }
170
171 #[test]
172 fn test_empty_features() {
173 let elem: Element = "<stream:features xmlns:stream='http://etherx.jabber.org/streams'/>"
174 .parse()
175 .unwrap();
176
177 let features = StreamFeatures::try_from(elem).unwrap();
178
179 assert_eq!(features.can_bind(), false);
180 assert_eq!(features.sasl_mechanisms.mechanisms.len(), 0);
181 assert_eq!(features.can_starttls(), false);
182 }
183}