1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
use log::trace;
use std::io;
pub(crate) const EMPTY_RESPONSE: Response = Response::empty();
pub(crate) const START_TLS: Response =
Response::fixed_action(220, "Ready to start TLS", Action::UpgradeTls);
pub const GOODBYE: Response = Response::fixed(221, "Goodbye");
pub const AUTH_OK: Response = Response::fixed(235, "Authentication succeeded");
pub const OK: Response = Response::fixed(250, "OK");
pub(crate) const VERIFY_RESPONSE: Response = Response::fixed(252, "Maybe");
pub(crate) const EMPTY_AUTH_CHALLENGE: Response = Response::fixed(334, "");
pub const START_DATA: Response = Response::fixed(354, "Start mail input; end with <CRLF>.<CRLF>");
pub(crate) const INVALID_STATE: Response =
Response::fixed(421, "Internal service error, closing connection");
pub const NO_SERVICE: Response = Response::fixed(421, "Service not available, closing connection");
pub const INTERNAL_ERROR: Response = Response::fixed(451, "Aborted: local error in processing");
pub const OUT_OF_SPACE: Response = Response::fixed(452, "Insufficient system storage");
pub const TEMP_AUTH_FAILURE: Response = Response::fixed(454, "Temporary authentication failure");
pub(crate) const SYNTAX_ERROR: Response = Response::fixed(500, "Syntax error");
pub(crate) const MISSING_PARAMETER: Response = Response::fixed(502, "Missing parameter");
pub(crate) const BAD_SEQUENCE_COMMANDS: Response = Response::fixed(503, "Bad sequence of commands");
pub const NO_STORAGE: Response = Response::fixed(552, "Exceeded storage allocation");
pub const AUTHENTICATION_REQUIRED: Response = Response::fixed(530, "Authentication required");
pub const INVALID_CREDENTIALS: Response = Response::fixed(535, "Invalid credentials");
pub const NO_MAILBOX: Response = Response::fixed(550, "Mailbox unavailable");
pub const BAD_HELLO: Response = Response::fixed(550, "Bad HELO");
pub const BLOCKED_IP: Response = Response::fixed(550, "IP address on blocklists");
pub const BAD_MAILBOX: Response = Response::fixed(553, "Mailbox name not allowed");
pub const TRANSACTION_FAILED: Response = Response::fixed(554, "Transaction failed");
#[derive(Clone, Debug, PartialEq)]
pub struct Response {
pub code: u16,
message: Message,
pub is_error: bool,
pub action: Action,
}
#[derive(Clone, Debug, PartialEq)]
pub(crate) enum Message {
Fixed(&'static str),
Custom(String),
Dynamic(String, Vec<&'static str>),
Empty,
}
#[derive(PartialEq, Clone, Debug)]
pub enum Action {
Close,
UpgradeTls,
NoReply,
Reply,
}
impl Response {
pub(crate) const fn fixed(code: u16, message: &'static str) -> Self {
Self::fixed_action(code, message, Response::action_from_code(code))
}
const fn action_from_code(code: u16) -> Action {
match code {
221 | 421 => Action::Close,
_ => Action::Reply,
}
}
pub(crate) const fn fixed_action(code: u16, message: &'static str, action: Action) -> Self {
Self {
code,
message: Message::Fixed(message),
is_error: (code < 200 || code >= 400),
action,
}
}
pub const fn custom(code: u16, message: String) -> Self {
Self {
code,
message: Message::Custom(message),
is_error: (code < 200 || code >= 400),
action: Response::action_from_code(code),
}
}
pub(crate) fn dynamic(code: u16, head: String, tail: Vec<&'static str>) -> Self {
Self {
code,
message: Message::Dynamic(head, tail),
is_error: false,
action: Action::Reply,
}
}
pub(crate) const fn empty() -> Self {
Self {
code: 0,
message: Message::Empty,
is_error: false,
action: Action::NoReply,
}
}
pub fn write_to(&self, out: &mut dyn io::Write) -> io::Result<()> {
match &self.message {
Message::Dynamic(ref head, ref tail) => {
if tail.is_empty() {
write!(out, "{} {}\r\n", self.code, head)?;
} else {
write!(out, "{}-{}\r\n", self.code, head)?;
for i in 0..tail.len() {
if tail.len() > 1 && i < tail.len() - 1 {
write!(out, "{}-{}\r\n", self.code, tail[i])?;
} else {
write!(out, "{} {}\r\n", self.code, tail[i])?;
}
}
}
}
Message::Fixed(s) => write!(out, "{} {}\r\n", self.code, s)?,
Message::Custom(s) => write!(out, "{} {}\r\n", self.code, s)?,
Message::Empty => (),
};
Ok(())
}
pub(crate) fn log(&self) {
match self.message {
Message::Empty => (),
_ => {
let mut buf = Vec::new();
let _ = self.write_to(&mut buf);
trace!("< {}", String::from_utf8_lossy(&buf));
}
}
}
}