-
-
Notifications
You must be signed in to change notification settings - Fork 5
/
email.go
111 lines (83 loc) · 1.9 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
package emailscraper
import (
"bytes"
"regexp"
"strconv"
"strings"
"sync"
"github.com/lawzava/go-tld"
)
type emails struct {
emails []string
m sync.Mutex
}
func (s *emails) add(email string) {
if !isValidEmail(email) {
return
}
// check for already existing emails
s.m.Lock()
defer s.m.Unlock()
for _, existingEmail := range s.emails {
if existingEmail == email {
return
}
}
s.emails = append(s.emails, email)
}
// Initialize once.
var (
reg = regexp.MustCompile(`([a-zA-Z0-9._-]+@([a-zA-Z0-9_-]+\.)+[a-zA-Z0-9_-]+)`)
obfuscatedSeparators = regexp.MustCompile(`.(AT|at|ETA).`)
)
// Parse any *@*.* string and append to the slice.
func (s *emails) parseEmails(body []byte) {
res := reg.FindAll(body, -1)
for _, r := range res {
s.add(string(r))
}
body = obfuscatedSeparators.ReplaceAll(body, []byte("@"))
res = reg.FindAll(body, -1)
for _, r := range res {
s.add(string(r))
}
}
func (s *emails) parseCloudflareEmail(cloudflareEncodedEmail string) {
decodedEmail := decodeCloudflareEmail(cloudflareEncodedEmail)
email := reg.FindString(decodedEmail)
s.add(email)
}
func decodeCloudflareEmail(email string) string {
var buffer bytes.Buffer
r, _ := strconv.ParseInt(email[0:2], 16, 0)
for n := 4; n < len(email)+2; n += 2 {
i, _ := strconv.ParseInt(email[n-2:n], 16, 0)
c := i ^ r
buffer.WriteRune(rune(c))
}
return buffer.String()
}
// Check if email looks valid.
func isValidEmail(email string) bool {
if email == "" {
return false
}
split := strings.Split(email, ".")
//nolint:gomnd // allow magic number here
if len(split) < 2 {
return false
}
ending := split[len(split)-1]
//nolint:gomnd // allow magic number here
if len(ending) < 2 {
return false
}
// check if TLD name actually exists and is not some image ending
if !tld.IsValid(ending) {
return false
}
if _, err := strconv.Atoi(ending); err == nil {
return false
}
return true
}