diff options
author | Neal H. Walfield <neal@pep.foundation> | 2019-05-28 14:02:19 +0200 |
---|---|---|
committer | Neal H. Walfield <neal@pep.foundation> | 2019-05-28 22:23:24 +0200 |
commit | 0be384253369e5b5e01be48a1ae8c11782cdc16c (patch) | |
tree | a990998a867b7add9e347cbea250ac8f5aa0a60f | |
parent | dfa97e677900b7e147988ae4880dead9b42eafe9 (diff) |
rfc2822: Add more variants that recognize invalid email addresses
- Add variants of the AngleAddr and NameAddr productions that
recognize invalid email addresses.
-rw-r--r-- | rfc2822/src/grammar.lalrpop | 32 | ||||
-rw-r--r-- | rfc2822/src/lib.rs | 367 |
2 files changed, 265 insertions, 134 deletions
diff --git a/rfc2822/src/grammar.lalrpop b/rfc2822/src/grammar.lalrpop index b29af820..0f6f6363 100644 --- a/rfc2822/src/grammar.lalrpop +++ b/rfc2822/src/grammar.lalrpop @@ -566,6 +566,20 @@ name_addr : Vec<Component> = { components_concat!(c, a), } +// An extension. See addr-spec-or-other for details. + +// name-addr-or-other = [display-name] angle-addr-or-other +pub(crate) NameAddrOrOther : Vec<Component> = { + <n:name_addr_or_other> => components_merge(n), +} + +name_addr_or_other : Vec<Component> = { + <n:display_name?> <a:angle_addr_or_other_prime> => + components_concat!(n, a), + + <c:CFWS> <a:angle_addr_or_other_prime> => + components_concat!(c, a), +} // angle-addr = [CFWS] "<" addr-spec ">" [CFWS] / obs-angle-addr pub(crate) AngleAddr : Vec<Component> = { @@ -583,6 +597,24 @@ angle_addr_prime : Vec<Component> = { } +// An extension. See addr-spec-or-other for details. + +// angle-addr-or-other = [CFWS] "<" addr-spec-or-other ">" [CFWS] +pub(crate) AngleAddrOrOther : Vec<Component> = { + <a:angle_addr_or_other> => components_merge(a), +} + +angle_addr_or_other : Vec<Component> = { + <c1:CFWS?> LANGLE <a:addr_spec_or_other> RANGLE <c2:CFWS?> => + components_concat!(c1, a, c2), +} + +angle_addr_or_other_prime : Vec<Component> = { + LANGLE <a:addr_spec_or_other> RANGLE <c2:CFWS?> => + components_concat!(a, c2), +} + + // display-name = phrase pub(crate) DisplayName : Vec<Component> = { <d:display_name> => components_merge(d), diff --git a/rfc2822/src/lib.rs b/rfc2822/src/lib.rs index f207d1fe..e9d2c3a4 100644 --- a/rfc2822/src/lib.rs +++ b/rfc2822/src/lib.rs @@ -837,147 +837,246 @@ mod tests { // addr-spec-or-other = local-part "@" domain // | anything #[test] - fn addr_spec_or_other_parser() { + fn or_other_parsers() { fn e() -> ParseError<usize, String, LexicalError> { ParseError::User { error: LexicalError::NoError } } - c!(grammar::AddrSpecOrOtherParser::new(), Vec<Component>); - - // Normal email addresses. - c("foo@bar.com", Some(vec![Component::Address("foo@bar.com".into())])); - c("foo@bar", Some(vec![Component::Address("foo@bar".into())])); - c("foo.bar@x", Some(vec![Component::Address("foo.bar@x".into())])); - c("foo.bar@ß", Some(vec![Component::Address("foo.bar@ß".into())])); - - // Some invalid email addresses... - - // [ is not a valid localpart. - c("[@x", - Some(vec![ - Component::InvalidAddress(e(), "[@x".into()) - ])); - c("[ß@x", - Some(vec![ - Component::InvalidAddress(e(), "[ß@x".into()) - ])); - c("[@xß", - Some(vec![ - Component::InvalidAddress(e(), "[@xß".into()) - ])); - c("[@xßℝ", - Some(vec![ - Component::InvalidAddress(e(), "[@xßℝ".into()) - ])); - c("foo[@x", - Some(vec![ - Component::InvalidAddress(e(), "foo[@x".into()) - ])); - - // What happens with comments? - c("(c)[@x", - Some(vec![ - Component::InvalidAddress(e(), "(c)[@x".into()) - ])); - c("[(c)@x", - Some(vec![ - Component::InvalidAddress(e(), "[(c)@x".into()) - ])); - c("[@(c)x", - Some(vec![ - Component::InvalidAddress(e(), "[@(c)x".into()) - ])); - - c("foo(c)[@x", - Some(vec![ - Component::InvalidAddress(e(), "foo(c)[@x".into()) - ])); - c("foo[(c)@x", - Some(vec![ - Component::InvalidAddress(e(), "foo[(c)@x".into()) - ])); - c("foo[@(c)x", - Some(vec![ - Component::InvalidAddress(e(), "foo[@(c)x".into()) - ])); - - c("[@x (c)", - Some(vec![ - Component::InvalidAddress(e(), "[@x (c)".into()) - ])); - - c("(c) foo[@x", - Some(vec![ - Component::InvalidAddress(e(), "(c) foo[@x".into()) - ])); - c("foo[ (c)@x", - Some(vec![ - Component::InvalidAddress(e(), "foo[ (c)@x".into()) - ])); - - // @ is not a valid domain part. - c("foo.bar@@dings", - Some(vec![ - Component::InvalidAddress(e(), "foo.bar@@dings".into()) - ])); - c("foo.bar@x@dings", - Some(vec![ - Component::InvalidAddress(e(), "foo.bar@x@dings".into()) - ])); - - // Again, what happens with comments? - c("foo.bar (1)@@(2)dings", - Some(vec![ - Component::InvalidAddress(e(), "foo.bar (1)@@(2)dings".into()) - ])); - c("foo.bar (1) @@ (2)dings", - Some(vec![ - Component::InvalidAddress(e(), "foo.bar (1) @@ (2)dings".into()) - ])); - - c("foo.bar(1)@x@dings", - Some(vec![ - Component::InvalidAddress(e(), "foo.bar(1)@x@dings".into()) - ])); - c("foo.bar@(1)x@dings", - Some(vec![ - Component::InvalidAddress(e(), "foo.bar@(1)x@dings".into()) - ])); - c("foo.bar@x(1)@dings", - Some(vec![ - Component::InvalidAddress(e(), "foo.bar@x(1)@dings".into()) - ])); - c("foo.bar@x@(1)dings", - Some(vec![ - Component::InvalidAddress(e(), "foo.bar@x@(1)dings".into()) - ])); - c("foo.bar@x@ (1) dings", - Some(vec![ - Component::InvalidAddress(e(), "foo.bar@x@ (1) dings".into()) - ])); - - - - // Try some URIs for completeness. - c("ssh://user:pasword@example.org/resource", - Some(vec![ - Component::InvalidAddress(e(), "ssh://user:pasword@example.org/resource".into()) - ])); + struct Test<'a> { + input: &'a str, + output: Option<Vec<Component>>, + }; - c("(not a comment) ssh://user:pasword@example.org/resource", - Some(vec![ - Component::InvalidAddress(e(), "(not a comment) ssh://user:pasword@example.org/resource".into()) - ])); + let tests : &[Test] = &[ + // First, some normal, valid email addresses. + Test { + input: "foo@bar.com", + output: Some(vec![Component::Address("foo@bar.com".into())]) + }, + Test { + input: "foo@bar", + output: Some(vec![Component::Address("foo@bar".into())]) + }, + Test { + input: "foo.bar@x", + output: Some(vec![Component::Address("foo.bar@x".into())]) + }, + // Last character is a multibyte character. + Test { + input: "foo.bar@ß", + output: Some(vec![Component::Address("foo.bar@ß".into())]) + }, + + // Then some invalid email addresses... + + // [ is not a valid localpart. + Test { + input: "[@x", + output: Some(vec![ + Component::InvalidAddress(e(), "[@x".into()) + ]) + }, + Test { + input: "[ß@x", + output: Some(vec![ + Component::InvalidAddress(e(), "[ß@x".into()) + ]) + }, + // Last character is a multibyte character. + Test { + input: "[@xß", + output: Some(vec![ + Component::InvalidAddress(e(), "[@xß".into()) + ]) + }, + Test { + input: "[@xßℝ", + output: Some(vec![ + Component::InvalidAddress(e(), "[@xßℝ".into()) + ]) + }, + Test { + input: "foo[@x", + output: Some(vec![ + Component::InvalidAddress(e(), "foo[@x".into()) + ]) + }, + + // What happens with comments? + Test { + input: "(c)[@x", + output: Some(vec![ + Component::InvalidAddress(e(), "(c)[@x".into()) + ]) + }, + Test { + input: "[(c)@x", + output: Some(vec![ + Component::InvalidAddress(e(), "[(c)@x".into()) + ]) + }, + Test { + input: "[@(c)x", + output: Some(vec![ + Component::InvalidAddress(e(), "[@(c)x".into()) + ]) + }, + + Test { + input: "foo(c)[@x", + output: Some(vec![ + Component::InvalidAddress(e(), "foo(c)[@x".into()) + ]) + }, + Test { + input: "foo[(c)@x", + output: Some(vec![ + Component::InvalidAddress(e(), "foo[(c)@x".into()) + ]) + }, + Test { + input: "foo[@(c)x", + output: Some(vec![ + Component::InvalidAddress(e(), "foo[@(c)x".into()) + ]) + }, + + Test { + input: "[@x (c)", + output: Some(vec![ + Component::InvalidAddress(e(), "[@x (c)".into()) + ]) + }, + + Test { + input: "(c) foo[@x", + output: Some(vec![ + Component::InvalidAddress(e(), "(c) foo[@x".into()) + ]) + }, + Test { + input: "foo[ (c)@x", + output: Some(vec![ + Component::InvalidAddress(e(), "foo[ (c)@x".into()) + ]) + }, + + // @ is not a valid domain part. + Test { + input: "foo.bar@@dings", + output: Some(vec![ + Component::InvalidAddress(e(), "foo.bar@@dings".into()) + ]) + }, + Test { + input: "foo.bar@x@dings", + output: Some(vec![ + Component::InvalidAddress(e(), "foo.bar@x@dings".into()) + ]) + }, + + // Again, what happens with comments? + Test { + input: "foo.bar (1)@@(2)dings", + output: Some(vec![ + Component::InvalidAddress(e(), "foo.bar (1)@@(2)dings".into()) + ]) + }, + Test { + input: "foo.bar (1) @@ (2)dings", + output: Some(vec![ + Component::InvalidAddress(e(), "foo.bar (1) @@ (2)dings".into()) + ]) + }, + + Test { + input: "foo.bar(1)@x@dings", + output: Some(vec![ + Component::InvalidAddress(e(), "foo.bar(1)@x@dings".into()) + ]) + }, + Test { + input: "foo.bar@(1)x@dings", + output: Some(vec![ + Component::InvalidAddress(e(), "foo.bar@(1)x@dings".into()) + ]) + }, + Test { + input: "foo.bar@x(1)@dings", + output: Some(vec![ + Component::InvalidAddress(e(), "foo.bar@x(1)@dings".into()) + ]) + }, + Test { + input: "foo.bar@x@(1)dings", + output: Some(vec![ + Component::InvalidAddress(e(), "foo.bar@x@(1)dings".into()) + ]) + }, + Test { + input: "foo.bar@x@ (1) dings", + output: Some(vec![ + Component::InvalidAddress(e(), "foo.bar@x@ (1) dings".into()) + ]) + }, + + + + // Try some URIs for completeness. + Test { + input: "ssh://user:pasword@example.org/resource", + output: Some(vec![ + Component::InvalidAddress( + e(), "ssh://user:pasword@example.org/resource".into()) + ]) + }, + + Test { + input: "(not a comment) ssh://user:pasword@example.org/resource", + output: Some(vec![ + Component::InvalidAddress( + e(), "(not a comment) ssh://user:pasword@example.org/resource".into()) + ]) + }, + + Test { + input: "shark://grrrr/39874293847092837443987492834", + output: Some(vec![ + Component::InvalidAddress( + e(), "shark://grrrr/39874293847092837443987492834".into()) + ]) + }, + + Test { + input: "shark://bait/8uyoi3lu4hl2..dfoif983j4b@%", + output: Some(vec![ + Component::InvalidAddress( + e(), "shark://bait/8uyoi3lu4hl2..dfoif983j4b@%".into()) + ]) + }, + ][..]; + + for t in tests.iter() { + { + c!(grammar::AddrSpecOrOtherParser::new(), Vec<Component>); + c(t.input.to_string(), t.output.clone()) + } - c("shark://grrrr/39874293847092837443987492834", - Some(vec![ - Component::InvalidAddress(e(), "shark://grrrr/39874293847092837443987492834".into()) - ])); + { + c!(grammar::AngleAddrOrOtherParser::new(), Vec<Component>); + c(format!("<{}>", t.input), t.output.clone()) + } - c("shark://bait/8uyoi3lu4hl2..dfoif983j4b@%", - Some(vec![ - Component::InvalidAddress(e(), "shark://bait/8uyoi3lu4hl2..dfoif983j4b@%".into()) - ])); + { + c!(grammar::NameAddrOrOtherParser::new(), Vec<Component>); + c(format!("Foo Bar <{}>", t.input), + t.output.clone().map(|mut x| { + x.insert(0, Component::WS); + x.insert(0, Component::Text("Foo Bar".into())); + x + })) + } + } } // angle-addr = [CFWS] "<" addr-spec ">" [CFWS] / obs-angle-addr |