1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use regex::Regex;
use idna::domain_to_ascii;
use validation::ip::validate_ip;
lazy_static! {
static ref EMAIL_USER_RE: Regex = Regex::new(r"^(?i)[a-z0-9.!#$%&'*+/=?^_`{|}~-]+\z").unwrap();
static ref EMAIL_DOMAIN_RE: Regex = Regex::new(
r"(?i)^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$"
).unwrap();
static ref EMAIL_LITERAL_RE: Regex = Regex::new(r"(?i)\[([A-f0-9:\.]+)\]\z").unwrap();
}
pub fn validate_email(val: &str) -> bool {
if val.is_empty() || !val.contains('@') {
return false;
}
let parts: Vec<&str> = val.rsplitn(2, '@').collect();
let user_part = parts[1];
let domain_part = parts[0];
if !EMAIL_USER_RE.is_match(user_part) {
return false;
}
if !validate_domain_part(domain_part) {
return match domain_to_ascii(domain_part) {
Ok(d) => validate_domain_part(&d),
Err(_) => false,
};
}
true
}
fn validate_domain_part(domain_part: &str) -> bool {
if EMAIL_DOMAIN_RE.is_match(domain_part) {
return true;
}
match EMAIL_LITERAL_RE.captures(domain_part) {
Some(caps) => {
match caps.get(1) {
Some(c) => validate_ip(c.as_str()),
None => false,
}
}
None => false
}
}
#[cfg(test)]
mod tests {
use super::validate_email;
#[test]
fn test_validate_email() {
let tests = vec![
("email@here.com", true),
("weirder-email@here.and.there.com", true),
(r#"!def!xyz%abc@example.com"#, true),
("email@[127.0.0.1]", true),
("email@[2001:dB8::1]", true),
("email@[2001:dB8:0:0:0:0:0:1]", true),
("email@[::fffF:127.0.0.1]", true),
("example@valid-----hyphens.com", true),
("example@valid-with-hyphens.com", true),
("test@domain.with.idn.tld.उदाहरण.परीक्षा", true),
(r#""test@test"@example.com"#, false),
("a@atm.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", true),
("a@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.atm", true),
("a@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.bbbbbbbbbb.atm", true),
("a@atm.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", true),
("", false),
("abc", false),
("abc@", false),
("abc@bar", true),
("a @x.cz", false),
("something@@somewhere.com", false),
("email@127.0.0.1", true),
("email@[127.0.0.256]", false),
("email@[2001:db8::12345]", false),
("email@[2001:db8:0:0:0:0:1]", false),
("email@[::ffff:127.0.0.256]", false),
("example@invalid-.com", false),
("example@-invalid.com", false),
("example@invalid.com-", false),
("example@inv-.alid-.com", false),
("example@inv-.-alid.com", false),
(r#"test@example.com\n\n<script src="x.js">"#, false),
(r#""\\\011"@here.com"#, false),
(r#""\\\012"@here.com"#, false),
("trailingdot@shouldfail.com.", false),
("a@b.com\n", false),
("a\n@b.com", false),
(r#""test@test"\n@example.com"#, false),
("a@[127.0.0.1]\n", false),
];
for (input, expected) in tests {
println!("{} - {}", input, expected);
assert_eq!(validate_email(input), expected);
}
}
}