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}