|
|
@ -18,7 +18,9 @@ import ( |
|
|
|
"time" |
|
|
|
"time" |
|
|
|
|
|
|
|
|
|
|
|
"code.gitea.io/gitea/modules/base" |
|
|
|
"code.gitea.io/gitea/modules/base" |
|
|
|
|
|
|
|
"code.gitea.io/gitea/modules/graceful" |
|
|
|
"code.gitea.io/gitea/modules/log" |
|
|
|
"code.gitea.io/gitea/modules/log" |
|
|
|
|
|
|
|
"code.gitea.io/gitea/modules/queue" |
|
|
|
"code.gitea.io/gitea/modules/setting" |
|
|
|
"code.gitea.io/gitea/modules/setting" |
|
|
|
|
|
|
|
|
|
|
|
"github.com/jaytaylor/html2text" |
|
|
|
"github.com/jaytaylor/html2text" |
|
|
@ -27,38 +29,63 @@ import ( |
|
|
|
|
|
|
|
|
|
|
|
// Message mail body and log info
|
|
|
|
// Message mail body and log info
|
|
|
|
type Message struct { |
|
|
|
type Message struct { |
|
|
|
Info string // Message information for log purpose.
|
|
|
|
Info string // Message information for log purpose.
|
|
|
|
*gomail.Message |
|
|
|
FromAddress string |
|
|
|
|
|
|
|
FromDisplayName string |
|
|
|
|
|
|
|
To []string |
|
|
|
|
|
|
|
Subject string |
|
|
|
|
|
|
|
Date time.Time |
|
|
|
|
|
|
|
Body string |
|
|
|
|
|
|
|
Headers map[string][]string |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// NewMessageFrom creates new mail message object with custom From header.
|
|
|
|
// ToMessage converts a Message to gomail.Message
|
|
|
|
func NewMessageFrom(to []string, fromDisplayName, fromAddress, subject, body string) *Message { |
|
|
|
func (m *Message) ToMessage() *gomail.Message { |
|
|
|
log.Trace("NewMessageFrom (body):\n%s", body) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
msg := gomail.NewMessage() |
|
|
|
msg := gomail.NewMessage() |
|
|
|
msg.SetAddressHeader("From", fromAddress, fromDisplayName) |
|
|
|
msg.SetAddressHeader("From", m.FromAddress, m.FromDisplayName) |
|
|
|
msg.SetHeader("To", to...) |
|
|
|
msg.SetHeader("To", m.To...) |
|
|
|
|
|
|
|
for header := range m.Headers { |
|
|
|
|
|
|
|
msg.SetHeader(header, m.Headers[header]...) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if len(setting.MailService.SubjectPrefix) > 0 { |
|
|
|
if len(setting.MailService.SubjectPrefix) > 0 { |
|
|
|
msg.SetHeader("Subject", setting.MailService.SubjectPrefix+" "+subject) |
|
|
|
msg.SetHeader("Subject", setting.MailService.SubjectPrefix+" "+m.Subject) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
msg.SetHeader("Subject", subject) |
|
|
|
msg.SetHeader("Subject", m.Subject) |
|
|
|
} |
|
|
|
} |
|
|
|
msg.SetDateHeader("Date", time.Now()) |
|
|
|
msg.SetDateHeader("Date", m.Date) |
|
|
|
msg.SetHeader("X-Auto-Response-Suppress", "All") |
|
|
|
msg.SetHeader("X-Auto-Response-Suppress", "All") |
|
|
|
|
|
|
|
|
|
|
|
plainBody, err := html2text.FromString(body) |
|
|
|
plainBody, err := html2text.FromString(m.Body) |
|
|
|
if err != nil || setting.MailService.SendAsPlainText { |
|
|
|
if err != nil || setting.MailService.SendAsPlainText { |
|
|
|
if strings.Contains(base.TruncateString(body, 100), "<html>") { |
|
|
|
if strings.Contains(base.TruncateString(m.Body, 100), "<html>") { |
|
|
|
log.Warn("Mail contains HTML but configured to send as plain text.") |
|
|
|
log.Warn("Mail contains HTML but configured to send as plain text.") |
|
|
|
} |
|
|
|
} |
|
|
|
msg.SetBody("text/plain", plainBody) |
|
|
|
msg.SetBody("text/plain", plainBody) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
msg.SetBody("text/plain", plainBody) |
|
|
|
msg.SetBody("text/plain", plainBody) |
|
|
|
msg.AddAlternative("text/html", body) |
|
|
|
msg.AddAlternative("text/html", m.Body) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return msg |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// SetHeader adds additional headers to a message
|
|
|
|
|
|
|
|
func (m *Message) SetHeader(field string, value ...string) { |
|
|
|
|
|
|
|
m.Headers[field] = value |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// NewMessageFrom creates new mail message object with custom From header.
|
|
|
|
|
|
|
|
func NewMessageFrom(to []string, fromDisplayName, fromAddress, subject, body string) *Message { |
|
|
|
|
|
|
|
log.Trace("NewMessageFrom (body):\n%s", body) |
|
|
|
|
|
|
|
|
|
|
|
return &Message{ |
|
|
|
return &Message{ |
|
|
|
Message: msg, |
|
|
|
FromAddress: fromAddress, |
|
|
|
|
|
|
|
FromDisplayName: fromDisplayName, |
|
|
|
|
|
|
|
To: to, |
|
|
|
|
|
|
|
Subject: subject, |
|
|
|
|
|
|
|
Date: time.Now(), |
|
|
|
|
|
|
|
Body: body, |
|
|
|
|
|
|
|
Headers: map[string][]string{}, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -257,18 +284,7 @@ func (s *dummySender) Send(from string, to []string, msg io.WriterTo) error { |
|
|
|
return nil |
|
|
|
return nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func processMailQueue() { |
|
|
|
var mailQueue queue.Queue |
|
|
|
for msg := range mailQueue { |
|
|
|
|
|
|
|
log.Trace("New e-mail sending request %s: %s", msg.GetHeader("To"), msg.Info) |
|
|
|
|
|
|
|
if err := gomail.Send(Sender, msg.Message); err != nil { |
|
|
|
|
|
|
|
log.Error("Failed to send emails %s: %s - %v", msg.GetHeader("To"), msg.Info, err) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
log.Trace("E-mails sent %s: %s", msg.GetHeader("To"), msg.Info) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var mailQueue chan *Message |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Sender sender for sending mail synchronously
|
|
|
|
// Sender sender for sending mail synchronously
|
|
|
|
var Sender gomail.Sender |
|
|
|
var Sender gomail.Sender |
|
|
@ -291,14 +307,26 @@ func NewContext() { |
|
|
|
Sender = &dummySender{} |
|
|
|
Sender = &dummySender{} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
mailQueue = make(chan *Message, setting.MailService.QueueLength) |
|
|
|
mailQueue = queue.CreateQueue("mail", func(data ...queue.Data) { |
|
|
|
go processMailQueue() |
|
|
|
for _, datum := range data { |
|
|
|
|
|
|
|
msg := datum.(*Message) |
|
|
|
|
|
|
|
gomailMsg := msg.ToMessage() |
|
|
|
|
|
|
|
log.Trace("New e-mail sending request %s: %s", gomailMsg.GetHeader("To"), msg.Info) |
|
|
|
|
|
|
|
if err := gomail.Send(Sender, gomailMsg); err != nil { |
|
|
|
|
|
|
|
log.Error("Failed to send emails %s: %s - %v", gomailMsg.GetHeader("To"), msg.Info, err) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
log.Trace("E-mails sent %s: %s", gomailMsg.GetHeader("To"), msg.Info) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, &Message{}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
go graceful.GetManager().RunWithShutdownFns(mailQueue.Run) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// SendAsync send mail asynchronously
|
|
|
|
// SendAsync send mail asynchronously
|
|
|
|
func SendAsync(msg *Message) { |
|
|
|
func SendAsync(msg *Message) { |
|
|
|
go func() { |
|
|
|
go func() { |
|
|
|
mailQueue <- msg |
|
|
|
_ = mailQueue.Push(msg) |
|
|
|
}() |
|
|
|
}() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -306,7 +334,7 @@ func SendAsync(msg *Message) { |
|
|
|
func SendAsyncs(msgs []*Message) { |
|
|
|
func SendAsyncs(msgs []*Message) { |
|
|
|
go func() { |
|
|
|
go func() { |
|
|
|
for _, msg := range msgs { |
|
|
|
for _, msg := range msgs { |
|
|
|
mailQueue <- msg |
|
|
|
_ = mailQueue.Push(msg) |
|
|
|
} |
|
|
|
} |
|
|
|
}() |
|
|
|
}() |
|
|
|
} |
|
|
|
} |
|
|
|