-
Notifications
You must be signed in to change notification settings - Fork 0
/
sender.go
153 lines (128 loc) · 3.37 KB
/
sender.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
141
142
143
144
145
146
147
148
149
150
151
152
153
package MIMEMail
import (
"crypto/tls"
"fmt"
"io"
"net/smtp"
)
// various smtp ports as a shorthand
const (
SMTP = "25"
SMTPTLS = "465"
SMTPStartTLS = "587"
)
// Client establishes a connection and handles sending the MIME Message(s).
type Client struct {
*smtp.Client
cnf *Account
tls bool
}
// TLSClient establishes a TLSConnection to the Server described by config.
func TLSClient(cnf *Account) (*Client, error) {
var config *tls.Config
if cnf.Server.Config == nil {
config = &tls.Config{ServerName: cnf.Server.Host}
}
tlsCon, err := tls.Dial("tcp", cnf.Server.Addr(), config)
if err != nil {
return nil, err
}
c, err := smtp.NewClient(tlsCon, cnf.Server.Addr())
if err != nil {
return nil, err
}
return &Client{Client: c, cnf: cnf, tls: true}, nil
}
// PlainClient uses the standard smtp.Dail, so an unencrypted connection will
// be used. It will check wether STARTTLS is supported by the server
// and use it, if available.
func PlainClient(cnf *Account) (*Client, error) {
c, err := smtp.Dial(cnf.Server.Addr())
if err != nil {
return nil, err
}
return &Client{Client: c, cnf: cnf}, nil
}
func (c Client) prolog() error {
if err := c.Hello("localhost"); err != nil {
c.Quit()
return err
}
if !c.tls {
if ok, _ := c.Extension("STARTTLS"); ok {
var config *tls.Config
if c.cnf.Server.Config == nil {
config = &tls.Config{ServerName: c.cnf.Server.Host}
}
if err := c.StartTLS(config); err != nil {
return err
}
}
}
ok, extra := c.Extension("AUTH")
if !ok {
c.Quit()
return fmt.Errorf("no auth: %t %s", ok, extra)
}
if err := c.Auth(c.cnf.Auth()); err != nil {
c.Quit()
return err
}
return nil
}
// W is the equivalent of Writer, but returns a WriteCloser that you can write
// your message to. Remember to close the writer when you are done writing to it.
func (c Client) W(from string, to []string) (io.WriteCloser, error) {
if err := c.prolog(); err != nil {
return nil, err
}
if err := c.Mail(from); err != nil {
return nil, err
}
for _, addr := range to {
if err := c.Rcpt(addr); err != nil {
return nil, err
}
}
return c.Data()
}
// Write sends the message with the given from / to email addresses.
func (c Client) Write(from string, to []string, msg []byte) error {
w, err := c.W(from, to)
if err != nil {
return err
}
defer w.Close()
if _, err := w.Write(msg); err != nil {
return err
}
return nil
}
// Send sends the given Mail.
// If you have the "Sender" field set, it's first entry is used and
// should match the Address in auth, else the first "From" entry
// is used (with the same restrictions). If both are nil,
// a NoSender error is returned. To Send encrypted mails,
// use the (Client.Write / Mail.Encrypt) or (Client.W / Mail.WriteEncrypted)
// pairs.
func (c Client) Send(m *Mail) error {
efSender, err := m.EffectiveSender()
if err != nil {
return err
}
recp := m.Recipients()
b, err := m.Bytes()
if err != nil {
return err
}
return c.Write(efSender, recp, b)
}
// SendEncrypted sends the given mail to recipient, encrypting it with recipient's Key (which therefore cannot be nil)
// and signing it with sender's Key (which may also not be nil).
func (c Client) SendEncrypted(m *Mail, recipient, sender *Account) error {
enc, err := m.Encrypt(recipient, sender)
if err != nil {
return err
}
return c.Write(sender.Address, []string{recipient.Address}, enc)
}