Skip to main content

smpp_codec/pdus/session_pdus/
outbind.rs

1use crate::common::{read_c_string, write_c_string, PduError, CMD_OUTBIND, HEADER_LEN};
2use std::io::{Cursor, Read, Write};
3
4/// Represents an Outbind PDU.
5///
6/// Sent by the SMSC to the ESME to request the ESME to initiate a Bind.
7#[derive(Debug, Clone, PartialEq)]
8pub struct OutbindRequest {
9    /// Sequence number of the PDU
10    pub sequence_number: u32,
11    /// System ID identifying the ESME
12    pub system_id: String,
13    /// Password for authentication
14    pub password: String,
15}
16
17impl OutbindRequest {
18    /// Create a new Outbind Request.
19    ///
20    /// # Examples
21    ///
22    /// ```
23    /// use smpp_codec::pdus::OutbindRequest;
24    ///
25    /// let sequence_number: u32 = 1;
26    /// let outbind = OutbindRequest::new(
27    ///     sequence_number, // Sequence number
28    ///     "my_system_id".to_string(),
29    ///     "password".to_string(),
30    /// );
31    /// ```
32    pub fn new(sequence_number: u32, system_id: String, password: String) -> Self {
33        Self {
34            sequence_number,
35            system_id,
36            password,
37        }
38    }
39
40    /// Encode the struct into raw bytes for the network.
41    ///
42    /// # Errors
43    ///
44    /// Returns a [`PduError`] if:
45    /// * `system_id` exceeds 16 characters.
46    /// * `password` exceeds 9 characters.
47    /// * An I/O error occurs while writing.
48    ///
49    /// # Examples
50    ///
51    /// ```
52    /// # use smpp_codec::pdus::OutbindRequest;
53    /// # let sequence_number: u32 = 1;
54    /// # let outbind = OutbindRequest::new(sequence_number, "id".into(), "pwd".into());
55    /// let mut buffer = Vec::new();
56    /// outbind.encode(&mut buffer).expect("Encoding failed");
57    /// ```
58    pub fn encode(&self, writer: &mut impl Write) -> Result<(), PduError> {
59        // Validate
60        if self.system_id.len() > 16 {
61            return Err(PduError::StringTooLong("system_id".into(), 16));
62        }
63        if self.password.len() > 9 {
64            return Err(PduError::StringTooLong("password".into(), 9));
65        }
66
67        // Calculate body length
68        let body_len = self.system_id.len() + 1 + self.password.len() + 1;
69        let command_len = (HEADER_LEN + body_len) as u32;
70
71        // Header
72        writer.write_all(&command_len.to_be_bytes())?;
73        writer.write_all(&CMD_OUTBIND.to_be_bytes())?;
74        writer.write_all(&0u32.to_be_bytes())?; // Status is always 0
75        writer.write_all(&self.sequence_number.to_be_bytes())?;
76
77        // Body
78        write_c_string(writer, &self.system_id)?;
79        write_c_string(writer, &self.password)?;
80
81        Ok(())
82    }
83
84    /// Decode raw bytes from the network into the struct.
85    ///
86    /// # Errors
87    ///
88    /// Returns a [`PduError`] if:
89    /// * The buffer is too short.
90    /// * The buffer data is malformed.
91    ///
92    /// # Examples
93    ///
94    /// ```
95    /// # use smpp_codec::pdus::OutbindRequest;
96    /// # let sequence_number: u32 = 1;
97    /// # let outbind = OutbindRequest::new(sequence_number, "id".into(), "pwd".into());
98    /// # let mut buffer = Vec::new();
99    /// # outbind.encode(&mut buffer).unwrap();
100    /// let decoded = OutbindRequest::decode(&buffer).expect("Decoding failed");
101    /// assert_eq!(decoded.system_id, "id");
102    /// ```
103    pub fn decode(buffer: &[u8]) -> Result<Self, PduError> {
104        if buffer.len() < HEADER_LEN {
105            return Err(PduError::BufferTooShort);
106        }
107
108        let mut cursor = Cursor::new(buffer);
109
110        // Skip Header (assuming caller checked ID, or we just consume it)
111        cursor.set_position(12); // Skip len, id, status
112        let mut bytes = [0u8; 4];
113        cursor.read_exact(&mut bytes)?;
114        let sequence_number = u32::from_be_bytes(bytes);
115
116        // Body
117        let system_id = read_c_string(&mut cursor)?;
118        let password = read_c_string(&mut cursor)?;
119
120        Ok(Self {
121            sequence_number,
122            system_id,
123            password,
124        })
125    }
126}