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 (
|
|
|
|
"crypto/tls"
|
|
|
|
"net"
|
|
|
|
"net/mail"
|
|
|
|
"net/smtp"
|
|
|
|
|
|
|
|
"github.com/zeebo/errs"
|
|
|
|
)
|
|
|
|
|
2019-03-02 15:22:20 +00:00
|
|
|
// Address is alias of net/mail.Address
|
|
|
|
type Address = mail.Address
|
|
|
|
|
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
|
|
|
|
func (sender *SMTPSender) SendEmail(msg *Message) error {
|
2019-03-02 15:22:20 +00:00
|
|
|
// TODO: validate address before initializing SMTPSender
|
|
|
|
// suppress error because address should be validated
|
|
|
|
// before creating SMTPSender
|
|
|
|
host, _, _ := net.SplitHostPort(sender.ServerAddress)
|
2019-02-11 09:54:07 +00:00
|
|
|
|
|
|
|
client, err := smtp.Dial(sender.ServerAddress)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// close underlying connection
|
|
|
|
defer func() {
|
|
|
|
err = errs.Combine(err, client.Close())
|
|
|
|
}()
|
|
|
|
|
|
|
|
// send smtp hello or ehlo msg and establish connection over tls
|
|
|
|
err = client.StartTLS(&tls.Config{ServerName: host})
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
data, err := client.Data()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer func() {
|
|
|
|
err = errs.Combine(err, data.Close())
|
|
|
|
}()
|
|
|
|
|
2019-05-13 16:14:45 +01:00
|
|
|
mess, err := msg.Bytes()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = data.Write(mess)
|
2019-02-11 09:54:07 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-03-02 15:22:20 +00:00
|
|
|
// send quit msg to stop gracefully returns err on
|
|
|
|
// success but we don't really care about the result
|
|
|
|
_ = client.Quit()
|
|
|
|
return nil
|
2019-02-11 09:54:07 +00:00
|
|
|
}
|