Skip to main content

smpp_codec/pdus/submission_pdus/
submit_multi_response.rs

1use crate::common::{
2    get_status_code, get_status_description, read_c_string, write_c_string, Npi, PduError, Ton,
3    CMD_SUBMIT_MULTI_SM_RESP, HEADER_LEN,
4};
5
6use std::io::{Cursor, Read, Write};
7
8/// Represents an unsuccessful SME in a Submit Multi Response.
9#[derive(Debug, Clone, PartialEq)]
10pub struct UnsuccessfulDelivery {
11    /// Type of Number
12    pub ton: Ton,
13    /// Numbering Plan Indicator
14    pub npi: Npi,
15    /// Address that failed
16    pub address: String,
17    /// Error status code (ESME_Rx...)
18    pub error_status: u32,
19}
20
21/// Represents a Submit Multi Response PDU.
22///
23/// Sent by the SMSC in response to a Submit Multi Request.
24#[derive(Debug, Clone, PartialEq)]
25pub struct SubmitMultiResp {
26    /// Sequence number of the PDU
27    pub sequence_number: u32,
28    /// Command Status (0 = OK, others = Error)
29    pub command_status: u32, // 0 = OK, others = Error
30    /// Human-readable description of status
31    pub status_description: String, // Human-readable description of status
32    /// Message ID allocated by the SMSC
33    pub message_id: String,
34    /// List of unsuccessful deliveries (if any)
35    pub unsuccess_smes: Vec<UnsuccessfulDelivery>,
36}
37
38impl SubmitMultiResp {
39    /// Create a new Submit Multi Response.
40    ///
41    /// # Examples
42    ///
43    /// ```
44    /// use smpp_codec::pdus::{SubmitMultiResp, UnsuccessfulDelivery};
45    ///
46    /// let resp = SubmitMultiResp::new(
47    ///     1,
48    ///     "ESME_ROK",
49    ///     "MessageID".to_string(),
50    ///     vec![],
51    /// );
52    /// ```
53    pub fn new(
54        sequence_number: u32,
55        status_name: &str,
56        message_id: String,
57        unsuccess_smes: Vec<UnsuccessfulDelivery>,
58    ) -> Self {
59        let command_status = get_status_code(status_name);
60        Self {
61            sequence_number,
62            command_status,
63            status_description: status_name.to_string(),
64            message_id,
65            unsuccess_smes,
66        }
67    }
68
69    /// Encode the PDU into the writer.
70    ///
71    /// # Errors
72    ///
73    /// Returns a [`PduError`] if the write fails.
74    pub fn encode(&self, writer: &mut impl Write) -> Result<(), PduError> {
75        let mut body = Vec::new();
76
77        // Body is only present if we have a Message ID to return
78        // (Usually on success 0x00 or partial success 0x405)
79        if self.command_status == 0 || self.command_status == 0x00000405 {
80            write_c_string(&mut body, &self.message_id)?;
81            body.write_all(&[self.unsuccess_smes.len() as u8])?;
82
83            for sme in &self.unsuccess_smes {
84                body.write_all(&[sme.ton as u8, sme.npi as u8])?;
85                write_c_string(&mut body, &sme.address)?;
86                body.write_all(&sme.error_status.to_be_bytes())?;
87            }
88        }
89
90        let command_len = (HEADER_LEN + body.len()) as u32;
91        writer.write_all(&command_len.to_be_bytes())?;
92        writer.write_all(&CMD_SUBMIT_MULTI_SM_RESP.to_be_bytes())?;
93        writer.write_all(&self.command_status.to_be_bytes())?;
94        writer.write_all(&self.sequence_number.to_be_bytes())?;
95        writer.write_all(&body)?;
96
97        Ok(())
98    }
99
100    /// Decode the PDU from the buffer.
101    ///
102    /// # Errors
103    ///
104    /// Returns a [`PduError`] if the buffer is too short or malformed.
105    pub fn decode(buffer: &[u8]) -> Result<Self, PduError> {
106        if buffer.len() < HEADER_LEN {
107            return Err(PduError::BufferTooShort);
108        }
109        let mut cursor = Cursor::new(buffer);
110        cursor.set_position(8);
111
112        let mut bytes = [0u8; 4];
113        cursor.read_exact(&mut bytes)?;
114        let command_status = u32::from_be_bytes(bytes);
115        cursor.read_exact(&mut bytes)?;
116        let sequence_number = u32::from_be_bytes(bytes);
117
118        let mut message_id = String::new();
119        let mut unsuccess_smes = Vec::new();
120
121        if command_status == 0 || command_status == 0x00000405 {
122            // Partial success
123            message_id = read_c_string(&mut cursor)?;
124            let mut u8_buf = [0u8; 1];
125            cursor.read_exact(&mut u8_buf)?;
126            let count = u8_buf[0];
127
128            for _ in 0..count {
129                cursor.read_exact(&mut u8_buf)?;
130                let ton = Ton::from(u8_buf[0]);
131                cursor.read_exact(&mut u8_buf)?;
132                let npi = Npi::from(u8_buf[0]);
133                let address = read_c_string(&mut cursor)?;
134
135                let mut err_bytes = [0u8; 4];
136                cursor.read_exact(&mut err_bytes)?;
137                let error_status = u32::from_be_bytes(err_bytes);
138
139                unsuccess_smes.push(UnsuccessfulDelivery {
140                    ton,
141                    npi,
142                    address,
143                    error_status,
144                });
145            }
146        }
147
148        let status_description = get_status_description(command_status);
149
150        Ok(Self {
151            sequence_number,
152            command_status,
153            status_description,
154            message_id,
155            unsuccess_smes,
156        })
157    }
158}