Skip to content

Commit

Permalink
Email: add reply-to and direct attachment (grafana#18715)
Browse files Browse the repository at this point in the history
* Add support for `Reply-To` header

* Allow direct attachment

Don't have tests yet, but they will follow
  • Loading branch information
markelog authored Aug 26, 2019
1 parent e5e7bd3 commit c5bca40
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 20 deletions.
22 changes: 16 additions & 6 deletions pkg/models/notifications.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,25 @@ import "errors"
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")

// 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 {
To []string
Template string
Subject string
Data map[string]interface{}
Info string
EmbededFiles []string
To []string
Template string
Subject string
Data map[string]interface{}
Info string
ReplyTo []string
EmbededFiles []string
AttachedFiles []*SendEmailAttachFile
}

// SendEmailCommandSync is command for sending emails in sync
type SendEmailCommandSync struct {
SendEmailCommand
}
Expand Down
21 changes: 15 additions & 6 deletions pkg/services/notifications/email.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,22 @@ import (
"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 {
To []string
From string
Subject string
Body string
Info string
EmbededFiles []string
To []string
From string
Subject string
Body string
Info string
ReplyTo []string
EmbededFiles []string
AttachedFiles []*AttachedFile
}

func setDefaultTemplateData(data map[string]interface{}, u *m.User) {
Expand Down
55 changes: 47 additions & 8 deletions pkg/services/notifications/mailer.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import (
"crypto/tls"
"fmt"
"html/template"
"io"
"net"
"strconv"

gomail "gopkg.in/mail.v2"

"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util/errutil"
gomail "gopkg.in/mail.v2"
)

func (ns *NotificationService) send(msg *Message) (int, error) {
Expand All @@ -30,8 +32,11 @@ func (ns *NotificationService) send(msg *Message) (int, error) {
m.SetHeader("From", msg.From)
m.SetHeader("To", address)
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)
Expand All @@ -48,6 +53,23 @@ func (ns *NotificationService) send(msg *Message) (int, error) {
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) {
host, port, err := net.SplitHostPort(ns.Cfg.Smtp.Host)

Expand Down Expand Up @@ -127,10 +149,27 @@ func (ns *NotificationService) buildEmailMessage(cmd *models.SendEmailCommand) (
}

return &Message{
To: cmd.To,
From: fmt.Sprintf("%s <%s>", ns.Cfg.Smtp.FromName, ns.Cfg.Smtp.FromAddress),
Subject: subject,
Body: buffer.String(),
EmbededFiles: cmd.EmbededFiles,
To: cmd.To,
From: fmt.Sprintf("%s <%s>", ns.Cfg.Smtp.FromName, ns.Cfg.Smtp.FromAddress),
Subject: subject,
Body: buffer.String(),
EmbededFiles: cmd.EmbededFiles,
AttachedFiles: buildAttachedFiles(cmd.AttachedFiles),
}, 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
}

0 comments on commit c5bca40

Please sign in to comment.