Skip to main content

smpp_codec/pdus/submission_pdus/
submit_multi_request.rs

1use crate::common::{
2    read_c_string, write_c_string, Npi, PduError, Ton, CMD_SUBMIT_MULTI_SM, HEADER_LEN,
3};
4use crate::tlv::Tlv;
5use std::io::Read;
6use std::io::{Cursor, Write};
7
8#[derive(Debug, Clone, PartialEq)]
9/// Destination for Submit Multi (SME Address or Distribution List)
10pub enum Destination {
11    /// Normal SME Address
12    SmeAddress {
13        /// Type of Number
14        ton: Ton,
15        /// Numbering Plan Indicator
16        npi: Npi,
17        /// Address
18        address: String,
19    },
20    /// Distribution List Name
21    DistributionList(String),
22}
23
24/// Represents a Submit Multi PDU.
25///
26/// Used to submit a short message to multiple recipients (SME addresses or Distribution Lists).
27#[derive(Debug, Clone, PartialEq)]
28pub struct SubmitMulti {
29    /// Sequence number of the PDU
30    pub sequence_number: u32,
31    /// Service Type
32    pub service_type: String,
33    /// Source Address Type of Number
34    pub source_addr_ton: Ton,
35    /// Source Address Numbering Plan Indicator
36    pub source_addr_npi: Npi,
37    /// Source Address
38    pub source_addr: String,
39    /// List of Destinations (Max 255)
40    pub destinations: Vec<Destination>,
41    /// ESM Class
42    pub esm_class: u8,
43    /// Protocol Identifier
44    pub protocol_id: u8,
45    /// Priority Level
46    pub priority_flag: u8,
47    /// Scheduled Delivery Time
48    pub schedule_delivery_time: String,
49    /// Validity Period
50    pub validity_period: String,
51    /// Registered Delivery
52    pub registered_delivery: u8,
53    /// Replace If Present Flag
54    pub replace_if_present_flag: u8,
55    /// Data Coding Scheme
56    pub data_coding: u8,
57    /// SMSC Default Message ID
58    pub sm_default_msg_id: u8,
59    /// Short Message Data
60    pub short_message: Vec<u8>,
61    /// Optional Parameters (TLVs)
62    pub optional_params: Vec<Tlv>,
63}
64
65impl SubmitMulti {
66    /// Create a new Submit Multi PDU.
67    ///
68    /// # Examples
69    ///
70    /// ```
71    /// use smpp_codec::pdus::{SubmitMulti, Destination};
72    /// use smpp_codec::common::{Ton, Npi};
73    ///
74    /// let dest1 = Destination::SmeAddress {
75    ///     ton: Ton::International,
76    ///     npi: Npi::Isdn,
77    ///     address: "1234567890".to_string(),
78    /// };
79    /// let dest2 = Destination::DistributionList("MyList".to_string());
80    ///
81    /// let pdu = SubmitMulti::new(
82    ///     1,
83    ///     "Source".to_string(),
84    ///     vec![dest1, dest2],
85    ///     b"Hello World".to_vec(),
86    /// );
87    /// ```
88    pub fn new(
89        sequence_number: u32,
90        source_addr: String,
91        destinations: Vec<Destination>,
92        short_message: Vec<u8>,
93    ) -> Self {
94        Self {
95            sequence_number,
96            service_type: String::new(),
97            source_addr_ton: Ton::Unknown,
98            source_addr_npi: Npi::Unknown,
99            source_addr,
100            destinations,
101            esm_class: 0,
102            protocol_id: 0,
103            priority_flag: 0,
104            schedule_delivery_time: String::new(),
105            validity_period: String::new(),
106            registered_delivery: 0, // Default: Don't request delivery receipt
107            replace_if_present_flag: 0,
108            data_coding: 0, // Default: SMSC Default
109            sm_default_msg_id: 0,
110            short_message,
111            optional_params: Vec::new(),
112        }
113    }
114
115    /// Encode the PDU into the writer.
116    ///
117    /// # Errors
118    ///
119    /// Returns a [`PduError`] if the write fails or fields are invalid.
120    pub fn encode(&self, writer: &mut impl Write) -> Result<(), PduError> {
121        // Calculate length of destinations
122        let mut dest_len = 0;
123        for dest in &self.destinations {
124            match dest {
125                Destination::SmeAddress { address, .. } => {
126                    // Flag(1) + Ton(1) + Npi(1) + Address(N+1)
127                    dest_len += 1 + 1 + 1 + address.len() + 1;
128                }
129                Destination::DistributionList(name) => {
130                    // Flag(1) + Name(N+1)
131                    dest_len += 1 + name.len() + 1;
132                }
133            }
134        }
135
136        // Calculate length of TLVs
137        let tlvs_len: usize = self
138            .optional_params
139            .iter()
140            .map(|tlv| 4 + tlv.length as usize)
141            .sum();
142
143        // Calculate Body Length
144        // ServiceType(N+1)
145        // SrcTon(1) + SrcNpi(1) + SrcAddr(N+1)
146        // DestCount(1) + DestLen
147        // Flags(3)
148        // Sched(N+1) + Validity(N+1)
149        // Reg(1) + Rep(1) + DC(1) + MsgId(1)
150        // SmLen(1) + SmData(N)
151        // TLVs
152        let body_len = self.service_type.len() + 1 +
153                       1 + 1 + self.source_addr.len() + 1 +
154                       1 + dest_len +
155                       1 + 1 + 1 + // esm_class, protocol_id, priority_flag
156                       self.schedule_delivery_time.len() + 1 +
157                       self.validity_period.len() + 1 +
158                       1 + 1 + 1 + 1 + // reg, rep, dc, id
159                       1 + self.short_message.len() +
160                       tlvs_len;
161
162        let command_len = (HEADER_LEN + body_len) as u32;
163
164        writer.write_all(&command_len.to_be_bytes())?;
165        writer.write_all(&CMD_SUBMIT_MULTI_SM.to_be_bytes())?;
166        writer.write_all(&0u32.to_be_bytes())?;
167        writer.write_all(&self.sequence_number.to_be_bytes())?;
168
169        write_c_string(writer, &self.service_type)?;
170        writer.write_all(&[self.source_addr_ton as u8, self.source_addr_npi as u8])?;
171        write_c_string(writer, &self.source_addr)?;
172
173        // Standard: "number_of_dests"
174        // We calculate it dynamically from the Vector length
175        if self.destinations.len() > 255 {
176            // Optional: Return error if > 255, as spec limits it.
177            // For now, casting to u8 is the simple approach.
178        }
179        writer.write_all(&[self.destinations.len() as u8])?;
180        for dest in &self.destinations {
181            match dest {
182                Destination::SmeAddress { ton, npi, address } => {
183                    writer.write_all(&[1])?; // Dest Flag 1 = SME Address
184                    writer.write_all(&[*ton as u8, *npi as u8])?;
185                    write_c_string(writer, address)?;
186                }
187                Destination::DistributionList(name) => {
188                    writer.write_all(&[2])?; // Dest Flag 2 = Distribution List
189                    write_c_string(writer, name)?;
190                }
191            }
192        }
193
194        writer.write_all(&[self.esm_class, self.protocol_id, self.priority_flag])?;
195        write_c_string(writer, &self.schedule_delivery_time)?;
196        write_c_string(writer, &self.validity_period)?;
197        writer.write_all(&[
198            self.registered_delivery,
199            self.replace_if_present_flag,
200            self.data_coding,
201            self.sm_default_msg_id,
202            self.short_message.len() as u8,
203        ])?;
204        writer.write_all(&self.short_message)?;
205
206        for tlv in &self.optional_params {
207            tlv.encode(writer)?;
208        }
209        Ok(())
210    }
211
212    /// Decode the PDU from the buffer.
213    ///
214    /// # Errors
215    ///
216    /// Returns a [`PduError`] if the buffer is too short or malformed.
217    pub fn decode(buffer: &[u8]) -> Result<Self, PduError> {
218        if buffer.len() < HEADER_LEN {
219            return Err(PduError::BufferTooShort);
220        }
221        let mut cursor = Cursor::new(buffer);
222        cursor.set_position(12); // Skip Header fields (Len, ID, Status)
223
224        let mut bytes = [0u8; 4];
225        cursor.read_exact(&mut bytes)?;
226        let sequence_number = u32::from_be_bytes(bytes);
227
228        let service_type = read_c_string(&mut cursor)?;
229
230        let mut u8_buf = [0u8; 1];
231        cursor.read_exact(&mut u8_buf)?;
232        let source_addr_ton = Ton::from(u8_buf[0]);
233        cursor.read_exact(&mut u8_buf)?;
234        let source_addr_npi = Npi::from(u8_buf[0]);
235        let source_addr = read_c_string(&mut cursor)?;
236
237        // Decode Destination List
238        cursor.read_exact(&mut u8_buf)?;
239        let dest_count = u8_buf[0];
240        let mut destinations = Vec::with_capacity(dest_count as usize);
241
242        for _ in 0..dest_count {
243            cursor.read_exact(&mut u8_buf)?;
244            let dest_flag = u8_buf[0];
245            if dest_flag == 1 {
246                // SME Address
247                cursor.read_exact(&mut u8_buf)?;
248                let ton = Ton::from(u8_buf[0]);
249                cursor.read_exact(&mut u8_buf)?;
250                let npi = Npi::from(u8_buf[0]);
251                let address = read_c_string(&mut cursor)?;
252                destinations.push(Destination::SmeAddress { ton, npi, address });
253            } else {
254                // Distribution List
255                let dl_name = read_c_string(&mut cursor)?;
256                destinations.push(Destination::DistributionList(dl_name));
257            }
258        }
259
260        cursor.read_exact(&mut u8_buf)?;
261        let esm_class = u8_buf[0];
262        cursor.read_exact(&mut u8_buf)?;
263        let protocol_id = u8_buf[0];
264        cursor.read_exact(&mut u8_buf)?;
265        let priority_flag = u8_buf[0];
266
267        let schedule_delivery_time = read_c_string(&mut cursor)?;
268        let validity_period = read_c_string(&mut cursor)?;
269
270        cursor.read_exact(&mut u8_buf)?;
271        let registered_delivery = u8_buf[0];
272        cursor.read_exact(&mut u8_buf)?;
273        let replace_if_present_flag = u8_buf[0];
274        cursor.read_exact(&mut u8_buf)?;
275        let data_coding = u8_buf[0];
276        cursor.read_exact(&mut u8_buf)?;
277        let sm_default_msg_id = u8_buf[0];
278
279        cursor.read_exact(&mut u8_buf)?;
280        let sm_length = u8_buf[0] as usize;
281        let mut short_message = vec![0u8; sm_length];
282        cursor.read_exact(&mut short_message)?;
283
284        let mut optional_params = Vec::new();
285        while let Some(tlv) = Tlv::decode(&mut cursor)? {
286            optional_params.push(tlv);
287        }
288
289        Ok(Self {
290            sequence_number,
291            service_type,
292            source_addr_ton,
293            source_addr_npi,
294            source_addr,
295            destinations,
296            esm_class,
297            protocol_id,
298            priority_flag,
299            schedule_delivery_time,
300            validity_period,
301            registered_delivery,
302            replace_if_present_flag,
303            data_coding,
304            sm_default_msg_id,
305            short_message,
306            optional_params,
307        })
308    }
309}