1pub mod arf;
8pub mod dmarc;
9pub mod tlsrpt;
10
11use std::{borrow::Cow, net::IpAddr};
12
13use serde::{Deserialize, Serialize};
14
15#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
16#[cfg_attr(
17 feature = "rkyv",
18 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
19)]
20pub struct DateRange {
21 begin: u64,
22 end: u64,
23}
24
25#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
26#[cfg_attr(
27 feature = "rkyv",
28 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
29)]
30pub struct ReportMetadata {
31 org_name: String,
32 email: String,
33 extra_contact_info: Option<String>,
34 report_id: String,
35 date_range: DateRange,
36 error: Vec<String>,
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
40#[cfg_attr(
41 feature = "rkyv",
42 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
43)]
44pub enum Alignment {
45 Relaxed,
46 Strict,
47 #[default]
48 Unspecified,
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
52#[cfg_attr(
53 feature = "rkyv",
54 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
55)]
56pub enum Disposition {
57 None,
58 Quarantine,
59 Reject,
60 #[default]
61 Unspecified,
62}
63
64#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
65#[cfg_attr(
66 feature = "rkyv",
67 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
68)]
69pub enum ActionDisposition {
70 None,
71 Pass,
72 Quarantine,
73 Reject,
74 #[default]
75 Unspecified,
76}
77
78#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
79#[cfg_attr(
80 feature = "rkyv",
81 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
82)]
83pub struct PolicyPublished {
84 pub domain: String,
85 pub version_published: Option<f32>,
86 pub adkim: Alignment,
87 pub aspf: Alignment,
88 pub p: Disposition,
89 pub sp: Disposition,
90 pub testing: bool,
91 pub fo: Option<String>,
92}
93
94impl Eq for PolicyPublished {}
95
96#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize, Default)]
97#[cfg_attr(
98 feature = "rkyv",
99 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
100)]
101pub enum DmarcResult {
102 Pass,
103 Fail,
104 #[default]
105 Unspecified,
106}
107
108#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize, Default)]
109#[cfg_attr(
110 feature = "rkyv",
111 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
112)]
113pub enum PolicyOverride {
114 Forwarded,
115 SampledOut,
116 TrustedForwarder,
117 MailingList,
118 LocalPolicy,
119 #[default]
120 Other,
121}
122
123#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
124#[cfg_attr(
125 feature = "rkyv",
126 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
127)]
128pub struct PolicyOverrideReason {
129 type_: PolicyOverride,
130 comment: Option<String>,
131}
132
133#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
134#[cfg_attr(
135 feature = "rkyv",
136 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
137)]
138pub struct PolicyEvaluated {
139 disposition: ActionDisposition,
140 dkim: DmarcResult,
141 spf: DmarcResult,
142 reason: Vec<PolicyOverrideReason>,
143}
144
145#[derive(Debug, Clone, Hash, Default, PartialEq, Eq, Serialize, Deserialize)]
146#[cfg_attr(
147 feature = "rkyv",
148 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
149)]
150pub struct Row {
151 source_ip: Option<IpAddr>,
152 count: u32,
153 policy_evaluated: PolicyEvaluated,
154}
155
156#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
157#[cfg_attr(
158 feature = "rkyv",
159 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
160)]
161pub struct Extension {
162 name: String,
163 definition: String,
164}
165
166#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
167#[cfg_attr(
168 feature = "rkyv",
169 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
170)]
171pub struct Identifier {
172 envelope_to: Option<String>,
173 envelope_from: String,
174 header_from: String,
175}
176
177#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize, Default)]
178#[cfg_attr(
179 feature = "rkyv",
180 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
181)]
182pub enum DkimResult {
183 #[default]
184 None,
185 Pass,
186 Fail,
187 Policy,
188 Neutral,
189 TempError,
190 PermError,
191}
192
193#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
194#[cfg_attr(
195 feature = "rkyv",
196 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
197)]
198pub struct DKIMAuthResult {
199 domain: String,
200 selector: String,
201 result: DkimResult,
202 human_result: Option<String>,
203}
204
205#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize, Default)]
206#[cfg_attr(
207 feature = "rkyv",
208 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
209)]
210pub enum SPFDomainScope {
211 Helo,
212 MailFrom,
213 #[default]
214 Unspecified,
215}
216
217#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize, Default)]
218#[cfg_attr(
219 feature = "rkyv",
220 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
221)]
222pub enum SpfResult {
223 #[default]
224 None,
225 Neutral,
226 Pass,
227 Fail,
228 SoftFail,
229 TempError,
230 PermError,
231}
232
233#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
234#[cfg_attr(
235 feature = "rkyv",
236 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
237)]
238pub struct SPFAuthResult {
239 domain: String,
240 scope: SPFDomainScope,
241 result: SpfResult,
242 human_result: Option<String>,
243}
244
245#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
246#[cfg_attr(
247 feature = "rkyv",
248 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
249)]
250pub struct AuthResult {
251 dkim: Vec<DKIMAuthResult>,
252 spf: Vec<SPFAuthResult>,
253}
254
255#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
256#[cfg_attr(
257 feature = "rkyv",
258 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
259)]
260pub struct Record {
261 row: Row,
262 identifiers: Identifier,
263 auth_results: AuthResult,
264 extensions: Vec<Extension>,
265}
266
267#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
268#[cfg_attr(
269 feature = "rkyv",
270 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
271)]
272pub struct Report {
273 version: f32,
274 report_metadata: ReportMetadata,
275 policy_published: PolicyPublished,
276 record: Vec<Record>,
277 extensions: Vec<Extension>,
278}
279
280impl Eq for Report {}
281
282#[derive(Debug, Clone, PartialEq, Eq)]
283pub enum Error {
284 MailParseError,
285 ReportParseError(String),
286 UncompressError(String),
287 NoReportsFound,
288}
289
290impl From<String> for Error {
291 fn from(err: String) -> Self {
292 Error::ReportParseError(err)
293 }
294}
295
296#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
297#[cfg_attr(
298 feature = "rkyv",
299 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
300)]
301pub struct Feedback<'x> {
302 feedback_type: FeedbackType,
303 arrival_date: Option<i64>,
304 #[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
305 authentication_results: Vec<Cow<'x, str>>,
306 incidents: u32,
307 #[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
308 original_envelope_id: Option<Cow<'x, str>>,
309 #[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
310 original_mail_from: Option<Cow<'x, str>>,
311 #[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
312 original_rcpt_to: Option<Cow<'x, str>>,
313 #[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
314 reported_domain: Vec<Cow<'x, str>>,
315 #[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
316 reported_uri: Vec<Cow<'x, str>>,
317 #[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
318 reporting_mta: Option<Cow<'x, str>>,
319 source_ip: Option<IpAddr>,
320 #[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
321 user_agent: Option<Cow<'x, str>>,
322 version: u32,
323 source_port: u32,
324
325 auth_failure: AuthFailureType,
327 delivery_result: DeliveryResult,
328 #[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
329 dkim_adsp_dns: Option<Cow<'x, str>>,
330 #[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
331 dkim_canonicalized_body: Option<Cow<'x, str>>,
332 #[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
333 dkim_canonicalized_header: Option<Cow<'x, str>>,
334 #[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
335 dkim_domain: Option<Cow<'x, str>>,
336 #[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
337 dkim_identity: Option<Cow<'x, str>>,
338 #[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
339 dkim_selector: Option<Cow<'x, str>>,
340 #[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
341 dkim_selector_dns: Option<Cow<'x, str>>,
342 #[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
343 spf_dns: Option<Cow<'x, str>>,
344 identity_alignment: IdentityAlignment,
345
346 #[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
347 message: Option<Cow<'x, str>>,
348 #[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
349 headers: Option<Cow<'x, str>>,
350}
351
352#[derive(Debug, Clone, PartialEq, Eq, Copy, Serialize, Deserialize, Default)]
353#[cfg_attr(
354 feature = "rkyv",
355 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
356)]
357pub enum AuthFailureType {
358 Adsp,
359 BodyHash,
360 Revoked,
361 Signature,
362 Spf,
363 Dmarc,
364 #[default]
365 Unspecified,
366}
367
368#[derive(Debug, Clone, PartialEq, Eq, Copy, Serialize, Deserialize, Default)]
369#[cfg_attr(
370 feature = "rkyv",
371 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
372)]
373pub enum IdentityAlignment {
374 None,
375 Spf,
376 Dkim,
377 DkimSpf,
378 #[default]
379 Unspecified,
380}
381
382#[derive(Debug, Clone, PartialEq, Eq, Copy, Serialize, Deserialize, Default)]
383#[cfg_attr(
384 feature = "rkyv",
385 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
386)]
387pub enum DeliveryResult {
388 Delivered,
389 Spam,
390 Policy,
391 Reject,
392 Other,
393 #[default]
394 Unspecified,
395}
396
397#[derive(Debug, Clone, PartialEq, Eq, Copy, Serialize, Deserialize, Default)]
398#[cfg_attr(
399 feature = "rkyv",
400 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
401)]
402pub enum FeedbackType {
403 Abuse,
404 AuthFailure,
405 Fraud,
406 NotSpam,
407 #[default]
408 Other,
409 Virus,
410}
411
412impl From<&crate::DkimResult> for AuthFailureType {
413 fn from(value: &crate::DkimResult) -> Self {
414 match value {
415 crate::DkimResult::Neutral(err)
416 | crate::DkimResult::Fail(err)
417 | crate::DkimResult::PermError(err)
418 | crate::DkimResult::TempError(err) => match err {
419 crate::Error::FailedBodyHashMatch => AuthFailureType::BodyHash,
420 crate::Error::RevokedPublicKey => AuthFailureType::Revoked,
421 _ => AuthFailureType::Signature,
422 },
423 crate::DkimResult::Pass | crate::DkimResult::None => AuthFailureType::Signature,
424 }
425 }
426}