6
6
"io"
7
7
"log"
8
8
"math/rand"
9
+ "net"
9
10
"net/smtp"
10
11
"net/url"
11
12
@@ -29,11 +30,26 @@ const (
29
30
// Initialize loads ServiceConfig from configURL and sets logger for this Service
30
31
func (service * Service ) Initialize (configURL * url.URL , logger * log.Logger ) error {
31
32
service .Logger .SetLogger (logger )
32
- service .config = & Config {}
33
+ service .config = & Config {
34
+ Port : 25 ,
35
+ ToAddresses : nil ,
36
+ Subject : "" ,
37
+ Auth : authTypes .Unknown ,
38
+ UseStartTLS : true ,
39
+ UseHTML : false ,
40
+ }
33
41
if err := service .config .SetURL (configURL ); err != nil {
34
42
return err
35
43
}
36
44
45
+ if service .config .Auth == authTypes .Unknown {
46
+ if service .config .Username != "" {
47
+ service .config .Auth = authTypes .Plain
48
+ } else {
49
+ service .config .Auth = authTypes .None
50
+ }
51
+ }
52
+
37
53
return nil
38
54
}
39
55
@@ -42,10 +58,28 @@ func (service *Service) Send(message string, params *map[string]string) error {
42
58
if params == nil {
43
59
params = & map [string ]string {}
44
60
}
45
- return service .doSend (message , * params )
61
+ client , err := getClientConnection (service .config .Host , service .config .Port )
62
+ if err != nil {
63
+ return fail (FailGetSMTPClient , err )
64
+ }
65
+ return service .doSend (client , message , * params )
46
66
}
47
67
48
- func (service * Service ) doSend (message string , params map [string ]string ) error {
68
+ func getClientConnection (host string , port uint16 ) (* smtp.Client , error ) {
69
+ conn , err := net .Dial ("tcp" , fmt .Sprintf ("%s:%d" , host , port ))
70
+ if err != nil {
71
+ return nil , fail (FailConnectToServer , err )
72
+ }
73
+
74
+ client , err := smtp .NewClient (conn , host )
75
+ if err != nil {
76
+ return nil , fail (FailCreateSMTPClient , err )
77
+ }
78
+
79
+ return client , nil
80
+ }
81
+
82
+ func (service * Service ) doSend (client * smtp.Client , message string , params map [string ]string ) failure {
49
83
config := service .config
50
84
51
85
params ["message" ] = message
@@ -54,47 +88,42 @@ func (service *Service) doSend(message string, params map[string]string) error {
54
88
service .multipartBoundry = fmt .Sprintf ("%x" , rand .Int63 ())
55
89
}
56
90
57
- client , err := smtp .Dial (fmt .Sprintf ("%s:%d" , config .Host , config .Port ))
58
- if err != nil {
59
- return fmt .Errorf ("error connecting to server: %s" , err )
60
- }
61
-
62
91
if config .UseStartTLS {
63
92
if err := client .StartTLS (& tls.Config {
64
93
ServerName : config .Host ,
65
94
}); err != nil {
66
- return fmt . Errorf ( "error enabling StartTLS message: %s" , err )
95
+ return fail ( FailEnableStartTLS , err )
67
96
}
68
97
}
69
98
70
99
if auth , err := service .getAuth (); err != nil {
71
100
return err
72
101
} else if auth != nil {
73
102
if err := client .Auth (auth ); err != nil {
74
- return fmt . Errorf ( "error authenticating: %s" , err )
103
+ return fail ( FailAuthenticating , err )
75
104
}
76
105
}
77
106
78
107
for _ , toAddress := range config .ToAddresses {
79
108
80
109
err := service .sendToRecipient (client , toAddress , & params )
81
110
if err != nil {
82
- return fmt . Errorf ( "error sending message to recipient: %s" , err )
111
+ return fail ( FailSendRecipient , err )
83
112
}
84
113
85
114
service .Logf ("Mail successfully sent to \" %s\" !\n " , toAddress )
86
115
}
87
116
88
117
// Send the QUIT command and close the connection.
89
- err = client .Quit ()
118
+ err : = client .Quit ()
90
119
if err != nil {
91
- return fmt . Errorf ( "error closing session: %s" , err )
120
+ return fail ( FailClosingSession , err )
92
121
}
93
122
94
123
return nil
95
124
}
96
125
97
- func (service * Service ) getAuth () (smtp.Auth , error ) {
126
+ func (service * Service ) getAuth () (smtp.Auth , failure ) {
98
127
99
128
config := service .config
100
129
@@ -106,26 +135,26 @@ func (service *Service) getAuth() (smtp.Auth, error) {
106
135
case authTypes .CRAMMD5 :
107
136
return smtp .CRAMMD5Auth (config .Username , config .Password ), nil
108
137
default :
109
- return nil , fmt . Errorf ( "invalid authorization method '%s'" , config .Auth .String ())
138
+ return nil , fail ( FailAuthType , nil , config .Auth .String ())
110
139
}
111
140
112
141
}
113
142
114
- func (service * Service ) sendToRecipient (client * smtp.Client , toAddress string , params * map [string ]string ) error {
143
+ func (service * Service ) sendToRecipient (client * smtp.Client , toAddress string , params * map [string ]string ) failure {
115
144
conf := service .config
116
145
117
146
// Set the sender and recipient first
118
147
if err := client .Mail (conf .FromAddress ); err != nil {
119
- return fmt . Errorf ( "error creating new message: %s" , err )
148
+ return fail ( FailSetSender , err )
120
149
}
121
150
if err := client .Rcpt (toAddress ); err != nil {
122
- return fmt . Errorf ( "error setting RCPT: %s" , err )
151
+ return fail ( FailSetRecipient , err )
123
152
}
124
153
125
154
// Send the email body.
126
155
wc , err := client .Data ()
127
156
if err != nil {
128
- return fmt . Errorf ( "error creating message stream: %s" , err )
157
+ return fail ( FailOpenDataStream , err )
129
158
}
130
159
131
160
// TODO: Move param override to shared service API
@@ -134,22 +163,23 @@ func (service *Service) sendToRecipient(client *smtp.Client, toAddress string, p
134
163
subject = conf .Subject
135
164
}
136
165
137
- if err := writeHeaders (& wc , service .getHeaders (toAddress , subject )); err != nil {
138
- return fmt . Errorf ( "error writing message headers: %s" , err )
166
+ if err := writeHeaders (wc , service .getHeaders (toAddress , subject )); err != nil {
167
+ return fail ( FailWriteHeaders , err )
139
168
}
140
169
170
+ var ferr failure
141
171
if conf .UseHTML {
142
- err = service .writeMultipartMessage (& wc , params )
172
+ ferr = service .writeMultipartMessage (wc , params )
143
173
} else {
144
- err = writePlainMessage ( & wc , ( * params )[ "message" ] )
174
+ ferr = service . writeMessagePart ( wc , params , "plain" )
145
175
}
146
176
147
- if err != nil {
148
- return err
177
+ if ferr != nil {
178
+ return ferr
149
179
}
150
180
151
181
if err = wc .Close (); err != nil {
152
- return fmt . Errorf ( "error closing message stream: %s" , err )
182
+ return fail ( FailCloseDataStream , err )
153
183
}
154
184
155
185
return nil
@@ -174,72 +204,69 @@ func (service *Service) getHeaders(toAddress string, subject string) map[string]
174
204
}
175
205
}
176
206
177
- func (service * Service ) writeMultipartMessage (wc * io.WriteCloser , params * map [string ]string ) error {
178
-
179
- message := (* params )["message" ]
207
+ func (service * Service ) writeMultipartMessage (wc io.WriteCloser , params * map [string ]string ) failure {
180
208
181
209
if err := writeMultipartHeader (wc , service .multipartBoundry , contentPlain ); err != nil {
182
- return fmt . Errorf ( "error writing message: %s" , err )
210
+ return fail ( FailPlainHeader , err )
183
211
}
184
-
185
- if err := writePlainMessage (wc , message ); err != nil {
212
+ if err := service .writeMessagePart (wc , params , "plain" ); err != nil {
186
213
return err
187
214
}
188
215
189
216
if err := writeMultipartHeader (wc , service .multipartBoundry , contentHTML ); err != nil {
190
- return fmt . Errorf ( "error writing message: %s" , err )
217
+ return fail ( FailHTMLHeader , err )
191
218
}
192
-
193
- if tpl , found := service .GetTemplate ("message" , ); found {
194
- if err := tpl .Execute (* wc , params ); err != nil {
195
- return fmt .Errorf ("error applying message template: %s" , err )
196
- }
197
- } else {
198
- if _ , err := fmt .Fprintf (* wc , message ); err != nil {
199
- return fmt .Errorf ("error writing message: %s" , err )
200
- }
219
+ if err := service .writeMessagePart (wc , params , "HTML" ); err != nil {
220
+ return err
201
221
}
202
222
203
223
if err := writeMultipartHeader (wc , service .multipartBoundry , "" ); err != nil {
204
- return fmt .Errorf ("error writing message: %s" , err )
224
+ return fail (FailMultiEndHeader , err )
225
+
205
226
}
206
227
207
228
return nil
208
229
}
209
230
210
- func writePlainMessage (wc * io.WriteCloser , message string ) error {
211
- if _ , err := fmt .Fprintf (* wc , message ); err != nil {
212
- return fmt .Errorf ("error writing message: %s" , err )
231
+ func (service * Service ) writeMessagePart (wc io.WriteCloser , params * map [string ]string , template string ) failure {
232
+ if tpl , found := service .GetTemplate (template ); found {
233
+ if err := tpl .Execute (wc , params ); err != nil {
234
+ return fail (FailMessageTemplate , err )
235
+ }
236
+ } else {
237
+ if _ , err := fmt .Fprintf (wc , (* params )["message" ]); err != nil {
238
+ return fail (FailMessageRaw , err )
239
+ }
213
240
}
214
241
return nil
215
242
}
216
243
217
- func writeMultipartHeader (wc * io.WriteCloser , boundry string , contentType string ) error {
244
+ func writeMultipartHeader (wc io.WriteCloser , boundry string , contentType string ) error {
218
245
suffix := "\n "
219
246
if len (contentType ) < 1 {
220
247
suffix = "--"
221
248
}
222
249
223
- if _ , err := fmt .Fprintf (* wc , "\n \n --%s%s" , boundry , suffix ); err != nil {
250
+ if _ , err := fmt .Fprintf (wc , "\n \n --%s%s" , boundry , suffix ); err != nil {
224
251
return err
225
252
}
226
253
227
254
if len (contentType ) > 0 {
228
- if _ , err := fmt .Fprintf (* wc , "Content-Type: %s\n \n " , contentType ); err != nil {
255
+ if _ , err := fmt .Fprintf (wc , "Content-Type: %s\n \n " , contentType ); err != nil {
229
256
return err
230
257
}
231
258
}
232
259
233
260
return nil
234
261
}
235
262
236
- func writeHeaders (wc * io.WriteCloser , headers map [string ]string ) error {
263
+ func writeHeaders (wc io.WriteCloser , headers map [string ]string ) error {
237
264
for key , val := range headers {
238
- if _ , err := fmt .Fprintf (* wc , "%s: %s\n " , key , val ); err != nil {
265
+ if _ , err := fmt .Fprintf (wc , "%s: %s\n " , key , val ); err != nil {
239
266
return err
240
267
}
241
268
}
242
269
243
- _ , err := fmt .Fprintln (* wc )
270
+ _ , err := fmt .Fprintln (wc )
244
271
return err
245
272
}
0 commit comments