Commit c5bca405 by Oleg Gaidarenko Committed by GitHub

Email: add reply-to and direct attachment (#18715)

* Add support for `Reply-To` header

* Allow direct attachment

Don't have tests yet, but they will follow
parent e5e7bd31
...@@ -5,15 +5,25 @@ import "errors" ...@@ -5,15 +5,25 @@ import "errors"
var ErrInvalidEmailCode = errors.New("Invalid or expired email code") var ErrInvalidEmailCode = errors.New("Invalid or expired email code")
var ErrSmtpNotEnabled = errors.New("SMTP not configured, check your grafana.ini config file's [smtp] section") var ErrSmtpNotEnabled = errors.New("SMTP not configured, check your grafana.ini config file's [smtp] section")
// SendEmailAttachFile is a definition of the attached files without path
type SendEmailAttachFile struct {
Name string
Content []byte
}
// SendEmailCommand is command for sending emails
type SendEmailCommand struct { type SendEmailCommand struct {
To []string To []string
Template string Template string
Subject string Subject string
Data map[string]interface{} Data map[string]interface{}
Info string Info string
EmbededFiles []string ReplyTo []string
EmbededFiles []string
AttachedFiles []*SendEmailAttachFile
} }
// SendEmailCommandSync is command for sending emails in sync
type SendEmailCommandSync struct { type SendEmailCommandSync struct {
SendEmailCommand SendEmailCommand
} }
......
...@@ -5,13 +5,22 @@ import ( ...@@ -5,13 +5,22 @@ import (
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )
// AttachedFile is struct representating email attached files
type AttachedFile struct {
Name string
Content []byte
}
// Message is representation of the email message
type Message struct { type Message struct {
To []string To []string
From string From string
Subject string Subject string
Body string Body string
Info string Info string
EmbededFiles []string ReplyTo []string
EmbededFiles []string
AttachedFiles []*AttachedFile
} }
func setDefaultTemplateData(data map[string]interface{}, u *m.User) { func setDefaultTemplateData(data map[string]interface{}, u *m.User) {
......
...@@ -9,13 +9,15 @@ import ( ...@@ -9,13 +9,15 @@ import (
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"html/template" "html/template"
"io"
"net" "net"
"strconv" "strconv"
gomail "gopkg.in/mail.v2"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util/errutil" "github.com/grafana/grafana/pkg/util/errutil"
gomail "gopkg.in/mail.v2"
) )
func (ns *NotificationService) send(msg *Message) (int, error) { func (ns *NotificationService) send(msg *Message) (int, error) {
...@@ -30,8 +32,11 @@ func (ns *NotificationService) send(msg *Message) (int, error) { ...@@ -30,8 +32,11 @@ func (ns *NotificationService) send(msg *Message) (int, error) {
m.SetHeader("From", msg.From) m.SetHeader("From", msg.From)
m.SetHeader("To", address) m.SetHeader("To", address)
m.SetHeader("Subject", msg.Subject) m.SetHeader("Subject", msg.Subject)
for _, file := range msg.EmbededFiles {
m.Embed(file) ns.setFiles(m, msg)
for _, replyTo := range msg.ReplyTo {
m.SetAddressHeader("Reply-To", replyTo, "")
} }
m.SetBody("text/html", msg.Body) m.SetBody("text/html", msg.Body)
...@@ -48,6 +53,23 @@ func (ns *NotificationService) send(msg *Message) (int, error) { ...@@ -48,6 +53,23 @@ func (ns *NotificationService) send(msg *Message) (int, error) {
return num, err return num, err
} }
// setFiles attaches files in various forms
func (ns *NotificationService) setFiles(
m *gomail.Message,
msg *Message,
) {
for _, file := range msg.EmbededFiles {
m.Embed(file)
}
for _, file := range msg.AttachedFiles {
m.Attach(file.Name, gomail.SetCopyFunc(func(writer io.Writer) error {
_, err := writer.Write(file.Content)
return err
}))
}
}
func (ns *NotificationService) createDialer() (*gomail.Dialer, error) { func (ns *NotificationService) createDialer() (*gomail.Dialer, error) {
host, port, err := net.SplitHostPort(ns.Cfg.Smtp.Host) host, port, err := net.SplitHostPort(ns.Cfg.Smtp.Host)
...@@ -127,10 +149,27 @@ func (ns *NotificationService) buildEmailMessage(cmd *models.SendEmailCommand) ( ...@@ -127,10 +149,27 @@ func (ns *NotificationService) buildEmailMessage(cmd *models.SendEmailCommand) (
} }
return &Message{ return &Message{
To: cmd.To, To: cmd.To,
From: fmt.Sprintf("%s <%s>", ns.Cfg.Smtp.FromName, ns.Cfg.Smtp.FromAddress), From: fmt.Sprintf("%s <%s>", ns.Cfg.Smtp.FromName, ns.Cfg.Smtp.FromAddress),
Subject: subject, Subject: subject,
Body: buffer.String(), Body: buffer.String(),
EmbededFiles: cmd.EmbededFiles, EmbededFiles: cmd.EmbededFiles,
AttachedFiles: buildAttachedFiles(cmd.AttachedFiles),
}, nil }, nil
} }
// buildAttachedFiles build attached files
func buildAttachedFiles(
attached []*models.SendEmailAttachFile,
) []*AttachedFile {
result := make([]*AttachedFile, 0)
for _, file := range attached {
result = append(result, &AttachedFile{
Name: file.Name,
Content: file.Content,
})
}
return result
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment