use crate::{decoder::*, encoder::*, *};
use alloc::{string::String, vec::Vec};
use bytes::{Buf, BufMut, BytesMut};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Protocol {
MQTT311,
MQIsdp,
}
impl Protocol {
pub(crate) fn new(name: &str, level: u8) -> Result<Protocol, Error> {
match (name, level) {
("MQIsdp", 3) => Ok(Protocol::MQIsdp),
("MQTT", 4) => Ok(Protocol::MQTT311),
_ => Err(Error::InvalidProtocol(name.into(), level)),
}
}
pub(crate) fn to_buffer(&self, buf: &mut impl BufMut) -> Result<(), Error> {
match self {
Protocol::MQTT311 => {
Ok(buf.put_slice(&[0u8, 4, 'M' as u8, 'Q' as u8, 'T' as u8, 'T' as u8, 4]))
}
Protocol::MQIsdp => Ok(buf.put_slice(&[
0u8, 4, 'M' as u8, 'Q' as u8, 'i' as u8, 's' as u8, 'd' as u8, 'p' as u8, 4,
])),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct LastWill {
pub topic: String,
pub message: Vec<u8>,
pub qos: QoS,
pub retain: bool,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ConnectReturnCode {
Accepted,
RefusedProtocolVersion,
RefusedIdentifierRejected,
ServerUnavailable,
BadUsernamePassword,
NotAuthorized,
}
impl ConnectReturnCode {
fn to_u8(&self) -> u8 {
match *self {
ConnectReturnCode::Accepted => 0,
ConnectReturnCode::RefusedProtocolVersion => 1,
ConnectReturnCode::RefusedIdentifierRejected => 2,
ConnectReturnCode::ServerUnavailable => 3,
ConnectReturnCode::BadUsernamePassword => 4,
ConnectReturnCode::NotAuthorized => 5,
}
}
pub(crate) fn from_u8(byte: u8) -> Result<ConnectReturnCode, Error> {
match byte {
0 => Ok(ConnectReturnCode::Accepted),
1 => Ok(ConnectReturnCode::RefusedProtocolVersion),
2 => Ok(ConnectReturnCode::RefusedIdentifierRejected),
3 => Ok(ConnectReturnCode::ServerUnavailable),
4 => Ok(ConnectReturnCode::BadUsernamePassword),
5 => Ok(ConnectReturnCode::NotAuthorized),
n => Err(Error::InvalidConnectReturnCode(n)),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Connect {
pub protocol: Protocol,
pub keep_alive: u16,
pub client_id: String,
pub clean_session: bool,
pub last_will: Option<LastWill>,
pub username: Option<String>,
pub password: Option<Vec<u8>>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Connack {
pub session_present: bool,
pub code: ConnectReturnCode,
}
impl Connect {
pub(crate) fn from_buffer(buf: &mut BytesMut) -> Result<Self, Error> {
let protocol_name = read_string(buf)?;
let protocol_level = buf.get_u8();
let protocol = Protocol::new(&protocol_name, protocol_level).unwrap();
let connect_flags = buf.get_u8();
let keep_alive = buf.get_u16();
let client_id = read_string(buf)?;
let last_will = if connect_flags & 0b100 != 0 {
let will_topic = read_string(buf)?;
let will_message = read_bytes(buf)?;
let will_qod = QoS::from_u8((connect_flags & 0b11000) >> 3).unwrap();
Some(LastWill {
topic: will_topic,
message: will_message,
qos: will_qod,
retain: (connect_flags & 0b00100000) != 0,
})
} else {
None
};
let username = if connect_flags & 0b10000000 != 0 {
Some(read_string(buf)?)
} else {
None
};
let password = if connect_flags & 0b01000000 != 0 {
Some(read_bytes(buf)?)
} else {
None
};
let clean_session = (connect_flags & 0b10) != 0;
Ok(Connect {
protocol,
keep_alive,
client_id,
username,
password,
last_will,
clean_session,
})
}
pub(crate) fn to_buffer(&self, buf: &mut impl BufMut) -> Result<(), Error> {
let header: u8 = 0b00010000;
let mut length: usize = 6 + 1 + 1;
let mut connect_flags: u8 = 0b00000000;
if self.clean_session {
connect_flags |= 0b10;
};
length += 2 + self.client_id.len();
length += 2;
if let Some(username) = &self.username {
connect_flags |= 0b10000000;
length += username.len();
length += 2;
};
if let Some(password) = &self.password {
connect_flags |= 0b01000000;
length += password.len();
length += 2;
};
if let Some(last_will) = &self.last_will {
connect_flags |= 0b00000100;
connect_flags |= last_will.qos.to_u8() << 3;
if last_will.retain {
connect_flags |= 0b00100000;
};
length += last_will.message.len();
length += last_will.topic.len();
length += 4;
};
check_remaining(buf, length + 1)?;
buf.put_u8(header);
write_length(length, buf)?;
self.protocol.to_buffer(buf)?;
buf.put_u8(connect_flags);
buf.put_u16(self.keep_alive);
write_string(self.client_id.as_ref(), buf)?;
if let Some(last_will) = &self.last_will {
write_string(last_will.topic.as_ref(), buf)?;
write_bytes(&last_will.message, buf)?;
};
if let Some(username) = &self.username {
write_string(username.as_ref(), buf)?;
};
if let Some(password) = &self.password {
write_bytes(password, buf)?;
};
Ok(())
}
}
impl Connack {
pub(crate) fn from_buffer(buf: &mut impl Buf) -> Result<Self, Error> {
let flags = buf.get_u8();
let return_code = buf.get_u8();
Ok(Connack {
session_present: (flags & 0b1 == 1),
code: ConnectReturnCode::from_u8(return_code)?,
})
}
pub(crate) fn to_buffer(&self, buf: &mut impl BufMut) -> Result<(), Error> {
check_remaining(buf, 4)?;
let header: u8 = 0b00100000;
let length: u8 = 2;
let mut flags: u8 = 0b00000000;
if self.session_present {
flags |= 0b1;
};
let rc = self.code.to_u8();
buf.put_u8(header);
buf.put_u8(length);
buf.put_u8(flags);
buf.put_u8(rc);
Ok(())
}
}