summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeal H. Walfield <neal@pep.foundation>2019-05-28 14:02:19 +0200
committerNeal H. Walfield <neal@pep.foundation>2019-05-28 22:23:24 +0200
commit0be384253369e5b5e01be48a1ae8c11782cdc16c (patch)
treea990998a867b7add9e347cbea250ac8f5aa0a60f
parentdfa97e677900b7e147988ae4880dead9b42eafe9 (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.lalrpop32
-rw-r--r--rfc2822/src/lib.rs367
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