rust_smtp_server/
data.rs

1use std::{pin::Pin, future::Future, task::Poll};
2
3use tokio::io::{self, AsyncBufRead, AsyncRead, AsyncReadExt};
4
5use crate::{backend::{Backend}};
6
7pub type EnhancedCode = [i8; 3];
8
9pub struct SMTPError {
10    code: u16,
11    enhanced_code: EnhancedCode,
12    message: String,
13}
14
15impl std::fmt::Display for SMTPError {
16    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17        write!(f, "{} {}", self.code, self.message)
18    }
19}
20
21pub const NO_ENHANCED_CODE: EnhancedCode = [-1, -1, -1];
22
23pub const ENHANCED_CODE_NOT_SET: EnhancedCode = [0, 0, 0];
24
25#[derive(PartialEq)]
26enum State {
27    BeginLine,
28    Dot,
29    DotCR,
30    CR,
31    Data,
32    EOF,
33}
34
35
36impl SMTPError {
37    pub fn err_data_too_large() -> Self {
38        return SMTPError {
39            code: 552,
40            enhanced_code: ENHANCED_CODE_NOT_SET,
41            message: "Requested mail action aborted: exceeded storage allocation".to_string(),
42        };
43    }
44
45    pub fn err_auth_required() -> Self {
46        return SMTPError {
47            code: 502,
48            enhanced_code: [5, 7, 0],
49            message: "Please authenticate first".to_string(),
50        };
51    }
52
53    pub fn err_auth_unsupported() -> Self {
54        return SMTPError {
55            code: 502,
56            enhanced_code: [5, 7, 0],
57            message: "Authentication not supported".to_string(),
58        };
59    }
60
61    pub fn error(&self) -> String {
62        self.message.clone()
63    }
64
65    fn is_temporary(&self) -> bool {
66        self.code >= 400 && self.code < 500
67    }
68}
69
70pub struct DataReader<R: AsyncRead + Unpin> {
71    pub r: R,
72    state: State,
73    pub limited: bool,
74    n: usize,
75}
76
77impl<R: AsyncBufRead + Unpin> DataReader<R> {
78    pub fn new<B: Backend>(r: R, max_message_bytes: usize) -> Self {
79        DataReader {
80            r,
81            state: State::BeginLine,
82            limited: max_message_bytes > 0,
83            n: max_message_bytes,
84        }
85    }
86}
87
88impl<R: AsyncBufRead + Unpin> AsyncRead for DataReader<R> {
89    fn poll_read(
90        self: std::pin::Pin<&mut Self>,
91        cx: &mut std::task::Context<'_>,
92        buf: &mut tokio::io::ReadBuf<'_>,
93    ) -> std::task::Poll<std::io::Result<()>> {
94        let mut this = self.get_mut();
95
96        if this.n == 0 || this.state == State::EOF {
97            return Poll::Ready(Ok(()));
98        }
99
100        let mut fut = Box::pin(this.r.read_u8());
101        match Pin::new(&mut fut).poll(cx) {
102            Poll::Ready(Ok(c)) => {
103                match this.state {
104                    State::BeginLine => {
105                        if c == b'.' {
106                            this.state = State::Dot;
107                            cx.waker().wake_by_ref();
108                            return Poll::Pending;
109                        }
110                        this.state = State::Data;
111                    }
112                    State::Dot => {
113                        if c == b'\r' {
114                            this.state = State::DotCR;
115                            cx.waker().wake_by_ref();
116                            return Poll::Pending;
117                        }
118                        if c == b'\n' {
119                            this.state = State::EOF;
120                            return Poll::Ready(Ok(()));
121                        }
122    
123                        this.state = State::Data;
124                    }
125                    State::DotCR => {
126                        if c == b'\n' {
127                            this.state = State::EOF;
128                            return Poll::Ready(Ok(()));
129                        }
130                        this.state = State::Data;
131                    }
132                    State::CR => {
133                        if c == b'\n' {
134                            this.state = State::BeginLine;
135                            return Poll::Ready(Ok(()));
136                        }
137                        this.state = State::Data;
138                    }
139                    State::Data => {
140                        if c == b'\r' {
141                            this.state = State::CR;
142                        }
143                        if c == b'\n' {
144                            this.state = State::BeginLine;
145                        }
146                    }
147                    State::EOF => {
148                        return Poll::Ready(Ok(()));
149                    }
150                }
151
152                this.n -= 1;
153                buf.put_slice(&[c]);
154                return Poll::Ready(Ok(()));
155            }
156            Poll::Ready(Err(e)) => {
157                if e.kind() == io::ErrorKind::UnexpectedEof {
158                    return Poll::Ready(Ok(()));
159                }
160                return Poll::Ready(Err(e));
161            }
162            Poll::Pending => {
163                return Poll::Pending;
164            }
165        }
166
167    }
168}