1use std::num::{NonZeroU16, NonZeroU32};
2
3use bytes::{Buf, BufMut, Bytes, BytesMut};
4use bytestring::ByteString;
5use serde::{Deserialize, Serialize};
6
7use crate::cert::CertInfo;
8use crate::error::{DecodeError, EncodeError};
9use crate::types::{ConnectFlags, QoS, MQTT, MQTT_LEVEL_5, WILL_QOS_SHIFT};
10use crate::utils::{self, Decode, Encode, Property};
11use crate::v5::{encode::*, property_type as pt, UserProperties, UserProperty};
12
13#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
14pub struct Connect {
16 pub clean_start: bool,
18 pub keep_alive: u16,
20
21 pub session_expiry_interval_secs: u32,
22 pub auth_method: Option<ByteString>,
23 pub auth_data: Option<Bytes>,
24 pub request_problem_info: bool,
25 pub request_response_info: bool,
26 pub receive_max: Option<NonZeroU16>,
27 pub topic_alias_max: u16,
28 pub user_properties: UserProperties,
29 pub max_packet_size: Option<NonZeroU32>,
30
31 pub last_will: Option<LastWill>,
33 pub client_id: ByteString,
35 pub username: Option<ByteString>,
37 pub password: Option<Bytes>,
39 pub cert: Option<CertInfo>,
41}
42
43#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
44pub struct LastWill {
46 pub qos: QoS,
48 pub retain: bool,
50 pub topic: ByteString,
52 pub message: Bytes,
54
55 pub will_delay_interval_sec: Option<u32>,
56 pub correlation_data: Option<Bytes>,
57 pub message_expiry_interval: Option<NonZeroU32>,
58 pub content_type: Option<ByteString>,
59 pub user_properties: UserProperties,
60 pub is_utf8_payload: Option<bool>,
61 pub response_topic: Option<ByteString>,
62}
63
64impl LastWill {
65 fn properties_len(&self) -> usize {
66 encoded_property_size(&self.will_delay_interval_sec)
67 + encoded_property_size(&self.correlation_data)
68 + encoded_property_size(&self.message_expiry_interval)
69 + encoded_property_size(&self.content_type)
70 + encoded_property_size(&self.is_utf8_payload)
71 + encoded_property_size(&self.response_topic)
72 + self.user_properties.encoded_size()
73 }
74}
75
76impl Connect {
77 pub fn client_id<T>(mut self, client_id: T) -> Self
79 where
80 ByteString: From<T>,
81 {
82 self.client_id = client_id.into();
83 self
84 }
85
86 pub fn receive_max(mut self, max: u16) -> Self {
88 if let Some(num) = NonZeroU16::new(max) {
89 self.receive_max = Some(num);
90 } else {
91 self.receive_max = None;
92 }
93 self
94 }
95
96 fn properties_len(&self) -> usize {
97 encoded_property_size(&self.auth_method)
98 + encoded_property_size(&self.auth_data)
99 + encoded_property_size_default(&self.session_expiry_interval_secs, 0)
100 + encoded_property_size_default(&self.request_problem_info, true) + encoded_property_size_default(&self.request_response_info, false) + encoded_property_size(&self.receive_max)
103 + encoded_property_size(&self.max_packet_size)
104 + encoded_property_size_default(&self.topic_alias_max, 0)
105 + self.user_properties.encoded_size()
106 }
107
108 pub(crate) fn decode(src: &mut Bytes) -> Result<Self, DecodeError> {
109 ensure!(src.remaining() >= 10, DecodeError::InvalidLength);
110 let len = src.get_u16();
111
112 ensure!(len == 4 && &src.as_ref()[0..4] == MQTT, DecodeError::InvalidProtocol);
113 src.advance(4);
114
115 let level = src.get_u8();
116 ensure!(level == MQTT_LEVEL_5, DecodeError::UnsupportedProtocolLevel);
117
118 let flags = ConnectFlags::from_bits(src.get_u8()).ok_or(DecodeError::ConnectReservedFlagSet)?;
119 let keep_alive = src.get_u16();
120
121 let mut session_expiry_interval_secs = None;
123 let mut auth_method = None;
124 let mut auth_data = None;
125 let mut request_problem_info = None;
126 let mut request_response_info = None;
127 let mut receive_max = None;
128 let mut topic_alias_max = None;
129 let mut user_properties = Vec::new();
130 let mut max_packet_size = None;
131 let prop_src = &mut utils::take_properties(src)?;
132 while prop_src.has_remaining() {
133 match prop_src.get_u8() {
134 pt::SESS_EXPIRY_INT => session_expiry_interval_secs.read_value(prop_src)?,
135 pt::AUTH_METHOD => auth_method.read_value(prop_src)?,
136 pt::AUTH_DATA => auth_data.read_value(prop_src)?,
137 pt::REQ_PROB_INFO => request_problem_info.read_value(prop_src)?,
138 pt::REQ_RESP_INFO => request_response_info.read_value(prop_src)?,
139 pt::RECEIVE_MAX => receive_max.read_value(prop_src)?,
140 pt::TOPIC_ALIAS_MAX => topic_alias_max.read_value(prop_src)?,
141 pt::USER => user_properties.push(UserProperty::decode(prop_src)?),
142 pt::MAX_PACKET_SIZE => max_packet_size.read_value(prop_src)?,
143 _ => return Err(DecodeError::MalformedPacket),
144 }
145 }
146
147 let client_id = ByteString::decode(src)?;
148
149 ensure!(
150 !client_id.is_empty() || flags.contains(ConnectFlags::CLEAN_START),
152 DecodeError::InvalidClientId
153 );
154
155 let last_will =
156 if flags.contains(ConnectFlags::WILL) { Some(decode_last_will(src, flags)?) } else { None };
157
158 let username =
159 if flags.contains(ConnectFlags::USERNAME) { Some(ByteString::decode(src)?) } else { None };
160 let password = if flags.contains(ConnectFlags::PASSWORD) { Some(Bytes::decode(src)?) } else { None };
161
162 Ok(Connect {
163 clean_start: flags.contains(ConnectFlags::CLEAN_START),
164 keep_alive,
165 session_expiry_interval_secs: session_expiry_interval_secs.unwrap_or(0),
166 auth_method,
167 auth_data,
168 receive_max,
169 topic_alias_max: topic_alias_max.unwrap_or(0u16),
170 request_problem_info: request_problem_info.unwrap_or(true),
171 request_response_info: request_response_info.unwrap_or(false),
172 user_properties,
173 max_packet_size,
174
175 client_id,
176 last_will,
177 username,
178 password,
179 cert: None,
180 })
181 }
182}
183
184impl Default for Connect {
185 fn default() -> Connect {
186 Connect {
187 clean_start: false,
188 keep_alive: 0,
189 session_expiry_interval_secs: 0,
190 auth_method: None,
191 auth_data: None,
192 request_problem_info: true,
193 request_response_info: false,
194 receive_max: None,
195 topic_alias_max: 0,
196 user_properties: Vec::new(),
197 max_packet_size: None,
198 last_will: None,
199 client_id: ByteString::default(),
200 username: None,
201 password: None,
202 cert: None,
203 }
204 }
205}
206
207fn decode_last_will(src: &mut Bytes, flags: ConnectFlags) -> Result<LastWill, DecodeError> {
208 let mut will_delay_interval_sec = None;
209 let mut correlation_data = None;
210 let mut message_expiry_interval = None;
211 let mut content_type = None;
212 let mut user_properties = Vec::new();
213 let mut is_utf8_payload = None;
214 let mut response_topic = None;
215 let prop_src = &mut utils::take_properties(src)?;
216 while prop_src.has_remaining() {
217 match prop_src.get_u8() {
218 pt::WILL_DELAY_INT => will_delay_interval_sec.read_value(prop_src)?,
219 pt::CORR_DATA => correlation_data.read_value(prop_src)?,
220 pt::MSG_EXPIRY_INT => message_expiry_interval.read_value(prop_src)?,
221 pt::CONTENT_TYPE => content_type.read_value(prop_src)?,
222 pt::UTF8_PAYLOAD => is_utf8_payload.read_value(prop_src)?,
223 pt::RESP_TOPIC => response_topic.read_value(prop_src)?,
224 pt::USER => user_properties.push(UserProperty::decode(prop_src)?),
225 _ => return Err(DecodeError::MalformedPacket),
226 }
227 }
228
229 let topic = ByteString::decode(src)?;
230 let message = Bytes::decode(src)?;
231 Ok(LastWill {
232 qos: QoS::try_from((flags & ConnectFlags::WILL_QOS).bits() >> WILL_QOS_SHIFT)?,
233 retain: flags.contains(ConnectFlags::WILL_RETAIN),
234 topic,
235 message,
236 will_delay_interval_sec,
237 correlation_data,
238 message_expiry_interval,
239 content_type,
240 user_properties,
241 is_utf8_payload,
242 response_topic,
243 })
244}
245
246impl EncodeLtd for Connect {
247 fn encoded_size(&self, _limit: u32) -> usize {
248 let prop_len = self.properties_len();
249 6 + 1 + 1 + 2 + var_int_len(prop_len) as usize + prop_len + self.client_id.encoded_size()
256 + self.last_will.as_ref().map_or(0, |will| { let prop_len = will.properties_len();
258 var_int_len(prop_len) as usize + prop_len + will.topic.encoded_size() + will.message.encoded_size()
259 })
260 + self.username.as_ref().map_or(0, |v| v.encoded_size())
261 + self.password.as_ref().map_or(0, |v| v.encoded_size())
262 }
263
264 fn encode(&self, buf: &mut BytesMut, _size: u32) -> Result<(), EncodeError> {
265 b"MQTT".as_ref().encode(buf)?;
266
267 let mut flags = ConnectFlags::empty();
268
269 if self.username.is_some() {
270 flags |= ConnectFlags::USERNAME;
271 }
272 if self.password.is_some() {
273 flags |= ConnectFlags::PASSWORD;
274 }
275
276 if let Some(will) = self.last_will.as_ref() {
277 flags |= ConnectFlags::WILL;
278
279 if will.retain {
280 flags |= ConnectFlags::WILL_RETAIN;
281 }
282
283 flags |= ConnectFlags::from_bits_truncate(u8::from(will.qos) << WILL_QOS_SHIFT);
284 }
285
286 if self.clean_start {
287 flags |= ConnectFlags::CLEAN_START;
288 }
289
290 buf.put_slice(&[MQTT_LEVEL_5, flags.bits()]);
291
292 self.keep_alive.encode(buf)?;
293
294 let prop_len = self.properties_len();
295 utils::write_variable_length(prop_len as u32, buf); encode_property_default(&self.session_expiry_interval_secs, 0, pt::SESS_EXPIRY_INT, buf)?;
298 encode_property(&self.auth_method, pt::AUTH_METHOD, buf)?;
299 encode_property(&self.auth_data, pt::AUTH_DATA, buf)?;
300 encode_property_default(&self.request_problem_info, true, pt::REQ_PROB_INFO, buf)?; encode_property_default(&self.request_response_info, false, pt::REQ_RESP_INFO, buf)?; encode_property(&self.receive_max, pt::RECEIVE_MAX, buf)?;
303 encode_property(&self.max_packet_size, pt::MAX_PACKET_SIZE, buf)?;
304 encode_property_default(&self.topic_alias_max, 0, pt::TOPIC_ALIAS_MAX, buf)?;
305 self.user_properties.encode(buf)?;
306
307 self.client_id.encode(buf)?;
308
309 if let Some(will) = self.last_will.as_ref() {
310 let prop_len = will.properties_len();
311 utils::write_variable_length(prop_len as u32, buf); encode_property(&will.will_delay_interval_sec, pt::WILL_DELAY_INT, buf)?;
314 encode_property(&will.is_utf8_payload, pt::UTF8_PAYLOAD, buf)?;
315 encode_property(&will.message_expiry_interval, pt::MSG_EXPIRY_INT, buf)?;
316 encode_property(&will.content_type, pt::CONTENT_TYPE, buf)?;
317 encode_property(&will.response_topic, pt::RESP_TOPIC, buf)?;
318 encode_property(&will.correlation_data, pt::CORR_DATA, buf)?;
319 will.user_properties.encode(buf)?;
320 will.topic.encode(buf)?;
321 will.message.encode(buf)?;
322 }
323 if let Some(s) = self.username.as_ref() {
324 s.encode(buf)?;
325 }
326 if let Some(pwd) = self.password.as_ref() {
327 pwd.encode(buf)?;
328 }
329 Ok(())
330 }
331}