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