2019-02-11 09:54:07 +00:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information
|
|
|
|
|
2019-03-02 15:22:20 +00:00
|
|
|
package post
|
2019-02-11 09:54:07 +00:00
|
|
|
|
|
|
|
import (
|
2019-06-04 12:55:38 +01:00
|
|
|
"context"
|
2019-02-11 09:54:07 +00:00
|
|
|
"crypto/tls"
|
2019-07-01 18:43:10 +01:00
|
|
|
"io"
|
2019-02-11 09:54:07 +00:00
|
|
|
"net"
|
|
|
|
"net/mail"
|
|
|
|
"net/smtp"
|
|
|
|
|
|
|
|
"github.com/zeebo/errs"
|
2019-06-04 12:55:38 +01:00
|
|
|
monkit "gopkg.in/spacemonkeygo/monkit.v2"
|
2019-02-11 09:54:07 +00:00
|
|
|
)
|
|
|
|
|
2019-03-02 15:22:20 +00:00
|
|
|
// Address is alias of net/mail.Address
|
|
|
|
type Address = mail.Address
|
|
|
|
|
2019-06-04 12:55:38 +01:00
|
|
|
var mon = monkit.Package()
|
|
|
|
|
2019-02-11 09:54:07 +00:00
|
|
|
// SMTPSender is smtp sender
|
|
|
|
type SMTPSender struct {
|
|
|
|
ServerAddress string
|
|
|
|
|
2019-03-02 15:22:20 +00:00
|
|
|
From Address
|
2019-02-11 09:54:07 +00:00
|
|
|
Auth smtp.Auth
|
|
|
|
}
|
|
|
|
|
2019-03-02 15:22:20 +00:00
|
|
|
// FromAddress implements satellite/mail.SMTPSender
|
|
|
|
func (sender *SMTPSender) FromAddress() Address {
|
|
|
|
return sender.From
|
|
|
|
}
|
|
|
|
|
2019-02-11 09:54:07 +00:00
|
|
|
// SendEmail sends email message to the given recipient
|
2019-06-04 12:55:38 +01:00
|
|
|
func (sender *SMTPSender) SendEmail(ctx context.Context, msg *Message) (err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
2019-02-11 09:54:07 +00:00
|
|
|
|
|
|
|
client, err := smtp.Dial(sender.ServerAddress)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-07-01 18:43:10 +01:00
|
|
|
|
|
|
|
if err = sender.communicate(ctx, client, msg); err != nil {
|
|
|
|
return errs.Combine(err, client.Close())
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// communicate sends mail via SMTP using provided client and message
|
|
|
|
func (sender *SMTPSender) communicate(ctx context.Context, client *smtp.Client, msg *Message) error {
|
|
|
|
// suppress error because address should be validated
|
|
|
|
// before creating SMTPSender
|
|
|
|
host, _, _ := net.SplitHostPort(sender.ServerAddress)
|
2019-02-11 09:54:07 +00:00
|
|
|
|
|
|
|
// send smtp hello or ehlo msg and establish connection over tls
|
2019-07-01 18:43:10 +01:00
|
|
|
err := client.StartTLS(&tls.Config{ServerName: host})
|
2019-02-11 09:54:07 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = client.Auth(sender.Auth)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = client.Mail(sender.From.Address)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// add recipients
|
|
|
|
for _, to := range msg.To {
|
|
|
|
err = client.Rcpt(to.Address)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-01 18:43:10 +01:00
|
|
|
mess, err := msg.Bytes()
|
2019-02-11 09:54:07 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-07-01 18:43:10 +01:00
|
|
|
data, err := client.Data()
|
2019-05-13 16:14:45 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-07-01 18:43:10 +01:00
|
|
|
err = writeData(data, mess)
|
2019-02-11 09:54:07 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-07-01 18:43:10 +01:00
|
|
|
// send quit msg to stop gracefully
|
|
|
|
return client.Quit()
|
|
|
|
}
|
|
|
|
|
|
|
|
// writeData ensures that writer will be closed after data is written
|
|
|
|
func writeData(writer io.WriteCloser, data []byte) (err error) {
|
|
|
|
defer func() {
|
|
|
|
err = errs.Combine(err, writer.Close())
|
|
|
|
}()
|
|
|
|
|
|
|
|
_, err = writer.Write(data)
|
|
|
|
return
|
2019-02-11 09:54:07 +00:00
|
|
|
}
|