-
Notifications
You must be signed in to change notification settings - Fork 0
/
address.go
140 lines (129 loc) · 3.17 KB
/
address.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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package email
import (
"errors"
)
// Address represents a human-friendly email address: a name plus the actual address.
type Address struct {
Name string
Addr string
}
// NewAddress creates a new Address enforcing a very basic validity check - see `SeemsValidAddr`.
func NewAddress(name, addr string) (*Address, error) {
if !SeemsValidAddr(addr) {
return nil, errors.New("invalid address: " + addr)
}
return &Address{name, addr}, nil
}
// SeemsValidAddr does a very loose check on addr, to weed out obviously invalid addresses.
// This function only checks that addr contains one and only one '@', followed by a domain name
// that has a TLD part.
func SeemsValidAddr(addr string) bool {
var seenAt, seenDom, seenDot, seenTld bool
for _, char := range addr {
switch char {
case '@':
if seenAt { // more than one '@'
return false
}
seenAt = true
case '.':
seenDot = seenAt && seenDom // only care about '.' after '@' and domain name
default:
if '!' > char || char > '~' {
return false
}
if seenAt {
// https://tools.ietf.org/html/rfc5322#section-3.4.1
if char == '[' || char == ']' || char == '\\' {
return false
}
seenDom = !seenDot
seenTld = seenDot
}
}
}
return seenTld
}
// Clone creates a new Address with the same contents as the receiver.
func (a *Address) Clone() *Address {
if a == nil {
return nil
}
return &Address{a.Name, a.Addr}
}
// Domain extracts the domain portion of the email address in the receiver.
func (a *Address) Domain() string {
for i := len(a.Addr) - 1; i > -1; i-- {
if a.Addr[i] == '@' {
return a.Addr[i+1:]
}
}
return ""
}
func (a *Address) encode(offset int) (dst []byte, pos int) {
la := len(a.Addr)
if ln := len(a.Name); ln > 0 {
nq, safe := 0, true
for i := 0; i < ln && safe; i++ {
c := a.Name[i]
safe = ' ' <= c && c <= '~'
if c == '\\' || c == '"' {
nq++
}
}
if safe {
dst = make([]byte, 0, ln+nq+la+7) // 2*'"'+(' ' or "\r\n ")+'<'+'>'
dst = append(dst, '"')
for i := 0; i < ln; i++ {
c := a.Name[i]
if c == '\\' || c == '"' {
dst = append(dst, '\\')
}
dst = append(dst, c)
}
offset += ln + nq + 3 // 2*'"'+' '
if offset+la <= 74 { // max 76; need room for '<' and '>'
dst = append(dst, '"', ' ')
} else {
dst = append(dst, '"', '\r', '\n', ' ')
offset = 1
}
} else {
var buf []byte
buf, offset = QEncode([]byte(a.Name), offset)
dst = make([]byte, len(buf), len(buf)+la+5) // (' ' or "\r\n ")+'<'+'>'
copy(dst, buf)
offset++
if offset+la <= 74 { // max 76; need room for '<' and '>'
dst = append(dst, ' ')
} else {
dst = append(dst, '\r', '\n', ' ')
offset = 1
}
}
} else {
if offset+la <= 74 { // max 76; need room for '<' and '>'
dst = make([]byte, 0, la+2)
} else {
dst = make([]byte, 0, la+5)
dst = append(dst, '\r', '\n', ' ')
offset = 1
}
}
dst = append(dst, '<')
dst = append(dst, []byte(a.Addr)...)
dst = append(dst, '>')
offset += la + 2
return dst, offset
}
type addrList []*Address
func (al addrList) Clone() addrList {
if len(al) == 0 {
return nil
}
cl := make(addrList, len(al))
for i, a := range al {
cl[i] = a.Clone()
}
return cl
}