1use logos::{Lexer, Logos};
4
5use crate::alloc_yes::AuthenticationResults;
6use crate::auth::{SmtpAuthResult, SmtpAuthResultCode};
7use crate::dkim::{DkimResult, DkimResultCode};
8use crate::error::AuthResultsError;
9use crate::iprev::{IpRevResult, IpRevResultCode};
10use crate::spf::{SpfResult, SpfResultCode};
11
12use crate::alloc_yes::UnknownResult;
13
14mod host_version;
15mod policy;
16mod ptypes;
17mod reason;
18mod unknown;
19mod version;
20
21use crate::parser::comment::{parse_comment, CommentToken};
22use host_version::{parse_host_version, HostVersionToken};
23use policy::{parse_policy, PolicyToken};
24use ptypes::{parse_ptype_properties, PtypeToken};
25use reason::{parse_reason, ReasonToken};
26use unknown::{parse_unknown, UnknownToken};
27use version::{parse_version, VersionToken};
28
29#[cfg(feature = "mail_parser")]
30use mail_parser::HeaderValue;
31
32impl<'hdr> TryFrom<AuthResultToken<'hdr>> for SmtpAuthResultCode {
33 type Error = AuthResultsError<'hdr>;
34
35 fn try_from(token: AuthResultToken<'hdr>) -> Result<Self, Self::Error> {
36 let res = match token {
37 AuthResultToken::NoneNone => Self::NoneSmtp,
38 AuthResultToken::Pass => Self::Pass,
39 AuthResultToken::Fail => Self::Fail,
40 AuthResultToken::TempError => Self::TempError,
41 AuthResultToken::PermError => Self::PermError,
42 _ => return Err(AuthResultsError::InvalidSmtpAuthResult("".to_string())),
43 };
44 Ok(res)
45 }
46}
47
48impl<'hdr> TryFrom<AuthResultToken<'hdr>> for DkimResultCode {
49 type Error = AuthResultsError<'hdr>;
50
51 fn try_from(token: AuthResultToken<'hdr>) -> Result<Self, Self::Error> {
52 let res = match token {
53 AuthResultToken::NoneNone => Self::NoneDkim,
54 AuthResultToken::Pass => Self::Pass,
55 AuthResultToken::Fail => Self::Fail,
56 AuthResultToken::Policy => Self::Policy,
57 AuthResultToken::Neutral => Self::Neutral,
58 AuthResultToken::TempError => Self::TempError,
59 AuthResultToken::PermError => Self::PermError,
60 _ => return Err(AuthResultsError::InvalidDkimResult("".to_string())),
61 };
62 Ok(res)
63 }
64}
65
66impl<'hdr> TryFrom<AuthResultToken<'hdr>> for SpfResultCode {
67 type Error = AuthResultsError<'hdr>;
68
69 fn try_from(token: AuthResultToken<'_>) -> Result<Self, Self::Error> {
70 let res = match token {
71 AuthResultToken::NoneNone => Self::NoneSpf,
72 AuthResultToken::Pass => Self::Pass,
73 AuthResultToken::Fail => Self::Fail,
74 AuthResultToken::SoftFail => Self::SoftFail,
75 AuthResultToken::Policy => Self::Policy,
76 AuthResultToken::Neutral => Self::Neutral,
77 AuthResultToken::TempError => Self::TempError,
78 AuthResultToken::PermError => Self::PermError,
79 _ => return Err(AuthResultsError::InvalidSpfResult("".to_string())),
80 };
81 Ok(res)
82 }
83}
84
85impl<'hdr> TryFrom<AuthResultToken<'hdr>> for IpRevResultCode {
86 type Error = AuthResultsError<'hdr>;
87
88 fn try_from(token: AuthResultToken<'_>) -> Result<Self, Self::Error> {
89 let res = match token {
90 AuthResultToken::Pass => Self::Pass,
91 AuthResultToken::Fail => Self::Fail,
92 AuthResultToken::TempError => Self::TempError,
93 AuthResultToken::PermError => Self::PermError,
94 _ => return Err(AuthResultsError::InvalidIpRevResult("".to_string())),
95 };
96 Ok(res)
97 }
98}
99
100#[derive(Debug, Logos)]
101#[logos(skip r"[ \t\r\n]+")]
102pub enum AuthResultToken<'hdr> {
103 #[token("auth", priority = 200)]
104 Auth,
105 #[token("dkim", priority = 200)]
106 Dkim,
107 #[token("spf", priority = 200)]
108 Spf,
109 #[token("iprev", priority = 200)]
110 IpRev,
111
112 #[token("/", priority = 200)]
113 ForwardSlash,
114
115 #[token("=", priority = 200)]
116 Equal,
117
118 #[token("none", priority = 100)]
119 NoneNone,
120 #[token("softfail", priority = 100)]
121 SoftFail,
122 #[token("fail", priority = 100)]
123 Fail,
124 #[token("neutral", priority = 100)]
125 Neutral,
126 #[token("pass", priority = 100)]
127 Pass,
128 #[token("temperror", priority = 100)]
129 TempError,
130 #[token("permerror", priority = 100)]
131 PermError,
132
133 #[token("reason", priority = 50)]
134 Reason,
135
136 #[token("policy", priority = 50)]
137 Policy,
138
139 #[token("(", priority = 200)]
140 CommentStart,
141
142 #[regex("[a-z-_]+", |lex| lex.slice(), priority = 10)]
143 OtherAlphaDash(&'hdr str),
144
145 SuperDumbPlaceholder(&'hdr str),
146}
147
148#[derive(Clone, Debug, PartialEq)]
149enum Stage {
150 WantHost,
151 SawHost,
152 WantIdentifier,
153
154 WantAuthEqual,
156 WantAuthResult,
157
158 WantSpfEqual,
160 WantSpfResult,
161
162 WantDkimEqual,
164 WantDkimResult,
165
166 WantIpRevEqual,
168 WantIpRevResult,
169}
170
171impl Stage {
173 fn is_cur_expect_resultset_want(&self) -> bool {
175 matches!(
176 self,
177 Self::WantAuthResult
178 | Self::WantSpfResult
179 | Self::WantDkimResult
180 | Self::WantIpRevResult
181 )
182 }
183 fn is_cur_expect_resultset_equal(&self) -> bool {
185 matches!(
186 self,
187 Self::WantAuthEqual | Self::WantSpfEqual | Self::WantDkimEqual | Self::WantIpRevEqual
188 )
189 }
190 fn equal_to_result(&mut self) -> bool {
192 let new_stage = match self {
193 Stage::WantAuthEqual => Stage::WantAuthResult,
194 Stage::WantSpfEqual => Stage::WantSpfResult,
195 Stage::WantDkimEqual => Stage::WantDkimResult,
196 Stage::WantIpRevEqual => Stage::WantIpRevResult,
197 _ => return false,
198 };
199 *self = new_stage;
200 true
201 }
202}
203
204#[derive(Clone, Debug)]
205enum ParseCurrentResultChoice<'hdr> {
206 SmtpAuth(SmtpAuthResult<'hdr>),
207 Spf(SpfResult<'hdr>),
208 Dkim(DkimResult<'hdr>),
209 IpRev(IpRevResult<'hdr>),
210}
211
212impl<'hdr> ParseCurrentResultChoice<'hdr> {
213 fn set_reason(&mut self, reason: &'hdr str) {
214 if let ParseCurrentResultChoice::Dkim(ref mut dkim_res) = self {
215 dkim_res.reason = Some(reason);
216 }
217 }
218}
219
220#[derive(Clone, Debug, Default)]
221struct ParseCurrentResultCode<'hdr> {
222 result: Option<ParseCurrentResultChoice<'hdr>>,
223}
224
225fn assign_result_code<'hdr>(
226 token: AuthResultToken<'hdr>,
227 stage: Stage,
228 cur_res: &mut ParseCurrentResultCode<'hdr>,
229) -> Result<(), AuthResultsError<'hdr>> {
230 let mut new_res: ParseCurrentResultCode<'hdr> = ParseCurrentResultCode::default();
231
232 match stage {
233 Stage::WantAuthResult => {
234 let code = SmtpAuthResultCode::try_from(token)?;
235 let smtp_auth_result = SmtpAuthResult {
236 code,
237 ..Default::default()
238 };
239 new_res.result = Some(ParseCurrentResultChoice::SmtpAuth(smtp_auth_result));
240 *cur_res = new_res;
241 Ok(())
242 }
243 Stage::WantSpfResult => {
244 let code = SpfResultCode::try_from(token)?;
245 let spf_result = SpfResult {
246 code,
247 ..Default::default()
248 };
249 new_res.result = Some(ParseCurrentResultChoice::Spf(spf_result));
250 *cur_res = new_res;
251 Ok(())
252 }
253 Stage::WantDkimResult => {
254 let code = DkimResultCode::try_from(token)?;
255 let dkim_result = DkimResult {
256 code,
257 ..Default::default()
258 };
259 new_res.result = Some(ParseCurrentResultChoice::Dkim(dkim_result));
260 *cur_res = new_res;
261 Ok(())
262 }
263 Stage::WantIpRevResult => {
264 let code = IpRevResultCode::try_from(token)?;
265 let iprev_result = IpRevResult {
266 code,
267 ..Default::default()
268 };
269 new_res.result = Some(ParseCurrentResultChoice::IpRev(iprev_result));
270 *cur_res = new_res;
271 Ok(())
272 }
273 _ => Err(AuthResultsError::InvalidResultStage),
274 }
275}
276
277impl<'hdr> From<&'hdr HeaderValue<'hdr>> for AuthenticationResults<'hdr> {
278 fn from(hval: &'hdr HeaderValue<'hdr>) -> Self {
279 let mut res = Self {
280 raw: hval.as_text(),
281 ..Default::default()
282 };
283
284 let text = match hval.as_text() {
285 None => {
286 res.errors.push(AuthResultsError::NoHeader);
287 return res;
288 }
289 Some(text) => text,
290 };
291
292 let mut host_lexer = HostVersionToken::lexer(text);
293
294 let host = match parse_host_version(&mut host_lexer) {
295 Ok(host) => host,
296 Err(e) => {
297 res.errors.push(e);
298 return res;
299 }
300 };
301
302 let mut lexer: Lexer<'hdr, AuthResultToken<'hdr>> = host_lexer.morph();
303 res.host = Some(host);
304
305 let mut stage = Stage::WantIdentifier;
306 let mut cur_res = ParseCurrentResultCode::default();
307
308 let mut raw_part_start = 0;
309
310 while let Some(token) = lexer.next() {
311 match token {
312 Ok(AuthResultToken::NoneNone) if stage == Stage::WantIdentifier => {
313 res.none_done = true;
314 }
315 Ok(AuthResultToken::Auth) if stage == Stage::WantIdentifier => {
316 stage = Stage::WantAuthEqual;
317 raw_part_start = lexer.span().start;
318 }
319 Ok(AuthResultToken::Spf) if stage == Stage::WantIdentifier => {
320 stage = Stage::WantSpfEqual;
321 raw_part_start = lexer.span().start;
322 }
323 Ok(AuthResultToken::Dkim) if stage == Stage::WantIdentifier => {
324 stage = Stage::WantDkimEqual;
325 raw_part_start = lexer.span().start;
326 }
327 Ok(AuthResultToken::IpRev) if stage == Stage::WantIdentifier => {
328 stage = Stage::WantIpRevEqual;
329 raw_part_start = lexer.span().start;
330 }
331 Ok(AuthResultToken::Equal) if stage.is_cur_expect_resultset_equal() => {
332 stage.equal_to_result();
333 }
334 Ok(AuthResultToken::Policy) => {
336 let mut policy_lexer: Lexer<'hdr, PolicyToken<'hdr>> = lexer.morph();
337 let _policy = match parse_policy(&mut policy_lexer) {
338 Ok(policy) => policy,
339 Err(e) => {
340 res.errors.push(e);
341 break;
342 }
343 };
344 lexer = policy_lexer.morph();
345 }
346 Ok(
347 AuthResultToken::Pass
348 | AuthResultToken::Fail
349 | AuthResultToken::TempError
350 | AuthResultToken::PermError
351 | AuthResultToken::SoftFail
352 | AuthResultToken::NoneNone
353 | AuthResultToken::Neutral,
354 ) if stage.is_cur_expect_resultset_want() => {
355 if let Err(e) = assign_result_code(
356 token.expect("BUG: Matched err?!"),
357 stage.clone(),
358 &mut cur_res,
359 ) {
360 res.errors.push(e);
361 break;
362 }
363
364 let lexer_end = lexer.span().end;
365 let mut ptype_lexer = PtypeToken::lexer(lexer.remainder());
366
367 let raw_part_end =
368 match parse_ptype_properties(&mut ptype_lexer, &mut cur_res.result) {
369 Err(e) => {
370 res.errors.push(e);
371 break;
372 }
373 Ok(raw_part_end) => raw_part_end,
374 };
375
376 lexer.bump(ptype_lexer.span().end);
377
378 stage = Stage::WantIdentifier;
379 match cur_res.result {
380 Some(ParseCurrentResultChoice::Dkim(mut dkim_res)) => {
381 dkim_res.raw =
382 Some(&lexer.source()[raw_part_start..lexer_end + raw_part_end]);
383 res.dkim_result.push(dkim_res)
384 }
385 Some(ParseCurrentResultChoice::IpRev(mut iprev_res)) => {
386 iprev_res.raw =
387 Some(&lexer.source()[raw_part_start..lexer_end + raw_part_end]);
388 res.iprev_result.push(iprev_res)
389 }
390 Some(ParseCurrentResultChoice::Spf(mut spf_res)) => {
391 spf_res.raw =
392 Some(&lexer.source()[raw_part_start..lexer_end + raw_part_end]);
393 res.spf_result.push(spf_res)
394 }
395 Some(ParseCurrentResultChoice::SmtpAuth(mut auth_res)) => {
396 auth_res.raw =
397 Some(&lexer.source()[raw_part_start..lexer_end + raw_part_end]);
398 res.smtp_auth_result.push(auth_res)
399 }
400 _ => {
401 res.errors
402 .push(AuthResultsError::ParseCurrentPushNotImplemented);
403 break;
404 }
405 }
406 cur_res = ParseCurrentResultCode::default();
407 }
408 Ok(AuthResultToken::ForwardSlash) => {
409 let mut version_lexer = VersionToken::lexer(lexer.remainder());
410
411 let _version_res = match parse_version(&mut version_lexer) {
413 Ok(version) => version,
414 Err(e) => {
415 res.errors.push(e);
416 break;
417 }
418 };
419 lexer.bump(version_lexer.span().end);
420 }
421 Ok(AuthResultToken::CommentStart) => {
422 let mut comment_lexer: Lexer<'hdr, CommentToken<'hdr>> = lexer.morph();
423 match parse_comment(&mut comment_lexer) {
424 Ok(_comment) => {} Err(e) => {
426 res.errors.push(AuthResultsError::ParseComment(e));
427 break;
428 }
429 }
430 lexer = comment_lexer.morph();
431 }
432 Ok(AuthResultToken::OtherAlphaDash(_)) if stage == Stage::WantIdentifier => {
434 let start = lexer.span().start;
435 let mut unknown_lexer: Lexer<'hdr, UnknownToken<'hdr>> = lexer.morph();
436 match parse_unknown(&mut unknown_lexer) {
437 Ok(raw_end) => {
438 let raw_str = &unknown_lexer.source()[start..raw_end];
439 res.unknown_result.push(UnknownResult { raw: raw_str });
440 }
441 Err(e) => {
442 res.errors.push(e);
443 break;
444 }
445 }
446 lexer = unknown_lexer.morph();
447 }
448 _ => {
450 let cut_slice = &lexer.source()[lexer.span().start..];
451 let cut_span = &lexer.source()[lexer.span().start..lexer.span().end];
452
453 let detail = crate::error::ParsingDetail {
454 component: "parse_ptypes_properties",
455 span_start: lexer.span().start,
456 span_end: lexer.span().end,
457 source: lexer.source(),
458 clipped_span: cut_span,
459 clipped_remaining: cut_slice,
460 };
461
462 res.errors.push(AuthResultsError::ParsingDetailed(detail));
463 break;
464 }
465 }
466 }
467 res
468 }
469}