1use std::borrow::Cow;
2
3use bytes::{BufMut, BytesMut};
4use itertools::Itertools;
5
6use crate::decoding::Parsable;
7use crate::encoding::Writable;
8use crate::{error::STAGE_DECODING, NotEnoughData};
9use crate::{InvalidData, ProtocolError};
10use miltr_utils::ByteParsing;
11
12#[derive(Debug, Clone)]
14pub struct Discard;
15
16impl Discard {
17 const CODE: u8 = b'd';
18}
19
20impl Parsable for Discard {
21 const CODE: u8 = Self::CODE;
22
23 fn parse(_buffer: BytesMut) -> Result<Self, ProtocolError> {
24 Ok(Self)
25 }
26}
27
28impl Writable for Discard {
29 fn write(&self, _buffer: &mut BytesMut) {}
30
31 fn len(&self) -> usize {
32 0
33 }
34
35 fn code(&self) -> u8 {
36 Self::CODE
37 }
38
39 fn is_empty(&self) -> bool {
40 self.len() == 0
41 }
42}
43
44#[derive(Debug, Clone)]
46pub struct Reject;
47
48impl Reject {
49 const CODE: u8 = b'r';
50}
51
52impl Parsable for Reject {
53 const CODE: u8 = Self::CODE;
54
55 fn parse(_buffer: BytesMut) -> Result<Self, ProtocolError> {
56 Ok(Self)
57 }
58}
59
60impl Writable for Reject {
61 fn write(&self, _buffer: &mut BytesMut) {}
62
63 fn len(&self) -> usize {
64 0
65 }
66
67 fn code(&self) -> u8 {
68 Self::CODE
69 }
70
71 fn is_empty(&self) -> bool {
72 self.len() == 0
73 }
74}
75
76#[derive(Debug, Clone)]
78pub struct Tempfail;
79
80impl Tempfail {
81 const CODE: u8 = b't';
82}
83
84impl Parsable for Tempfail {
85 const CODE: u8 = Self::CODE;
86
87 fn parse(_buffer: BytesMut) -> Result<Self, ProtocolError> {
88 Ok(Self)
89 }
90}
91
92impl Writable for Tempfail {
93 fn write(&self, _buffer: &mut BytesMut) {}
94
95 fn len(&self) -> usize {
96 0
97 }
98
99 fn code(&self) -> u8 {
100 Self::CODE
101 }
102 fn is_empty(&self) -> bool {
103 self.len() == 0
104 }
105}
106
107#[derive(Debug, Clone)]
109pub struct Skip;
110
111impl Skip {
112 const CODE: u8 = b's';
113}
114
115impl Parsable for Skip {
116 const CODE: u8 = Self::CODE;
117
118 fn parse(_buffer: BytesMut) -> Result<Self, ProtocolError> {
119 Ok(Self)
120 }
121}
122
123impl Writable for Skip {
124 fn write(&self, _buffer: &mut BytesMut) {}
125
126 fn len(&self) -> usize {
127 0
128 }
129
130 fn code(&self) -> u8 {
131 Self::CODE
132 }
133
134 fn is_empty(&self) -> bool {
135 self.len() == 0
136 }
137}
138
139const REPLY_CODE_LENGTH: usize = 3;
140#[derive(Debug, Clone)]
142pub struct Replycode {
143 rcode: RCode,
144 xcode: Option<XCode>,
145 message: BytesMut,
146}
147
148impl Replycode {
149 const CODE: u8 = b'y';
150
151 #[must_use]
153 #[allow(clippy::similar_names)]
154 pub fn new<R: Into<RCode>, X: Into<XCode>>(rcode: R, xcode: X, message: &str) -> Self {
155 let rcode = rcode.into();
156 let xcode = Some(xcode.into());
157
158 Self {
159 rcode,
160 xcode,
161 message: BytesMut::from(message.as_bytes()),
162 }
163 }
164
165 #[allow(clippy::similar_names)]
167 pub fn without_xcode<R: Into<RCode>>(rcode: R, message: &str) -> Self {
168 let rcode = rcode.into();
169
170 Self {
171 rcode,
172 xcode: None,
173 message: BytesMut::from(message.as_bytes()),
174 }
175 }
176
177 #[must_use]
179 pub fn message(&self) -> Cow<str> {
180 String::from_utf8_lossy(&self.message)
181 }
182
183 #[must_use]
185 pub fn rcode(&self) -> &RCode {
186 &self.rcode
187 }
188
189 #[must_use]
191 pub fn xcode(&self) -> &Option<XCode> {
192 &self.xcode
193 }
194}
195
196impl Parsable for Replycode {
197 const CODE: u8 = Self::CODE;
198
199 #[allow(clippy::similar_names)]
201 fn parse(mut buffer: BytesMut) -> Result<Self, ProtocolError> {
202 #[allow(clippy::similar_names)]
203 let Some(rcode) = buffer.delimited(b' ') else {
204 return Err(NotEnoughData::new(
205 STAGE_DECODING,
206 "Replycode",
207 "Missing nullbyte delimiter after rcode",
208 1,
209 0,
210 buffer,
211 )
212 .into());
213 };
214 let rcode = RCode::parse(rcode)?;
215
216 let Some(raw_message) = buffer.delimited(0) else {
217 return Err(NotEnoughData::new(
218 STAGE_DECODING,
219 "Replycode",
220 "Missing nullbyte delimiter after message",
221 1,
222 0,
223 buffer,
224 )
225 .into());
226 };
227 let mut xcode = None;
228 let mut message = raw_message;
229
230 if let Some(pos) = message.iter().position(|c| *c == b' ') {
231 if let Ok(code) = XCode::parse(BytesMut::from(message[0..pos].as_ref())) {
232 xcode = Some(code);
233 message = BytesMut::from(&message[pos + 1..]);
234 }
235 }
236
237 Ok(Self {
238 rcode,
239 xcode,
240 message,
241 })
242 }
243}
244
245impl Writable for Replycode {
246 fn write(&self, buffer: &mut BytesMut) {
247 buffer.put_slice(self.rcode.as_bytes());
248 buffer.put_u8(b' ');
249 if let Some(ref xcode) = self.xcode {
250 buffer.put_slice(xcode.as_bytes());
251 buffer.put_u8(b' ');
252 }
253 buffer.put_slice(&self.message);
254 buffer.put_u8(0);
255 }
256
257 fn len(&self) -> usize {
258 self.rcode.len()
259 + 1
260 + self.xcode.as_ref().map_or(0, |code| code.len() + 1)
261 + self.message.len()
262 + 1
263 }
264
265 fn code(&self) -> u8 {
266 Self::CODE
267 }
268 fn is_empty(&self) -> bool {
269 self.len() == 0
270 }
271}
272
273#[derive(Debug, Clone)]
274pub struct XCode {
275 code: [u16; REPLY_CODE_LENGTH],
276 bytes: BytesMut,
277}
278
279impl From<[u16; REPLY_CODE_LENGTH]> for XCode {
280 fn from(code: [u16; REPLY_CODE_LENGTH]) -> Self {
281 Self::new(code)
282 }
283}
284
285impl XCode {
286 pub fn new(code: [u16; REPLY_CODE_LENGTH]) -> Self {
287 Self {
288 code,
289 bytes: BytesMut::from_iter(code.iter().map(ToString::to_string).join(".").as_bytes()),
290 }
291 }
292
293 fn parse(buffer: BytesMut) -> Result<Self, InvalidData> {
294 let mut positions = buffer.iter().positions(|&c| c == b'.');
295 let mut code: [u16; 3] = [0_u16; REPLY_CODE_LENGTH];
296
297 let mut start = 0;
298 for c_code in code.iter_mut().take(REPLY_CODE_LENGTH - 1) {
299 let Some(end) = positions.next() else {
300 return Err(InvalidData {
301 msg: "missing '.' delimiter in code",
302 offending_bytes: buffer,
303 });
304 };
305 let raw = &buffer[start..end];
306 let Ok(number) = String::from_utf8_lossy(raw).parse() else {
307 return Err(InvalidData {
308 msg: "invalid u16 in code",
309 offending_bytes: buffer,
310 });
311 };
312
313 *c_code = number;
314 start = end + 1;
315 }
316 let raw = &buffer[start..buffer.len()];
317 let Ok(number) = String::from_utf8_lossy(raw).parse() else {
318 return Err(InvalidData {
319 msg: "invalid u16 in code",
320 offending_bytes: buffer,
321 });
322 };
323
324 code[REPLY_CODE_LENGTH - 1] = number;
325
326 Ok(Self {
327 code,
328 bytes: buffer,
329 })
330 }
331
332 #[must_use]
334 pub fn code(&self) -> [u16; REPLY_CODE_LENGTH] {
335 self.code
336 }
337
338 fn as_bytes(&self) -> &[u8] {
339 &self.bytes
340 }
341
342 fn len(&self) -> usize {
343 self.bytes.len()
344 }
345}
346#[derive(Debug, Clone)]
347pub struct RCode {
348 code: [u8; REPLY_CODE_LENGTH],
349 bytes: BytesMut,
350}
351
352impl From<[u8; REPLY_CODE_LENGTH]> for RCode {
353 fn from(code: [u8; REPLY_CODE_LENGTH]) -> Self {
354 Self::new(code)
355 }
356}
357
358impl RCode {
359 pub fn new(code: [u8; REPLY_CODE_LENGTH]) -> Self {
360 Self {
361 code,
362 bytes: BytesMut::from_iter(code.iter().map(ToString::to_string).join("").as_bytes()),
363 }
364 }
365
366 fn parse(buffer: BytesMut) -> Result<Self, InvalidData> {
367 if buffer.len() < REPLY_CODE_LENGTH {
368 return Err(InvalidData {
369 msg: "Invalid length of code",
370 offending_bytes: buffer,
371 });
372 }
373 let mut code: [u8; 3] = [0_u8; REPLY_CODE_LENGTH];
374 for (pos, c_code) in code.iter_mut().enumerate() {
375 let Ok(number) = String::from_utf8_lossy(&[buffer[pos]]).parse::<u8>() else {
376 return Err(InvalidData {
377 msg: "invalid u8 in code",
378 offending_bytes: buffer,
379 });
380 };
381 *c_code = number;
382 }
383 Ok(Self {
384 code,
385 bytes: buffer,
386 })
387 }
388
389 #[must_use]
391 pub fn code(&self) -> [u8; REPLY_CODE_LENGTH] {
392 self.code
393 }
394
395 fn as_bytes(&self) -> &[u8] {
396 &self.bytes
397 }
398
399 fn len(&self) -> usize {
400 self.bytes.len()
401 }
402}
403
404#[cfg(test)]
405mod test {
406 use super::*;
407
408 #[test]
409 fn test_xcode_valid() {
410 let input = BytesMut::from_iter(b"1.20.3");
411 let code = XCode::parse(input).expect("Failed parsing input");
412
413 assert_eq!(code.code, [1, 20, 3]);
414
415 println!("{:?}", code.bytes);
416 assert_eq!(6, code.bytes.len());
417 }
418
419 #[test]
420 fn test_xcode_invalid() {
421 let input = BytesMut::from_iter(b"1.23");
422 let _code = XCode::parse(input).expect_err("Parsing did not error on invalid");
423 }
424
425 #[test]
426 fn test_rcode_valid() {
427 let input = BytesMut::from_iter(b"454");
428 let code = RCode::parse(input).expect("Failed parsing input");
429
430 assert_eq!(code.code, [4, 5, 4]);
431
432 println!("{:?}", code.bytes);
433 assert_eq!(code.bytes.as_ref(), b"454");
434 }
435
436 #[test]
437 fn test_rcode_invalid() {
438 let input = BytesMut::from_iter(b"4.54");
439 let _code = RCode::parse(input).expect_err("Parsing did not error on invalid");
440 }
441
442 #[test]
443 fn test_reply_parse() {
444 let input = BytesMut::from_iter(b"501 5.7.0 Client initiated Authentication Exchange\0");
445 let reply: Replycode = Parsable::parse(input).expect("Parsing failed");
446 assert_eq!(reply.rcode.as_bytes(), b"501");
447 assert_eq!(reply.xcode.expect("Parsing failed").as_bytes(), b"5.7.0");
448 assert_eq!(reply.message, "Client initiated Authentication Exchange");
449 }
450
451 #[test]
452 fn test_reply_parse_empty_xcode() {
453 let input =
454 BytesMut::from_iter(b"421 Service not available, closing transmission channel\0");
455 let reply: Replycode = Parsable::parse(input).expect("Parsing failed");
456 assert_eq!(reply.rcode.as_bytes(), b"421");
457 assert!(reply.xcode.is_none());
458 assert_eq!(
459 reply.message,
460 "Service not available, closing transmission channel"
461 );
462 }
463
464 #[test]
465 fn test_reply_write() {
466 let input = BytesMut::from_iter(b"501 5.7.0 Client initiated Authentication Exchange\0");
467 let reply: Replycode = Parsable::parse(input.clone()).expect("Parsing failed");
468 let mut output = BytesMut::new();
469 reply.write(&mut output);
470 assert_eq!(output.as_ref(), input.as_ref());
471 }
472
473 #[test]
474 fn test_reply_write_with_empty_xcode() {
475 let input =
476 BytesMut::from_iter(b"421 Service not available, closing transmission channel\0");
477 let reply: Replycode = Parsable::parse(input.clone()).expect("Parsing failed");
478 let mut output = BytesMut::new();
479 reply.write(&mut output);
480 assert_eq!(output.as_ref(), input.as_ref());
481 }
482}