-
Notifications
You must be signed in to change notification settings - Fork 0
/
email.go
122 lines (97 loc) · 2.58 KB
/
email.go
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
122
package checker
import (
"reflect"
"regexp"
"strings"
)
// CheckerEmail is the name of the checker.
const CheckerEmail = "email"
// ResultNotEmail indicates that the given string is not a valid email.
const ResultNotEmail = "NOT_EMAIL"
// ipV6Prefix is the IPv6 prefix for the domain.
const ipV6Prefix = "[IPv6:"
// notQuotedChars is the valid not quoted characters.
var notQuotedChars = regexp.MustCompile("[a-zA-Z0-9!#$%&'*\\+\\-/=?^_`{|}~]")
// IsEmail checks if the given string is an email address.
func IsEmail(email string) Result {
atIndex := strings.LastIndex(email, "@")
if atIndex == -1 || atIndex == len(email)-1 {
return ResultNotEmail
}
domain := email[atIndex+1:]
if isValidEmailDomain(domain) != ResultValid {
return ResultNotEmail
}
return isValidEmailUser(email[:atIndex])
}
// makeEmail makes a checker function for the email checker.
func makeEmail(_ string) CheckFunc {
return checkEmail
}
// checkEmail checks if the given string is an email address.
func checkEmail(value, _ reflect.Value) Result {
if value.Kind() != reflect.String {
panic("string expected")
}
return IsEmail(value.String())
}
// isValidEmailDomain checks if the email domain is a IPv4 or IPv6 address, or a FQDN.
func isValidEmailDomain(domain string) Result {
if len(domain) > 255 {
return ResultNotEmail
}
if domain[0] == '[' {
if strings.HasPrefix(domain, ipV6Prefix) {
// postmaster@[IPv6:2001:0db8:85a3:0000:0000:8a2e:0370:7334]
return IsIPV6(domain[len(ipV6Prefix) : len(domain)-1])
}
// postmaster@[123.123.123.123]
return IsIPV4(domain[1 : len(domain)-1])
}
return IsFqdn(domain)
}
// isValidEmailUser checks if the email user is valid.
func isValidEmailUser(user string) Result {
// Cannot be empty user
if user == "" || len(user) > 64 {
return ResultNotEmail
}
// Cannot start or end with dot
if user[0] == '.' || user[len(user)-1] == '.' {
return ResultNotEmail
}
return isValidEmailUserCharacters(user)
}
// isValidEmailUserCharacters if the email user characters are valid.
func isValidEmailUserCharacters(user string) Result {
quoted := false
start := true
prev := ' '
for _, c := range user {
// Cannot have a double dot unless quoted
if !quoted && c == '.' && prev == '.' {
return ResultNotEmail
}
if start {
start = false
if c == '"' {
quoted = true
prev = c
continue
}
}
if !quoted {
if c == '.' {
start = true
} else if !notQuotedChars.MatchString(string(c)) {
return ResultNotEmail
}
} else {
if c == '"' && prev != '\\' {
quoted = false
}
}
prev = c
}
return ResultValid
}