/
email.cr
45 lines (36 loc) · 1.47 KB
/
email.cr
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
module Marten
module Core
module Validator
# Email address validator.
module Email
extend self
# Returns `true` of the passed string corresponds to a valid email address.
def self.valid?(value : String) : Bool
return false if value.empty? || !value.includes?('@')
user_part, _, domain_part = value.rpartition('@')
return false unless user_part.match(ADDRESS_USER_PART_RE)
return true if domain_part == LOCALHOST
return true if valid_domain?(domain_part) || valid_domain?(URI::Punycode.to_ascii(domain_part))
false
end
private def valid_domain?(domain_part)
return true if domain_part.match(ADDRESS_DOMAIN_PART_RE)
# Try to extract a possible IPv4 or IPv6 address from the domain part.
if !(matched = domain_part.match(ADDRESS_LITERAL_PART_RE)).nil?
return Socket::IPAddress.valid?(matched[1])
end
false
end
private ADDRESS_DOMAIN_PART_RE = /^((?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+)(?:[A-Z0-9-]{2,63}(?<!-))\z/i
private ADDRESS_LITERAL_PART_RE = /\[([A-F0-9:.]+)\]\Z/i
private ADDRESS_USER_PART_RE = /
(
^[-!#$%&'*+\/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+\/=?^_`{}|~0-9A-Z]+)*\z
|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"\z
)
/xi
private LOCALHOST = "localhost"
end
end
end
end