-
Notifications
You must be signed in to change notification settings - Fork 2
/
address.go
131 lines (112 loc) · 3.37 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
package mailaddress
import (
"fmt"
"mime"
"strings"
)
// Address is a single mail address.
type Address struct {
Name string `db:"name" json:"name"`
Address string `db:"email" json:"address"`
Raw string `db:"-" json:"-"`
err error `db:"-"`
}
// String formats an address. It is *not* RFC 2047 encoded!
func (a Address) String() string {
if a.Name == "" {
return a.Address
}
return fmt.Sprintf(`"%s" <%s>`, strings.Replace(a.Name, `"`, `\"`, -1), a.Address)
}
// NameEncoded returns the name ready to be put in an email header. Special
// characters will be appropriately escaped and RFC 2047 encoding will be
// applied.
func (a Address) NameEncoded() string {
if a.Name == "" {
return ""
}
name := a.Name
if strings.ContainsAny(name, `",;@<>()`) {
name = fmt.Sprintf(`"%s"`, strings.Replace(name, `"`, `\\"`, -1))
}
return mime.QEncoding.Encode("utf-8", name)
}
// AddressEncoded returns the address ready to be put in an email header.
// Special characters will be appropriately escaped and RFC 2047 encoding will
// be applied.
func (a Address) AddressEncoded() string {
if a.Address == "" {
return ""
}
return mime.QEncoding.Encode("utf-8", a.Address)
}
// StringEncoded makes a string that *is* RFC 2047 encoded
//
// TODO: This won't work with IDN. This is okay since most email clients don't
// work with IDN. Last I checked this included Gmail, FastMail, Thunderbird,
// etc. The only client that works 100% correct AFAIK is mutt.
func (a Address) StringEncoded() string {
if a.Name == "" {
return a.Address
}
return fmt.Sprintf("%v <%v>", a.NameEncoded(), a.AddressEncoded())
}
// ToList puts this Address in an List.
func (a Address) ToList() (l List) {
l = append(l, a)
return l
}
// Local gets the local part of an address (i.e. everything before the first @).
//
// TODO: the local part can contain a quoted/escaped @, but practically no email
// system deals with that, so it's not a huge deal at the moment.
func (a Address) Local() string {
s := strings.Split(a.Address, "@")
return s[0]
}
// Domain gets the domain part of an address (i.e. everything after the first
// @).
//
// TODO: Same as Local().
func (a Address) Domain() string {
s := strings.Split(a.Address, "@")
if len(s) < 2 {
return s[0]
}
return strings.Join(s[1:], "")
}
// WithoutTag gets the address with the tag part removed (if any). The tag part
// is everything in the local part after the first +.
func (a Address) WithoutTag() string {
if !a.Valid() {
return ""
}
plus := strings.Index(a.Address, "+")
at := strings.Index(a.Address, "@")
if plus != -1 && at != -1 {
return a.Address[:plus] + a.Address[at:]
}
return a.Address
}
// Valid reports if this email looks valid. This includes some small extra
// checks for sanity. For example "martin@arp242 is a "valid" email address in
// the RFC sense, but not in the "something we can send emails to"-sense.
//
// TODO: Perhaps consider renaming to CanSend() or Sendable() or Deliverable()?
//
// It is also useful if the address wasn't created with ParseList() but directly
// (e.g. addr := Address{...}).
func (a *Address) Valid() bool {
if a.Address == "" || !reValidEmail.MatchString(a.Address) {
a.err = ErrNoEmail
return false
}
return a.err == nil
}
// Error returns any error that may have been associated with the mail address
func (a *Address) Error() error {
if a.Valid() {
return nil
}
return a.err
}