Skip to main content

smpp_codec/pdus/session_pdus/
alert_notification.rs

1use crate::common::{
2    read_c_string, write_c_string, Npi, PduError, Ton, CMD_ALERT_NOTIFICATION, HEADER_LEN,
3};
4use crate::tlv::Tlv;
5use std::io::{Cursor, Read, Write};
6
7/// Represents an Alert Notification PDU.
8///
9/// Sent by the SMSC to the ESME to provide information about a message state (e.g., delivered).
10#[derive(Debug, Clone, PartialEq)]
11pub struct AlertNotification {
12    /// Sequence number of the PDU
13    pub sequence_number: u32,
14    /// Source Address Type of Number
15    pub source_addr_ton: Ton,
16    /// Source Address Numbering Plan Indicator
17    pub source_addr_npi: Npi,
18    /// Source Address (Sender)
19    pub source_addr: String, // Max 65
20    /// ESME Address Type of Number
21    pub esme_addr_ton: Ton,
22    /// ESME Address Numbering Plan Indicator
23    pub esme_addr_npi: Npi,
24    /// ESME Address (Receiver)
25    pub esme_addr: String, // Max 65
26    /// Optional TLVs
27    pub optional_params: Vec<Tlv>,
28}
29
30impl AlertNotification {
31    /// Create a new Alert Notification.
32    ///
33    /// # Examples
34    ///
35    /// ```
36    /// use smpp_codec::pdus::AlertNotification;
37    ///
38    /// let sequence_number: u32 = 1;
39    /// let alert = AlertNotification::new(
40    ///     sequence_number,
41    ///     "source_addr".to_string(),
42    ///     "esme_addr".to_string()
43    /// );
44    /// ```
45    pub fn new(sequence_number: u32, source_addr: String, esme_addr: String) -> Self {
46        Self {
47            sequence_number,
48            source_addr_ton: Ton::Unknown,
49            source_addr_npi: Npi::Unknown,
50            source_addr,
51            esme_addr_ton: Ton::Unknown,
52            esme_addr_npi: Npi::Unknown,
53            esme_addr,
54            optional_params: Vec::new(),
55        }
56    }
57
58    // Builder for TON/NPI
59    /// Builder for setting Source Address TON/NPI/Address.
60    pub fn with_source_addr(mut self, ton: Ton, npi: Npi, addr: String) -> Self {
61        self.source_addr_ton = ton;
62        self.source_addr_npi = npi;
63        self.source_addr = addr;
64        self
65    }
66
67    /// Builder for setting ESME Address TON/NPI/Address.
68    pub fn with_esme_addr(mut self, ton: Ton, npi: Npi, addr: String) -> Self {
69        self.esme_addr_ton = ton;
70        self.esme_addr_npi = npi;
71        self.esme_addr = addr;
72        self
73    }
74
75    /// Add a generic TLV
76    pub fn add_tlv(&mut self, tlv: Tlv) {
77        self.optional_params.push(tlv);
78    }
79
80    /// Encode the struct into raw bytes for the network.
81    ///
82    /// # Errors
83    ///
84    /// Returns a [`PduError`] if:
85    /// * `source_addr` exceeds 65 characters.
86    /// * `esme_addr` exceeds 65 characters.
87    /// * An I/O error occurs while writing.
88    ///
89    /// # Examples
90    ///
91    /// ```
92    /// # use smpp_codec::pdus::AlertNotification;
93    /// # let sequence_number: u32 = 1;
94    /// # let alert = AlertNotification::new(sequence_number, "src".into(), "dst".into());
95    /// let mut buffer = Vec::new();
96    /// alert.encode(&mut buffer).expect("Encoding failed");
97    /// ```
98    pub fn encode(&self, writer: &mut impl Write) -> Result<(), PduError> {
99        // Validate lengths
100        if self.source_addr.len() > 65 {
101            return Err(PduError::StringTooLong("source_addr".into(), 65));
102        }
103        if self.esme_addr.len() > 65 {
104            return Err(PduError::StringTooLong("esme_addr".into(), 65));
105        }
106
107        // Calculate body length
108        let mut body_len = 1 + 1 + self.source_addr.len() + 1 + // Source Address
109                           1 + 1 + self.esme_addr.len() + 1; // ESME Address
110
111        // Calculate TLV length
112        for tlv in &self.optional_params {
113            body_len += 4 + tlv.value.len();
114        }
115
116        // Header
117        let command_len = (HEADER_LEN + body_len) as u32;
118        writer.write_all(&command_len.to_be_bytes())?;
119        writer.write_all(&CMD_ALERT_NOTIFICATION.to_be_bytes())?;
120        writer.write_all(&0u32.to_be_bytes())?;
121        writer.write_all(&self.sequence_number.to_be_bytes())?;
122
123        // Body - Source Address
124        writer.write_all(&[self.source_addr_ton as u8, self.source_addr_npi as u8])?;
125        write_c_string(writer, &self.source_addr)?;
126
127        // Body - ESME Address
128        writer.write_all(&[self.esme_addr_ton as u8, self.esme_addr_npi as u8])?;
129        write_c_string(writer, &self.esme_addr)?;
130
131        // Optional Params
132        for tlv in &self.optional_params {
133            tlv.encode(writer)?;
134        }
135
136        Ok(())
137    }
138
139    /// Decode raw bytes from the network into the struct.
140    ///
141    /// # Errors
142    ///
143    /// Returns a [`PduError`] if:
144    /// * The buffer is too short.
145    /// * The buffer data is malformed.
146    ///
147    /// # Examples
148    ///
149    /// ```
150    /// # use smpp_codec::pdus::AlertNotification;
151    /// # let sequence_number: u32 = 1;
152    /// # let alert = AlertNotification::new(sequence_number, "src".into(), "dst".into());
153    /// # let mut buffer = Vec::new();
154    /// # alert.encode(&mut buffer).unwrap();
155    /// let decoded = AlertNotification::decode(&buffer).expect("Decoding failed");
156    /// assert_eq!(decoded.source_addr, "src");
157    /// ```
158    pub fn decode(buffer: &[u8]) -> Result<Self, PduError> {
159        if buffer.len() < HEADER_LEN {
160            return Err(PduError::BufferTooShort);
161        }
162
163        let mut cursor = Cursor::new(buffer);
164        // Skip Header
165        cursor.set_position(12);
166        let mut bytes = [0u8; 4];
167        cursor.read_exact(&mut bytes)?;
168        let sequence_number = u32::from_be_bytes(bytes);
169
170        // 3. Read Body
171        let mut u8_buf = [0u8; 1];
172
173        cursor.read_exact(&mut u8_buf)?;
174        let source_addr_ton = Ton::from(u8_buf[0]);
175        cursor.read_exact(&mut u8_buf)?;
176        let source_addr_npi = Npi::from(u8_buf[0]);
177        let source_addr = read_c_string(&mut cursor)?;
178
179        cursor.read_exact(&mut u8_buf)?;
180        let esme_addr_ton = Ton::from(u8_buf[0]);
181        cursor.read_exact(&mut u8_buf)?;
182        let esme_addr_npi = Npi::from(u8_buf[0]);
183        let esme_addr = read_c_string(&mut cursor)?;
184
185        // Optional Params (TLVs)
186        let mut optional_params = Vec::new();
187        while let Some(tlv) = Tlv::decode(&mut cursor)? {
188            optional_params.push(tlv);
189        }
190
191        Ok(Self {
192            sequence_number,
193            source_addr_ton,
194            source_addr_npi,
195            source_addr,
196            esme_addr_ton,
197            esme_addr_npi,
198            esme_addr,
199            optional_params,
200        })
201    }
202}