use std::{convert::TryInto, io};
use crate::pdu::formats::{COctetString, Integer1, OctetString, WriteStream};
use crate::pdu::pduparseerror::fld;
use crate::pdu::tlvs::Tlvs;
use crate::pdu::{PduParseError, PduParseErrorBody};
const MAX_LENGTH_SERVICE_TYPE: usize = 6;
const MAX_LENGTH_SOURCE_ADDR: usize = 21;
const MAX_LENGTH_DESTINATION_ADDR: usize = 21;
const MAX_LENGTH_SCHEDULE_DELIVERY_TIME: usize = 17;
const MAX_LENGTH_VALIDITY_PERIOD: usize = 17;
const MAX_LENGTH_SHORT_MESSAGE: usize = 254;
#[derive(Debug, PartialEq)]
pub struct SmData {
pub service_type: COctetString,
pub source_addr_ton: Integer1,
pub source_addr_npi: Integer1,
pub source_addr: COctetString,
pub dest_addr_ton: Integer1,
pub dest_addr_npi: Integer1,
pub destination_addr: COctetString,
pub esm_class: Integer1,
pub protocol_id: Integer1,
pub priority_flag: Integer1,
pub schedule_delivery_time: COctetString,
pub validity_period: COctetString,
pub registered_delivery: Integer1,
pub replace_if_present_flag: Integer1,
pub data_coding: Integer1,
pub sm_default_msg_id: Integer1,
pub short_message: OctetString,
pub tlvs: Tlvs,
}
impl SmData {
pub fn new(
service_type: &str,
source_addr_ton: u8,
source_addr_npi: u8,
source_addr: &str,
dest_addr_ton: u8,
dest_addr_npi: u8,
destination_addr: &str,
esm_class: u8,
protocol_id: u8,
priority_flag: u8,
schedule_delivery_time: &str,
validity_period: &str,
registered_delivery: u8,
replace_if_present_flag: u8,
data_coding: u8,
sm_default_msg_id: u8,
short_message: &[u8],
tlvs: Tlvs,
) -> Result<Self, PduParseError> {
validate_length_1_or_17(
"schedule_delivery_time",
schedule_delivery_time.len(),
)?;
validate_length_1_or_17("validity_period", validity_period.len())?;
if short_message.len() > 254 {
return Err(PduParseError::new(
PduParseErrorBody::IncorrectLength(
short_message.len().try_into().unwrap_or(0),
String::from("short_message must be less than 255 bytes."),
),
));
}
Ok(Self {
service_type: COctetString::from_str(
service_type,
MAX_LENGTH_SERVICE_TYPE,
)?,
source_addr_ton: Integer1::new(source_addr_ton),
source_addr_npi: Integer1::new(source_addr_npi),
source_addr: COctetString::from_str(
source_addr,
MAX_LENGTH_SOURCE_ADDR,
)?,
dest_addr_ton: Integer1::new(dest_addr_ton),
dest_addr_npi: Integer1::new(dest_addr_npi),
destination_addr: COctetString::from_str(
destination_addr,
MAX_LENGTH_DESTINATION_ADDR,
)?,
esm_class: Integer1::new(esm_class),
protocol_id: Integer1::new(protocol_id),
priority_flag: Integer1::new(priority_flag),
schedule_delivery_time: COctetString::from_str(
schedule_delivery_time,
MAX_LENGTH_SCHEDULE_DELIVERY_TIME,
)?,
validity_period: fld(
"validity_period",
COctetString::from_str(
validity_period,
MAX_LENGTH_VALIDITY_PERIOD,
),
)?,
registered_delivery: Integer1::new(registered_delivery),
replace_if_present_flag: Integer1::new(replace_if_present_flag),
data_coding: Integer1::new(data_coding),
sm_default_msg_id: Integer1::new(sm_default_msg_id),
short_message: fld(
"short_message",
OctetString::from_bytes(
short_message,
MAX_LENGTH_SHORT_MESSAGE,
),
)?,
tlvs,
})
}
pub async fn write(&self, stream: &mut WriteStream) -> io::Result<()> {
self.service_type.write(stream).await?;
self.source_addr_ton.write(stream).await?;
self.source_addr_npi.write(stream).await?;
self.source_addr.write(stream).await?;
self.dest_addr_ton.write(stream).await?;
self.dest_addr_npi.write(stream).await?;
self.destination_addr.write(stream).await?;
self.esm_class.write(stream).await?;
self.protocol_id.write(stream).await?;
self.priority_flag.write(stream).await?;
self.schedule_delivery_time.write(stream).await?;
self.validity_period.write(stream).await?;
self.registered_delivery.write(stream).await?;
self.replace_if_present_flag.write(stream).await?;
self.data_coding.write(stream).await?;
self.sm_default_msg_id.write(stream).await?;
assert!(self.short_message.len() < 255);
Integer1::new(self.short_message.len() as u8)
.write(stream)
.await?;
self.short_message.write(stream).await?;
self.tlvs.write(stream).await?;
Ok(())
}
pub fn parse(
bytes: &mut dyn io::BufRead,
_command_status: u32,
) -> Result<Self, PduParseError> {
let service_type = fld(
"service_type",
COctetString::read(bytes, MAX_LENGTH_SERVICE_TYPE),
)?;
let source_addr_ton = fld("source_addr_ton", Integer1::read(bytes))?;
let source_addr_npi = fld("source_addr_npi", Integer1::read(bytes))?;
let source_addr = fld(
"source_addr",
COctetString::read(bytes, MAX_LENGTH_SOURCE_ADDR),
)?;
let dest_addr_ton = fld("dest_addr_ton", Integer1::read(bytes))?;
let dest_addr_npi = fld("dest_addr_npi", Integer1::read(bytes))?;
let destination_addr = fld(
"destination_addr",
COctetString::read(bytes, MAX_LENGTH_DESTINATION_ADDR),
)?;
let esm_class = fld("esm_class", Integer1::read(bytes))?;
let protocol_id = fld("protocol_id", Integer1::read(bytes))?;
let priority_flag = fld("priority_flag", Integer1::read(bytes))?;
let schedule_delivery_time = fld(
"schedule_delivery_time",
COctetString::read(bytes, MAX_LENGTH_SCHEDULE_DELIVERY_TIME),
)?;
let validity_period = fld(
"validity_period",
COctetString::read(bytes, MAX_LENGTH_VALIDITY_PERIOD),
)?;
let registered_delivery =
fld("registered_delivery", Integer1::read(bytes))?;
let replace_if_present_flag =
fld("replace_if_present_flag", Integer1::read(bytes))?;
let data_coding = fld("data_coding", Integer1::read(bytes))?;
let sm_default_msg_id =
fld("sm_default_msg_id", Integer1::read(bytes))?;
let sm_length = fld("sm_length", Integer1::read(bytes))?;
let short_message = fld(
"short_message",
OctetString::read(
bytes,
sm_length.value as usize,
MAX_LENGTH_SHORT_MESSAGE,
),
)?;
let tlvs = Tlvs::read(bytes)?;
validate_length_1_or_17(
"schedule_delivery_time",
schedule_delivery_time.value.len(),
)?;
validate_length_1_or_17(
"validity_period",
validity_period.value.len(),
)?;
Ok(Self {
service_type,
source_addr_ton,
source_addr_npi,
source_addr,
dest_addr_ton,
dest_addr_npi,
destination_addr,
esm_class,
protocol_id,
priority_flag,
schedule_delivery_time,
validity_period,
registered_delivery,
replace_if_present_flag,
data_coding,
sm_default_msg_id,
short_message,
tlvs,
})
}
pub fn validate_command_status(
self,
command_status: u32,
) -> Result<Self, PduParseError> {
if command_status == 0x00000000 {
Ok(self)
} else {
Err(PduParseError::new(PduParseErrorBody::StatusIsNotZero))
}
}
}
fn validate_length_1_or_17(
field_name: &str,
length: usize,
) -> Result<(), PduParseError> {
if length == 0 || length == 16 {
Ok(())
} else {
Err(PduParseError::new(PduParseErrorBody::IncorrectLength(
length as u32,
String::from(
"Must be either 1 or 17 characters, including \
the NULL character.",
),
))
.into_with_field_name(field_name))
}
}