mailrelay/client.go
2018-11-28 20:24:10 -05:00

124 lines
2.7 KiB
Go

package main
import (
"bytes"
"crypto/tls"
"fmt"
"io"
"net/smtp"
"net/textproto"
"github.com/flashmob/go-guerrilla/mail"
"github.com/pkg/errors"
)
type closeable interface {
Close() error
}
// sendMail sends the contents of the envelope to a SMTP server.
func sendMail(e *mail.Envelope, config *relayConfig) error {
server := fmt.Sprintf("%s:%d", config.SMTPServer, config.SMTPPort)
to := getTo(e)
var msg bytes.Buffer
msg.Write(e.Data.Bytes())
msg.WriteString("\r\n")
fmt.Println("==== Starting email send ====")
defer fmt.Println("==== Finished email send ====")
var err error
var conn *tls.Conn
var client *smtp.Client
var writer io.WriteCloser
tlsconfig := &tls.Config{
InsecureSkipVerify: true,
ServerName: config.SMTPServer,
}
if conn, err = tls.Dial("tcp", server, tlsconfig); err != nil {
return errors.Wrap(err, "dial error")
}
if client, err = smtp.NewClient(conn, config.SMTPServer); err != nil {
close(conn, "conn")
return errors.Wrap(err, "newclient error")
}
shouldCloseClient := true
defer func(shouldClose *bool) {
if *shouldClose {
close(client, "client")
}
}(&shouldCloseClient)
auth := smtp.PlainAuth("", config.SMTPUsername, config.SMTPPassword, config.SMTPServer)
if err = client.Auth(auth); err != nil {
return errors.Wrap(err, "auth error")
}
if err = client.Mail(e.MailFrom.String()); err != nil {
return errors.Wrap(err, "mail error")
}
for _, addy := range to {
if err = client.Rcpt(addy); err != nil {
return errors.Wrap(err, "rcpt error")
}
}
if writer, err = client.Data(); err != nil {
return errors.Wrap(err, "data error")
}
_, err = writer.Write(msg.Bytes())
close(writer, "writer")
if err != nil {
return errors.Wrap(err, "write error")
}
if err = client.Quit(); isQuitError(err) {
return errors.Wrap(err, "quit error")
}
// We only need to close client if some other error prevented us
// from getting to `client.Quit`
shouldCloseClient = false
return nil
}
func close(c closeable, what string) {
err := c.Close()
if err != nil {
fmt.Printf("!!!!! Error closing %s: %v\n", what, err)
}
}
func isQuitError(err error) bool {
if err == nil {
return false
}
e, ok := err.(*textproto.Error)
if ok {
// SMTP codes 221 or 250 are acceptable here
if e.Code == 221 || e.Code == 250 {
return false
}
}
return true
}
// getTo returns the array of email addresses in the envelope.
func getTo(e *mail.Envelope) []string {
var ret []string
for _, addy := range e.RcptTo {
ret = append(ret, addy.String())
}
return ret
}
func display(b []byte) {
s := string(b)
fmt.Println("################################")
fmt.Printf("%s\n", s)
fmt.Println("################################")
}