1use super::Resolver;
8use crate::{
9 DateTime,
10 store::{HostEntry, ReceivedEntry},
11};
12use std::{fmt, net::IpAddr};
13
14#[derive(Clone, Copy)]
17pub struct Received<'m> {
18 resolver: Resolver<'m>,
19 entry: &'m ReceivedEntry,
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum Host<'m> {
25 Name(&'m str),
27 IpAddr(IpAddr),
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
33#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
34pub enum TlsVersion {
35 SSLv2,
37 SSLv3,
39 TLSv1_0,
41 TLSv1_1,
43 TLSv1_2,
45 TLSv1_3,
47 DTLSv1_0,
49 DTLSv1_2,
51 DTLSv1_3,
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
57#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
58pub enum Greeting {
59 Helo,
61 Ehlo,
63 Lhlo,
65}
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
70#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
71#[allow(clippy::upper_case_acronyms)]
72pub enum Protocol {
73 SMTP,
75 ESMTP,
77 ESMTPA,
79 ESMTPS,
81 ESMTPSA,
83 LMTP,
85 LMTPA,
87 LMTPS,
89 LMTPSA,
91 MMS,
93 UTF8SMTP,
95 UTF8SMTPA,
97 UTF8SMTPS,
99 UTF8SMTPSA,
101 UTF8LMTP,
103 UTF8LMTPA,
105 UTF8LMTPS,
107 UTF8LMTPSA,
109 HTTP,
111 HTTPS,
113 IMAP,
115 POP3,
117 Local,
119}
120
121impl<'m> Received<'m> {
122 pub(crate) fn new(resolver: Resolver<'m>, entry: &'m ReceivedEntry) -> Self {
123 Received { resolver, entry }
124 }
125
126 fn host(&self, host: HostEntry) -> Option<Host<'m>> {
127 match host {
128 HostEntry::None => None,
129 HostEntry::Name(name) => Some(Host::Name(self.resolver.str(name))),
130 HostEntry::Ip(ip) => Some(Host::IpAddr(ip)),
131 }
132 }
133
134 pub fn from(&self) -> Option<Host<'m>> {
136 self.host(self.entry.from)
137 }
138
139 pub fn from_ip(&self) -> Option<IpAddr> {
142 self.entry.from_ip
143 }
144
145 pub fn from_iprev(&self) -> Option<&'m str> {
147 self.resolver.opt(self.entry.from_iprev)
148 }
149
150 pub fn by(&self) -> Option<Host<'m>> {
152 self.host(self.entry.by)
153 }
154
155 pub fn for_(&self) -> Option<&'m str> {
157 self.resolver.opt(self.entry.for_)
158 }
159
160 pub fn with(&self) -> Option<Protocol> {
162 self.entry.with
163 }
164
165 pub fn tls_version(&self) -> Option<TlsVersion> {
167 self.entry.tls_version
168 }
169
170 pub fn tls_cipher(&self) -> Option<&'m str> {
172 self.resolver.opt(self.entry.tls_cipher)
173 }
174
175 pub fn id(&self) -> Option<&'m str> {
177 self.resolver.opt(self.entry.id)
178 }
179
180 pub fn ident(&self) -> Option<&'m str> {
182 self.resolver.opt(self.entry.ident)
183 }
184
185 pub fn helo(&self) -> Option<Host<'m>> {
187 self.host(self.entry.helo)
188 }
189
190 pub fn helo_cmd(&self) -> Option<Greeting> {
192 self.entry.helo_cmd
193 }
194
195 pub fn via(&self) -> Option<&'m str> {
197 self.resolver.opt(self.entry.via)
198 }
199
200 pub fn date(&self) -> Option<DateTime> {
202 self.entry.date
203 }
204}
205
206impl fmt::Debug for Received<'_> {
207 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208 f.debug_struct("Received")
209 .field("from", &self.from())
210 .field("from_ip", &self.from_ip())
211 .field("from_iprev", &self.from_iprev())
212 .field("by", &self.by())
213 .field("for", &self.for_())
214 .field("with", &self.with())
215 .field("tls_version", &self.tls_version())
216 .field("tls_cipher", &self.tls_cipher())
217 .field("id", &self.id())
218 .field("ident", &self.ident())
219 .field("helo", &self.helo())
220 .field("helo_cmd", &self.helo_cmd())
221 .field("via", &self.via())
222 .field("date", &self.date())
223 .finish()
224 }
225}
226
227impl PartialEq for Received<'_> {
228 fn eq(&self, other: &Self) -> bool {
229 self.from() == other.from()
230 && self.from_ip() == other.from_ip()
231 && self.from_iprev() == other.from_iprev()
232 && self.by() == other.by()
233 && self.for_() == other.for_()
234 && self.with() == other.with()
235 && self.tls_version() == other.tls_version()
236 && self.tls_cipher() == other.tls_cipher()
237 && self.id() == other.id()
238 && self.ident() == other.ident()
239 && self.helo() == other.helo()
240 && self.helo_cmd() == other.helo_cmd()
241 && self.via() == other.via()
242 && self.date() == other.date()
243 }
244}
245
246impl Eq for Received<'_> {}
247
248impl TlsVersion {
249 pub fn as_str(&self) -> &'static str {
251 match self {
252 TlsVersion::SSLv2 => "SSLv2",
253 TlsVersion::SSLv3 => "SSLv3",
254 TlsVersion::TLSv1_0 => "TLSv1.0",
255 TlsVersion::TLSv1_1 => "TLSv1.1",
256 TlsVersion::TLSv1_2 => "TLSv1.2",
257 TlsVersion::TLSv1_3 => "TLSv1.3",
258 TlsVersion::DTLSv1_0 => "DTLSv1.0",
259 TlsVersion::DTLSv1_2 => "DTLSv1.2",
260 TlsVersion::DTLSv1_3 => "DTLSv1.3",
261 }
262 }
263}
264
265impl Greeting {
266 pub fn as_str(&self) -> &'static str {
268 match self {
269 Greeting::Helo => "HELO",
270 Greeting::Ehlo => "EHLO",
271 Greeting::Lhlo => "LHLO",
272 }
273 }
274}
275
276impl Protocol {
277 pub fn as_str(&self) -> &'static str {
279 match self {
280 Protocol::SMTP => "SMTP",
281 Protocol::LMTP => "LMTP",
282 Protocol::ESMTP => "ESMTP",
283 Protocol::ESMTPS => "ESMTPS",
284 Protocol::ESMTPA => "ESMTPA",
285 Protocol::ESMTPSA => "ESMTPSA",
286 Protocol::LMTPA => "LMTPA",
287 Protocol::LMTPS => "LMTPS",
288 Protocol::LMTPSA => "LMTPSA",
289 Protocol::UTF8SMTP => "UTF8SMTP",
290 Protocol::UTF8SMTPA => "UTF8SMTPA",
291 Protocol::UTF8SMTPS => "UTF8SMTPS",
292 Protocol::UTF8SMTPSA => "UTF8SMTPSA",
293 Protocol::UTF8LMTP => "UTF8LMTP",
294 Protocol::UTF8LMTPA => "UTF8LMTPA",
295 Protocol::UTF8LMTPS => "UTF8LMTPS",
296 Protocol::UTF8LMTPSA => "UTF8LMTPSA",
297 Protocol::HTTP => "HTTP",
298 Protocol::HTTPS => "HTTPS",
299 Protocol::IMAP => "IMAP",
300 Protocol::POP3 => "POP3",
301 Protocol::MMS => "MMS",
302 Protocol::Local => "Local",
303 }
304 }
305}
306
307impl fmt::Display for Host<'_> {
308 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
309 match self {
310 Host::Name(name) => f.write_str(name),
311 Host::IpAddr(ip) => ip.fmt(f),
312 }
313 }
314}
315
316impl fmt::Display for Protocol {
317 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
318 f.write_str(self.as_str())
319 }
320}
321
322impl fmt::Display for Greeting {
323 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
324 f.write_str(self.as_str())
325 }
326}
327
328impl fmt::Display for TlsVersion {
329 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
330 f.write_str(self.as_str())
331 }
332}