Skip to main content

smpp_codec/pdus/session_pdus/
bind_response.rs

1// bind_response.rs (Handles both Encoder and Decoder)
2use crate::common::{get_status_code, get_status_description, PduError, HEADER_LEN};
3use std::io::{Cursor, Read, Write};
4
5/// Represents a Bind Response PDU.
6///
7/// Sent by the SMSC in response to a Bind Request.
8#[derive(Debug, Clone)]
9pub struct BindResponse {
10    pub sequence_number: u32,
11    pub command_status: u32,        // 0 = OK, others = Error
12    pub status_description: String, // Human-readable description of status
13    pub command_id: u32,            // e.g., 0x80000009 (bind_transceiver_resp)
14    pub system_id: String,          // SMSC ID
15    pub optional_params: Vec<u8>,   // Rarely used in Bind Resp, but allowed
16}
17
18impl BindResponse {
19    /// Create a new Bind Response.
20    ///
21    /// # Examples
22    ///
23    /// ```
24    /// use smpp_codec::pdus::BindResponse;
25    /// use smpp_codec::common::{CMD_BIND_TRANSCEIVER_RESP, BindMode};
26    ///
27    /// let sequence_number: u32 = 1;
28    /// let resp = BindResponse::new(
29    ///     sequence_number,
30    ///     CMD_BIND_TRANSCEIVER_RESP,
31    ///     "ESME_ROK",
32    ///     "system_id".to_string()
33    /// );
34    /// ```
35    pub fn new(
36        sequence_number: u32,
37        command_id: u32,
38        status_name: &str,
39        system_id: String,
40    ) -> Self {
41        let command_status = get_status_code(status_name);
42        Self {
43            sequence_number,
44            command_id,
45            command_status,
46            status_description: status_name.to_string(),
47            system_id,
48            optional_params: Vec::new(),
49        }
50    }
51
52    /// Encode the struct into raw bytes for the network.
53    ///
54    /// # Errors
55    ///
56    /// Returns a [`PduError`] if:
57    /// * An I/O error occurs while writing.
58    ///
59    /// # Examples
60    ///
61    /// ```
62    /// # use smpp_codec::pdus::BindResponse;
63    /// # use smpp_codec::common::CMD_BIND_TRANSMITTER_RESP;
64    /// # let sequence_number: u32 = 1;
65    /// # let bind_resp = BindResponse::new(sequence_number, CMD_BIND_TRANSMITTER_RESP, "ESME_ROK", "id".into());
66    /// let mut buffer = Vec::new();
67    /// bind_resp.encode(&mut buffer).expect("Encoding failed");
68    /// ```
69    pub fn encode(&self, writer: &mut impl Write) -> Result<(), PduError> {
70        let mut body = Vec::new();
71
72        // If status is OK, we must include system_id.
73        // On error, body can be empty (or just header).
74        if self.command_status == 0 {
75            body.write_all(self.system_id.as_bytes())?;
76            body.write_all(&[0])?; // Null terminator
77
78            // Add optional params if any
79            body.write_all(&self.optional_params)?;
80        }
81
82        let command_len = (HEADER_LEN + body.len()) as u32;
83
84        // Write Header
85        writer.write_all(&command_len.to_be_bytes())?;
86        writer.write_all(&self.command_id.to_be_bytes())?;
87        writer.write_all(&self.command_status.to_be_bytes())?;
88        writer.write_all(&self.sequence_number.to_be_bytes())?;
89
90        // Write Body
91        writer.write_all(&body)?;
92
93        Ok(())
94    }
95
96    /// Decode raw bytes from the network into the struct.
97    ///
98    /// # Errors
99    ///
100    /// Returns a [`PduError`] if:
101    /// * The buffer is too short.
102    /// * The buffer data is malformed.
103    ///
104    /// # Examples
105    ///
106    /// ```
107    /// # use smpp_codec::pdus::BindResponse;
108    /// # use smpp_codec::common::CMD_BIND_TRANSCEIVER_RESP;
109    /// # let sequence_number: u32 = 1;
110    /// # let resp = BindResponse::new(sequence_number, CMD_BIND_TRANSCEIVER_RESP, "ESME_ROK", "id".into());
111    /// # let mut buffer = Vec::new();
112    /// # resp.encode(&mut buffer).unwrap();
113    /// let decoded = BindResponse::decode(&buffer).expect("Decoding failed");
114    /// assert_eq!(decoded.sequence_number, 1);
115    /// ```
116    pub fn decode(buffer: &[u8]) -> Result<Self, PduError> {
117        if buffer.len() < HEADER_LEN {
118            return Err(PduError::BufferTooShort);
119        }
120
121        let mut cursor = Cursor::new(buffer);
122
123        // 1. Read Header
124        let mut bytes = [0u8; 4];
125
126        cursor.read_exact(&mut bytes)?;
127        let command_len = u32::from_be_bytes(bytes) as usize;
128
129        cursor.read_exact(&mut bytes)?;
130        let command_id = u32::from_be_bytes(bytes);
131
132        cursor.read_exact(&mut bytes)?;
133        let command_status = u32::from_be_bytes(bytes);
134
135        cursor.read_exact(&mut bytes)?;
136        let sequence_number = u32::from_be_bytes(bytes);
137
138        // 2. Read Body
139        let system_id: String;
140        let mut optional_params = Vec::new();
141
142        // Only try to read body if length > header
143        if command_len > HEADER_LEN {
144            // The cursor is currently at pos 16 (end of header).
145            // Read system_id (C-String)
146            if command_status == 0 {
147                system_id = crate::common::read_c_string(&mut cursor)?;
148            } else {
149                system_id = String::new();
150            }
151        } else {
152            system_id = String::new();
153        }
154
155        // Optional Params
156        // optional_params reused from above
157        let current_pos = cursor.position() as usize;
158        if current_pos < command_len {
159            let remaining_len = command_len - current_pos;
160            let mut temp_buf = vec![0u8; remaining_len];
161            cursor.read_exact(&mut temp_buf)?;
162            optional_params = temp_buf;
163        }
164
165        let status_description = get_status_description(command_status);
166
167        Ok(Self {
168            sequence_number,
169            command_status,
170            status_description,
171            command_id,
172            system_id,
173            optional_params,
174        })
175    }
176}