Skip to main content

smpp_codec/pdus/ancillary_pdus/
query_sm.rs

1use crate::common::{
2    get_status_code, read_c_string, write_c_string, Npi, PduError, Ton, CMD_QUERY_SM, HEADER_LEN,
3};
4use std::io::{Cursor, Read, Write};
5
6// --- Request ---
7/// Represents a Query SM Request PDU.
8///
9/// Used to query the status of a previously submitted message.
10#[derive(Debug, Clone, PartialEq)]
11pub struct QuerySmRequest {
12    /// Sequence number of the PDU
13    pub sequence_number: u32,
14    /// Message ID of the message to query
15    pub message_id: String,
16    /// Source Address Type of Number
17    pub source_addr_ton: Ton,
18    /// Source Address Numbering Plan Indicator
19    pub source_addr_npi: Npi,
20    /// Source Address
21    pub source_addr: String,
22}
23
24impl QuerySmRequest {
25    /// Create a new Query SM Request.
26    pub fn new(sequence_number: u32, message_id: String, source_addr: String) -> Self {
27        Self {
28            sequence_number,
29            message_id,
30            source_addr_ton: Ton::Unknown,
31            source_addr_npi: Npi::Unknown,
32            source_addr,
33        }
34    }
35
36    /// Encode the PDU into the writer.
37    ///
38    /// # Errors
39    ///
40    /// Returns a [`PduError`] if the write fails or strings are too long.
41    pub fn encode(&self, writer: &mut impl Write) -> Result<(), PduError> {
42        // 1. Validation
43        if self.message_id.len() > 64 {
44            return Err(PduError::StringTooLong("message_id".into(), 64));
45        }
46        if self.source_addr.len() > 21 {
47            return Err(PduError::StringTooLong("source_addr".into(), 21));
48        }
49
50        // 2. Calculate Length Upfront
51        let body_len = self.message_id.len() + 1 +
52                       1 + 1 + // source_addr_ton, source_addr_npi
53                       self.source_addr.len() + 1;
54
55        // 3. Write Header
56        let command_len = (HEADER_LEN + body_len) as u32;
57        writer.write_all(&command_len.to_be_bytes())?;
58        writer.write_all(&CMD_QUERY_SM.to_be_bytes())?;
59        writer.write_all(&0u32.to_be_bytes())?;
60        writer.write_all(&self.sequence_number.to_be_bytes())?;
61
62        // 4. Write Body
63        write_c_string(writer, &self.message_id)?;
64        writer.write_all(&[self.source_addr_ton as u8, self.source_addr_npi as u8])?;
65        write_c_string(writer, &self.source_addr)?;
66
67        Ok(())
68    }
69
70    /// Decode the PDU from the buffer.
71    ///
72    /// # Errors
73    ///
74    /// Returns a [`PduError`] if the buffer is too short or malformed.
75    pub fn decode(buffer: &[u8]) -> Result<Self, PduError> {
76        if buffer.len() < HEADER_LEN {
77            return Err(PduError::BufferTooShort);
78        }
79
80        let mut cursor = Cursor::new(buffer);
81        cursor.set_position(12);
82
83        let mut bytes = [0u8; 4];
84        cursor.read_exact(&mut bytes)?;
85        let sequence_number = u32::from_be_bytes(bytes);
86
87        let message_id = read_c_string(&mut cursor)?;
88
89        let mut u8_buf = [0u8; 1];
90        cursor.read_exact(&mut u8_buf)?;
91        let source_addr_ton = Ton::from(u8_buf[0]);
92        cursor.read_exact(&mut u8_buf)?;
93        let source_addr_npi = Npi::from(u8_buf[0]);
94
95        let source_addr = read_c_string(&mut cursor)?;
96
97        Ok(Self {
98            sequence_number,
99            message_id,
100            source_addr_ton,
101            source_addr_npi,
102            source_addr,
103        })
104    }
105}
106
107/// Represents a Query SM Response PDU.
108#[derive(Debug, Clone, PartialEq)]
109pub struct QuerySmResponse {
110    /// Sequence number of the PDU
111    pub sequence_number: u32,
112    /// Command Status
113    pub command_status: u32,
114    /// Message ID
115    pub message_id: String,
116    /// Final Date (Format: YYMMDDhhmmsstn00)
117    pub final_date: String,
118    /// Message State (See [`MessageState`])
119    pub message_state: u8,
120    /// Error Code (Network specific error code)
121    pub error_code: u8,
122    /// Status Description
123    pub status_description: String,
124}
125
126/// Message States (as per SMPP v3.4 Spec section 5.2.28)
127#[derive(Debug, Clone, Copy, PartialEq)]
128#[repr(u8)]
129pub enum MessageState {
130    /// Message is in enroute state
131    Enroute = 1,
132    /// Message is delivered
133    Delivered = 2,
134    /// Message validity period has expired
135    Expired = 3,
136    /// Message has been deleted
137    Deleted = 4,
138    /// Message is undeliverable
139    Undeliverable = 5,
140    /// Message is in accepted state
141    Accepted = 6,
142    /// Message is in invalid state
143    Unknown = 7,
144    /// Message is in rejected state
145    Rejected = 8,
146}
147
148impl QuerySmResponse {
149    /// Create a new Query SM Response.
150    pub fn new(
151        sequence_number: u32,
152        status_name: &str,
153        message_id: String,
154        final_date: String,
155        message_state: u8,
156        error_code: u8,
157    ) -> Self {
158        let command_status = get_status_code(status_name);
159        Self {
160            sequence_number,
161            command_status,
162            message_id,
163            final_date,
164            message_state,
165            error_code,
166            status_description: status_name.to_string(),
167        }
168    }
169
170    /// Encode the PDU into the writer.
171    ///
172    /// # Errors
173    ///
174    /// Returns a [`PduError`] if the write fails.
175    pub fn encode(&self, writer: &mut impl Write) -> Result<(), PduError> {
176        let body_len = if self.command_status == 0 {
177            self.message_id.len() + 1 + self.final_date.len() + 1 + 1 + 1 // message_state, error_code
178        } else {
179            0
180        };
181
182        let command_len = (HEADER_LEN + body_len) as u32;
183        writer.write_all(&command_len.to_be_bytes())?;
184        writer.write_all(&crate::common::CMD_QUERY_SM_RESP.to_be_bytes())?;
185        writer.write_all(&self.command_status.to_be_bytes())?;
186        writer.write_all(&self.sequence_number.to_be_bytes())?;
187
188        if self.command_status == 0 {
189            write_c_string(writer, &self.message_id)?;
190            write_c_string(writer, &self.final_date)?;
191            writer.write_all(&[self.message_state, self.error_code])?;
192        }
193
194        Ok(())
195    }
196
197    /// Decode the PDU from the buffer.
198    ///
199    /// # Errors
200    ///
201    /// Returns a [`PduError`] if the buffer is too short or malformed.
202    pub fn decode(buffer: &[u8]) -> Result<Self, PduError> {
203        if buffer.len() < HEADER_LEN {
204            return Err(PduError::BufferTooShort);
205        }
206        let mut cursor = Cursor::new(buffer);
207        cursor.set_position(8);
208
209        let mut bytes = [0u8; 4];
210        cursor.read_exact(&mut bytes)?;
211        let command_status = u32::from_be_bytes(bytes);
212        cursor.read_exact(&mut bytes)?;
213        let sequence_number = u32::from_be_bytes(bytes);
214
215        let message_id: String;
216        let final_date: String;
217        let message_state: u8;
218        let error_code: u8;
219
220        if command_status == 0 && buffer.len() > HEADER_LEN {
221            message_id = read_c_string(&mut cursor)?;
222            final_date = read_c_string(&mut cursor)?;
223
224            let mut u8_buf = [0u8; 1];
225            cursor.read_exact(&mut u8_buf)?;
226            message_state = u8_buf[0];
227            cursor.read_exact(&mut u8_buf)?;
228            error_code = u8_buf[0];
229        } else {
230            message_id = String::new();
231            final_date = String::new();
232            message_state = 0;
233            error_code = 0;
234        }
235
236        let status_description = crate::common::get_status_description(command_status);
237
238        Ok(Self {
239            sequence_number,
240            command_status,
241            message_id,
242            final_date,
243            message_state,
244            error_code,
245            status_description,
246        })
247    }
248}