miltr_common/actions/
to_mta_only.rs

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/// (Silently) discard this mail without forwarding it
13#[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/// Reject this mail, informing the smtp client about it
45#[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/// Return a tempfail code to the smtp client
77#[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/// Skip this mail processing
108#[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/// Return this status code to the smtp client
141#[derive(Debug, Clone)]
142pub struct Replycode {
143    rcode: Code,
144    xcode: Code,
145    message: BytesMut,
146}
147
148impl Replycode {
149    const CODE: u8 = b'y';
150
151    /// Create a Replycode
152    #[must_use]
153    #[allow(clippy::similar_names)]
154    pub fn new<R: Into<Code>, X: Into<Code>>(rcode: R, xcode: X, message: &str) -> Self {
155        let rcode = rcode.into();
156        let xcode = xcode.into();
157
158        Self {
159            rcode,
160            xcode,
161            message: BytesMut::from(message.as_bytes()),
162        }
163    }
164
165    /// The message associated with this reply code
166    #[must_use]
167    pub fn message(&self) -> Cow<str> {
168        String::from_utf8_lossy(&self.message)
169    }
170
171    /// The smtp return code
172    #[must_use]
173    pub fn rcode(&self) -> &Code {
174        &self.rcode
175    }
176
177    /// The smtp enhanced return code
178    #[must_use]
179    pub fn xcode(&self) -> &Code {
180        &self.xcode
181    }
182}
183
184impl Parsable for Replycode {
185    const CODE: u8 = Self::CODE;
186
187    // rcode and xcode are just named that in the docs. Keeping it consistent.
188    #[allow(clippy::similar_names)]
189    fn parse(mut buffer: BytesMut) -> Result<Self, ProtocolError> {
190        #[allow(clippy::similar_names)]
191        let Some(rcode) = buffer.delimited(0) else {
192            return Err(NotEnoughData::new(
193                STAGE_DECODING,
194                "Replycode",
195                "Missing nullbyte delimiter after rcode",
196                1,
197                0,
198                buffer,
199            )
200            .into());
201        };
202        let rcode = Code::parse(rcode)?;
203
204        let Some(xcode) = buffer.delimited(0) else {
205            return Err(NotEnoughData::new(
206                STAGE_DECODING,
207                "Replycode",
208                "Missing nullbyte delimiter after xcode",
209                1,
210                0,
211                buffer,
212            )
213            .into());
214        };
215        let xcode = Code::parse(xcode)?;
216
217        let Some(message) = buffer.delimited(0) else {
218            return Err(NotEnoughData::new(
219                STAGE_DECODING,
220                "Replycode",
221                "Missing nullbyte delimiter after message",
222                1,
223                0,
224                buffer,
225            )
226            .into());
227        };
228
229        Ok(Self {
230            rcode,
231            xcode,
232            message,
233        })
234    }
235}
236
237impl Writable for Replycode {
238    fn write(&self, buffer: &mut BytesMut) {
239        buffer.put_slice(self.rcode.as_bytes());
240        buffer.put_u8(0);
241        buffer.put_slice(self.xcode.as_bytes());
242        buffer.put_u8(0);
243        buffer.put_slice(&self.message);
244        buffer.put_u8(0);
245    }
246
247    fn len(&self) -> usize {
248        self.rcode.len() + 1 + self.xcode.len() + 1 + self.message.len() + 1
249    }
250
251    fn code(&self) -> u8 {
252        Self::CODE
253    }
254    fn is_empty(&self) -> bool {
255        self.len() == 0
256    }
257}
258
259#[derive(Debug, Clone)]
260pub struct Code {
261    code: [u16; REPLY_CODE_LENGTH],
262    bytes: BytesMut,
263}
264
265impl From<[u16; REPLY_CODE_LENGTH]> for Code {
266    fn from(code: [u16; REPLY_CODE_LENGTH]) -> Self {
267        Self::new(code)
268    }
269}
270
271impl Code {
272    pub fn new(code: [u16; REPLY_CODE_LENGTH]) -> Self {
273        Self {
274            code,
275            bytes: BytesMut::from_iter(code.iter().map(ToString::to_string).join(".").as_bytes()),
276        }
277    }
278
279    fn parse(buffer: BytesMut) -> Result<Self, InvalidData> {
280        let mut positions = buffer.iter().positions(|&c| c == b'.');
281        let mut code: [u16; 3] = [0_u16; REPLY_CODE_LENGTH];
282
283        let mut start = 0;
284        for c_code in code.iter_mut().take(REPLY_CODE_LENGTH - 1) {
285            let Some(end) = positions.next() else {
286                return Err(InvalidData {
287                    msg: "missing '.' delimiter in code",
288                    offending_bytes: buffer,
289                });
290            };
291            let raw = &buffer[start..end];
292            let Ok(number) = String::from_utf8_lossy(raw).parse() else {
293                return Err(InvalidData {
294                    msg: "invalid u16 in code",
295                    offending_bytes: buffer,
296                });
297            };
298
299            *c_code = number;
300            start = end + 1;
301        }
302        let raw = &buffer[start..buffer.len()];
303        let Ok(number) = String::from_utf8_lossy(raw).parse() else {
304            return Err(InvalidData {
305                msg: "invalid u16 in code",
306                offending_bytes: buffer,
307            });
308        };
309
310        code[REPLY_CODE_LENGTH - 1] = number;
311
312        Ok(Self {
313            code,
314            bytes: buffer,
315        })
316    }
317
318    /// The status code
319    #[must_use]
320    pub fn code(&self) -> [u16; REPLY_CODE_LENGTH] {
321        self.code
322    }
323
324    fn as_bytes(&self) -> &[u8] {
325        &self.bytes
326    }
327
328    fn len(&self) -> usize {
329        self.bytes.len()
330    }
331}
332
333#[cfg(test)]
334mod test {
335    use super::*;
336
337    #[test]
338    fn test_rcode_valid() {
339        let input = BytesMut::from_iter(b"1.20.3");
340        let code = Code::parse(input).expect("Failed parsing input");
341
342        assert_eq!(code.code, [1, 20, 3]);
343
344        println!("{:?}", code.bytes);
345        assert_eq!(6, code.bytes.len());
346    }
347
348    #[test]
349    fn test_rcode_invalid() {
350        let input = BytesMut::from_iter(b"1.23");
351        let _code = Code::parse(input).expect_err("Parsing did not error on invalid");
352    }
353}