200 lines
4.6 KiB
Go
200 lines
4.6 KiB
Go
|
package emailclient
|
||
|
|
||
|
import (
|
||
|
"crypto/tls"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"net"
|
||
|
"net/mail"
|
||
|
"net/smtp"
|
||
|
|
||
|
log "github.com/sirupsen/logrus"
|
||
|
)
|
||
|
|
||
|
// SMTPCredentials is a struc holding the information needed to connect to a SMTP server
|
||
|
type SMTPCredentials struct {
|
||
|
Host string
|
||
|
Email string
|
||
|
Password string
|
||
|
}
|
||
|
|
||
|
// emailClient is a struct holding the information requiered to send with SMTP
|
||
|
type emailClient struct {
|
||
|
client *smtp.Client
|
||
|
}
|
||
|
|
||
|
// EmailClient is the generic interface for all client sending emails
|
||
|
type EmailClient interface {
|
||
|
SendMsg(headers map[string]string, body string)
|
||
|
}
|
||
|
|
||
|
// NewSMTPClient (host, user, password) returns a SMTPClient
|
||
|
//
|
||
|
// host is the SMTP server used to connect
|
||
|
// port is the SMTP port used to connect
|
||
|
// user is usually the email address use to connect to SMTP
|
||
|
// password is the associated password
|
||
|
func NewSMTPClient(host string, port string, user string, password string) (EmailClient, error) {
|
||
|
var ec emailClient
|
||
|
|
||
|
log.Infof("user: (%s) pass: (%s)", user, password)
|
||
|
|
||
|
auth := smtp.PlainAuth("", user, password, host)
|
||
|
|
||
|
// TLS config
|
||
|
tlsconfig := &tls.Config{
|
||
|
InsecureSkipVerify: false,
|
||
|
ServerName: host,
|
||
|
}
|
||
|
|
||
|
// Here is the key, you need to call tls.Dial instead of smtp.Dial
|
||
|
// for smtp servers running on 465 that require an ssl connection
|
||
|
// from the very beginning (no starttls)
|
||
|
var conn *tls.Conn
|
||
|
var c *smtp.Client
|
||
|
var err error
|
||
|
if port == "465" {
|
||
|
conn, err = tls.Dial("tcp", host+":"+port, tlsconfig)
|
||
|
if err != nil {
|
||
|
log.Error("[sendMail] ERROR while dialing: " + err.Error())
|
||
|
return ec, err
|
||
|
}
|
||
|
|
||
|
c, err = smtp.NewClient(conn, host)
|
||
|
if err != nil {
|
||
|
log.Error("[sendMail] ERROR while NewClient: " + err.Error())
|
||
|
return ec, err
|
||
|
}
|
||
|
} else {
|
||
|
c, err = smtp.Dial(host + ":" + port)
|
||
|
if err != nil {
|
||
|
log.Error("[sendMail] ERROR while Dialing: " + err.Error())
|
||
|
return ec, err
|
||
|
}
|
||
|
err = c.StartTLS(tlsconfig)
|
||
|
if err != nil {
|
||
|
log.Error("[sendMail] ERROR while StartTLS: " + err.Error())
|
||
|
return ec, err
|
||
|
}
|
||
|
}
|
||
|
// defer c.Quit()
|
||
|
// defer c.Close()
|
||
|
|
||
|
ec.client = c
|
||
|
|
||
|
// Auth
|
||
|
if err = ec.client.Auth(auth); err != nil {
|
||
|
log.Error("[sendMail] ERROR while Auth: " + err.Error())
|
||
|
return ec, err
|
||
|
}
|
||
|
|
||
|
return ec, nil
|
||
|
}
|
||
|
|
||
|
// NewGmailClient returns a GmailClient
|
||
|
func NewGmailClient(email, pass string) (EmailClient, error) {
|
||
|
var ec emailClient
|
||
|
|
||
|
// cred.Host = "smtp.gmail.com:587"
|
||
|
echost := "smtp.gmail.com:465"
|
||
|
if pass == "" {
|
||
|
log.Error("Email password missing")
|
||
|
return ec, errors.New("Email password missing")
|
||
|
}
|
||
|
|
||
|
host, _, _ := net.SplitHostPort(echost)
|
||
|
auth := smtp.PlainAuth("", email, pass, host)
|
||
|
|
||
|
// TLS config
|
||
|
tlsconfig := &tls.Config{
|
||
|
InsecureSkipVerify: true,
|
||
|
ServerName: host,
|
||
|
}
|
||
|
|
||
|
// Cavehat, you need to call tls.Dial instead of smtp.Dial
|
||
|
// for smtp servers running on 465 that require an ssl connection
|
||
|
// from the very beginning (no starttls)
|
||
|
conn, err := tls.Dial("tcp", echost, tlsconfig)
|
||
|
if err != nil {
|
||
|
log.Error(fmt.Sprintf("Dial error: " + err.Error()))
|
||
|
return ec, err
|
||
|
}
|
||
|
|
||
|
client, err := smtp.NewClient(conn, host)
|
||
|
if err != nil {
|
||
|
log.Error(fmt.Sprintf("NewClient error: " + err.Error()))
|
||
|
return ec, err
|
||
|
}
|
||
|
ec.client = client
|
||
|
|
||
|
// Auth
|
||
|
if err = ec.client.Auth(auth); err != nil {
|
||
|
log.Error(fmt.Sprintf("Auth error: %s (%s)", err, host))
|
||
|
return ec, err
|
||
|
}
|
||
|
|
||
|
return ec, nil
|
||
|
}
|
||
|
|
||
|
// SendMsg send a message (email) to rcpt which is in the 'headers' parameter ('To' key) and whose content is the 'body' parameter.
|
||
|
// Ohter keys of interest in headers are 'Subject' and 'MIME-version'
|
||
|
func (ec emailClient) SendMsg(headers map[string]string, body string) {
|
||
|
from := mail.Address{Name: "", Address: headers["From"]}
|
||
|
to := mail.Address{Name: "", Address: headers["To"]}
|
||
|
|
||
|
// headers["To"] = to.String()
|
||
|
if _, ok := headers["Subject"]; ok {
|
||
|
headers["Subject"] = "Subject unknown"
|
||
|
}
|
||
|
|
||
|
log.Printf("from = %s", headers["From"])
|
||
|
log.Printf("to = %s", headers["To"])
|
||
|
log.Printf("subj = %s", headers["Subject"])
|
||
|
|
||
|
// Setup message
|
||
|
message := ""
|
||
|
for k, v := range headers {
|
||
|
message += fmt.Sprintf("%s: %s\r\n", k, v)
|
||
|
}
|
||
|
message += "\r\n" + body
|
||
|
|
||
|
c := ec.client
|
||
|
|
||
|
// To && From
|
||
|
if err := c.Mail(from.Address); err != nil {
|
||
|
log.Error("Can't set From: " + err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if err := c.Rcpt(to.Address); err != nil {
|
||
|
log.Error("Can't set To: " + err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Data
|
||
|
w, err := c.Data()
|
||
|
if err != nil {
|
||
|
log.Error("Can't set Data: " + err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
_, err = w.Write([]byte(message))
|
||
|
if err != nil {
|
||
|
log.Error("Can't write Data: " + err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
err = w.Close()
|
||
|
if err != nil {
|
||
|
log.Error("Can't Close: " + err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
err = c.Quit()
|
||
|
if err != nil {
|
||
|
log.Error("Can't Quit: " + err.Error())
|
||
|
return
|
||
|
}
|
||
|
c.Close()
|
||
|
}
|