-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathemail.go
117 lines (91 loc) · 2.67 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
package email
import (
"golang.org/x/net/publicsuffix"
"net"
"regexp"
"strings"
)
const (
emptyString string = ""
)
var (
emailRegexp = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
)
//Validate - validates an email address via all options
func Validate(email string) error {
err := ValidateDomainRecords(email)
if err != nil {
return err
}
return nil
}
//ValidateFormat - validates an email address meets rfc 822 format via a regex
func ValidateFormat(email string) error {
_, _, err := validateFormatAndSplit(email)
if err != nil {
return err
}
return nil
}
//ValidateDomainRecords - validates an email address domain's NS and MX records via a DNS lookup
func ValidateDomainRecords(email string) error {
_, domain, err := validateFormatAndSplit(email)
if err != nil {
return err
}
effectiveTLDPlusOne, err := publicsuffix.EffectiveTLDPlusOne(domain)
if err != nil {
return ErrInvalidDomainNoNameServers
}
// Added NS check as some ISPs hijack the MX record lookup :(
if effectiveTLDPlusOne == domain {
nsRecords, err := net.LookupNS(effectiveTLDPlusOne)
if err != nil || len(nsRecords) == 0 {
return ErrInvalidDomainNoNameServers
}
} else {
nsRecords, err := net.LookupNS(effectiveTLDPlusOne)
if err != nil || len(nsRecords) == 0 {
return ErrInvalidDomainNoNameServers
}
}
mxRecords, err := net.LookupMX(domain)
if err != nil || len(mxRecords) == 0 {
ipRecords, err := net.LookupIP(domain)
if err != nil || len(ipRecords) == 0 {
return ErrInvalidDomainNoMXRecords
}
}
return nil
}
//Split - Splits an email address into a username prefix and domain
func Split(email string) (username string, domain string) {
username, domain, err := validateFormatAndSplit(email)
if err != nil {
return emptyString, emptyString
}
return username, domain
}
// Normalize - Trim whitespaces, extra dots in the hostname and converts to Lowercase.
func Normalize(email string) string {
email = strings.TrimSpace(email)
email = strings.TrimRight(email, ".")
email = strings.ToLower(email)
return email
}
func validateFormatAndSplit(email string) (username string, domain string, err error) {
if len(email) < 6 || len(email) > 254 {
return emptyString, emptyString, ErrInvalidFormat
}
// Regex matches as per rfc 822 https://tools.ietf.org/html/rfc822
if !emailRegexp.MatchString(email) {
return emptyString, emptyString, ErrInvalidFormat
}
i := strings.LastIndexByte(email, '@')
username = email[:i]
domain = email[i+1:]
if len(username) > 64 {
return emptyString, emptyString, ErrInvalidFormat
}
return username, domain, nil
}