-
Notifications
You must be signed in to change notification settings - Fork 2
/
smtp_client_config.go
196 lines (151 loc) · 3.7 KB
/
smtp_client_config.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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
package email
import (
"encoding/json"
"errors"
"fmt"
"strings"
"time"
"crypto/tls"
"net"
"log"
"os"
)
type SMTPClientMode uint8
const (
ModeUNENCRYPTED SMTPClientMode = 0
ModeSTARTTLS SMTPClientMode = 1
ModeFORCETLS SMTPClientMode = 2
)
var errInvalidMode error = errors.New("Valid SMTPClientModes are: UNENCRYPTED, STARTTLS, or FORCETLS")
func (MODE *SMTPClientMode) UnmarshalJSON(V []byte) (E error) {
var STR string
E = json.Unmarshal(V, &STR)
if E != nil {
return
}
STR = strings.ToUpper(strings.TrimSpace(STR))
switch STR {
case "UNENCRYPTED":
*MODE = ModeUNENCRYPTED
case "STARTTLS":
*MODE = ModeSTARTTLS
case "FORCETLS":
*MODE = ModeFORCETLS
default:
E = errInvalidMode
}
return
}
type SMTPClientConfig struct {
Server string
Port uint16
Username string
Password string
Mode SMTPClientMode
TimeoutMsec uint32
Proto string // dial protocol: `tcp`, `tcp4`, or `tcp6`; defaults to `tcp`
SMTPLog string // path to SMTP log: complete filepath, "-" for STDOUT, or empty to disable SMTP logging
}
/*
Example
oCfg := SMTPClientConfig{
Server: "mx.test.com",
Port: 587,
Username: "test@test.com",
Password: "...",
Mode: ModeSTARTTLS,
// SMTPLog: "-", // note: uncomment to log SMTP session to STDOUT
}
oEmail := NewEmail()
oEmail.From = "test@test.com"
oEmail.To = []string{"test_receiver@eggplant.pro"}
oEmail.Subject = "Test Message"
oEmail.Text = []byte("Whoomp there it is!")
E := oCfg.SimpleSend(oEmail)
if E != nil { return E }
*/
func (CFG SMTPClientConfig) SimpleSend(MSG ...*Email) (E error) {
iAuth := LoginAuth(CFG.Username, CFG.Password)
pTLSCfg := TLSConfig(CFG.Server)
DIAL_ADDR := fmt.Sprintf("%s:%d", CFG.Server, CFG.Port)
if len(CFG.Proto) == 0 {
CFG.Proto = "tcp"
}
// FOR DIAL/IO TIMEOUTS
TIMEOUT_DURATION := time.Millisecond * time.Duration(CFG.TimeoutMsec)
pDialer := &net.Dialer{
Timeout: TIMEOUT_DURATION,
KeepAlive: -1, // disabled
}
var iConn net.Conn
var pTLSCfgClient *tls.Config = nil
switch CFG.Mode {
case ModeSTARTTLS:
// [1]: open an unencrypted network connection
iConn, E = pDialer.Dial(CFG.Proto, DIAL_ADDR)
if E != nil {
return
}
// negotiate TLS in SMTP session
pTLSCfgClient = pTLSCfg
case ModeFORCETLS:
// [1]: open a TLS-secured network connection
iConn, E = tls.DialWithDialer(pDialer, CFG.Proto, DIAL_ADDR, pTLSCfg)
if E != nil {
return
}
case ModeUNENCRYPTED:
// [1]: open an unencrypted network connection
iConn, E = pDialer.Dial(CFG.Proto, DIAL_ADDR)
if E != nil {
return
}
default:
E = errors.New("Invalid Mode for SimpleSend()")
return
}
var fnTextprotoCreate CreateTextprotoConnFn = nil
// SMTP SESSION LOGGING
if len(CFG.SMTPLog) > 0 {
var FILE *os.File
if CFG.SMTPLog == "-" {
FILE = os.Stdout
} else {
FILE, E = os.OpenFile(CFG.SMTPLog, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0660)
if E != nil {
return
}
}
fnTextprotoCreate = TextprotoLogged(log.New(FILE, "", log.Ltime|log.Lmicroseconds), true)
}
// COMMS TIMEOUT
if TIMEOUT_DURATION > 0 {
E = iConn.SetDeadline(time.Now().Add(TIMEOUT_DURATION))
if E != nil {
return
}
}
// [2]: ESTABLISH SMTP SESSION
SESS, E := NewClient(iConn, iAuth, CFG.Server, pTLSCfgClient, fnTextprotoCreate)
if E != nil {
return
}
// [3]: SEND MESSAGE(S)
for ix := range MSG {
// COMMS TIMEOUT
if TIMEOUT_DURATION > 0 {
E = iConn.SetDeadline(time.Now().Add(TIMEOUT_DURATION))
if E != nil {
return
}
}
E = SESS.Send(MSG[ix])
if E != nil {
return
}
}
// [4]: CLOSE SMTP SESSION WHEN FINISHED SENDING
// NOTE: this also closes the underlying network connection
E = SESS.Quit()
return
}