diff options
Diffstat (limited to 'modules/mailer')
-rw-r--r-- | modules/mailer/mail.go | 162 | ||||
-rw-r--r-- | modules/mailer/mailer.go | 126 |
2 files changed, 288 insertions, 0 deletions
diff --git a/modules/mailer/mail.go b/modules/mailer/mail.go new file mode 100644 index 00000000..d2bf1310 --- /dev/null +++ b/modules/mailer/mail.go @@ -0,0 +1,162 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package mailer + +import ( + "encoding/hex" + "errors" + "fmt" + + "github.com/gogits/gogs/models" + "github.com/gogits/gogs/modules/base" + "github.com/gogits/gogs/modules/log" + "github.com/gogits/gogs/modules/middleware" +) + +// Create New mail message use MailFrom and MailUser +func NewMailMessageFrom(To []string, from, subject, body string) Message { + msg := NewHtmlMessage(To, from, subject, body) + msg.User = base.MailService.User + return msg +} + +// Create New mail message use MailFrom and MailUser +func NewMailMessage(To []string, subject, body string) Message { + return NewMailMessageFrom(To, base.MailService.User, subject, body) +} + +func GetMailTmplData(user *models.User) map[interface{}]interface{} { + data := make(map[interface{}]interface{}, 10) + data["AppName"] = base.AppName + data["AppVer"] = base.AppVer + data["AppUrl"] = base.AppUrl + data["AppLogo"] = base.AppLogo + data["ActiveCodeLives"] = base.Service.ActiveCodeLives / 60 + data["ResetPwdCodeLives"] = base.Service.ResetPwdCodeLives / 60 + if user != nil { + data["User"] = user + } + return data +} + +// create a time limit code for user active +func CreateUserActiveCode(user *models.User, startInf interface{}) string { + minutes := base.Service.ActiveCodeLives + data := base.ToStr(user.Id) + user.Email + user.LowerName + user.Passwd + user.Rands + code := base.CreateTimeLimitCode(data, minutes, startInf) + + // add tail hex username + code += hex.EncodeToString([]byte(user.LowerName)) + return code +} + +// Send user register mail with active code +func SendRegisterMail(r *middleware.Render, user *models.User) { + code := CreateUserActiveCode(user, nil) + subject := "Register success, Welcome" + + data := GetMailTmplData(user) + data["Code"] = code + body, err := r.HTMLString("mail/auth/register_success", data) + if err != nil { + log.Error("mail.SendRegisterMail(fail to render): %v", err) + return + } + + msg := NewMailMessage([]string{user.Email}, subject, body) + msg.Info = fmt.Sprintf("UID: %d, send register mail", user.Id) + + SendAsync(&msg) +} + +// Send email verify active email. +func SendActiveMail(r *middleware.Render, user *models.User) { + code := CreateUserActiveCode(user, nil) + + subject := "Verify your e-mail address" + + data := GetMailTmplData(user) + data["Code"] = code + body, err := r.HTMLString("mail/auth/active_email", data) + if err != nil { + log.Error("mail.SendActiveMail(fail to render): %v", err) + return + } + + msg := NewMailMessage([]string{user.Email}, subject, body) + msg.Info = fmt.Sprintf("UID: %d, send active mail", user.Id) + + SendAsync(&msg) +} + +// Send reset password email. +func SendResetPasswdMail(r *middleware.Render, user *models.User) { + code := CreateUserActiveCode(user, nil) + + subject := "Reset your password" + + data := GetMailTmplData(user) + data["Code"] = code + body, err := r.HTMLString("mail/auth/reset_passwd", data) + if err != nil { + log.Error("mail.SendResetPasswdMail(fail to render): %v", err) + return + } + + msg := NewMailMessage([]string{user.Email}, subject, body) + msg.Info = fmt.Sprintf("UID: %d, send reset password email", user.Id) + + SendAsync(&msg) +} + +// SendIssueNotifyMail sends mail notification of all watchers of repository. +func SendIssueNotifyMail(user, owner *models.User, repo *models.Repository, issue *models.Issue) ([]string, error) { + watches, err := models.GetWatches(repo.Id) + if err != nil { + return nil, errors.New("mail.NotifyWatchers(get watches): " + err.Error()) + } + + tos := make([]string, 0, len(watches)) + for i := range watches { + uid := watches[i].UserId + if user.Id == uid { + continue + } + u, err := models.GetUserById(uid) + if err != nil { + return nil, errors.New("mail.NotifyWatchers(get user): " + err.Error()) + } + tos = append(tos, u.Email) + } + + if len(tos) == 0 { + return tos, nil + } + + subject := fmt.Sprintf("[%s] %s", repo.Name, issue.Name) + content := fmt.Sprintf("%s<br>-<br> <a href=\"%s%s/%s/issues/%d\">View it on Gogs</a>.", + base.RenderSpecialLink([]byte(issue.Content), owner.Name+"/"+repo.Name), + base.AppUrl, owner.Name, repo.Name, issue.Index) + msg := NewMailMessageFrom(tos, user.Name, subject, content) + msg.Info = fmt.Sprintf("Subject: %s, send issue notify emails", subject) + SendAsync(&msg) + return tos, nil +} + +// SendIssueMentionMail sends mail notification for who are mentioned in issue. +func SendIssueMentionMail(user, owner *models.User, repo *models.Repository, issue *models.Issue, tos []string) error { + if len(tos) == 0 { + return nil + } + + issueLink := fmt.Sprintf("%s%s/%s/issues/%d", base.AppUrl, owner.Name, repo.Name, issue.Index) + body := fmt.Sprintf(`%s mentioned you.`) + subject := fmt.Sprintf("[%s] %s", repo.Name, issue.Name) + content := fmt.Sprintf("%s<br>-<br> <a href=\"%s\">View it on Gogs</a>.", body, issueLink) + msg := NewMailMessageFrom(tos, user.Name, subject, content) + msg.Info = fmt.Sprintf("Subject: %s, send issue mention emails", subject) + SendAsync(&msg) + return nil +} diff --git a/modules/mailer/mailer.go b/modules/mailer/mailer.go new file mode 100644 index 00000000..63861d87 --- /dev/null +++ b/modules/mailer/mailer.go @@ -0,0 +1,126 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package mailer + +import ( + "fmt" + "net/smtp" + "strings" + + "github.com/gogits/gogs/modules/base" + "github.com/gogits/gogs/modules/log" +) + +type Message struct { + To []string + From string + Subject string + Body string + User string + Type string + Massive bool + Info string +} + +// create mail content +func (m Message) Content() string { + // set mail type + contentType := "text/plain; charset=UTF-8" + if m.Type == "html" { + contentType = "text/html; charset=UTF-8" + } + + // create mail content + content := "From: " + m.From + "<" + m.User + + ">\r\nSubject: " + m.Subject + "\r\nContent-Type: " + contentType + "\r\n\r\n" + m.Body + return content +} + +var mailQueue chan *Message + +func NewMailerContext() { + mailQueue = make(chan *Message, base.Cfg.MustInt("mailer", "SEND_BUFFER_LEN", 10)) + go processMailQueue() +} + +func processMailQueue() { + for { + select { + case msg := <-mailQueue: + num, err := Send(msg) + tos := strings.Join(msg.To, "; ") + info := "" + if err != nil { + if len(msg.Info) > 0 { + info = ", info: " + msg.Info + } + log.Error(fmt.Sprintf("Async sent email %d succeed, not send emails: %s%s err: %s", num, tos, info, err)) + } else { + log.Trace(fmt.Sprintf("Async sent email %d succeed, sent emails: %s%s", num, tos, info)) + } + } + } +} + +// Direct Send mail message +func Send(msg *Message) (int, error) { + log.Trace("Sending mails to: %s", strings.Join(msg.To, "; ")) + host := strings.Split(base.MailService.Host, ":") + + // get message body + content := msg.Content() + + auth := smtp.PlainAuth("", base.MailService.User, base.MailService.Passwd, host[0]) + + if len(msg.To) == 0 { + return 0, fmt.Errorf("empty receive emails") + } + + if len(msg.Body) == 0 { + return 0, fmt.Errorf("empty email body") + } + + if msg.Massive { + // send mail to multiple emails one by one + num := 0 + for _, to := range msg.To { + body := []byte("To: " + to + "\r\n" + content) + err := smtp.SendMail(base.MailService.Host, auth, msg.From, []string{to}, body) + if err != nil { + return num, err + } + num++ + } + return num, nil + } else { + body := []byte("To: " + strings.Join(msg.To, ";") + "\r\n" + content) + + // send to multiple emails in one message + err := smtp.SendMail(base.MailService.Host, auth, msg.From, msg.To, body) + if err != nil { + return 0, err + } else { + return 1, nil + } + } +} + +// Async Send mail message +func SendAsync(msg *Message) { + go func() { + mailQueue <- msg + }() +} + +// Create html mail message +func NewHtmlMessage(To []string, From, Subject, Body string) Message { + return Message{ + To: To, + From: From, + Subject: Subject, + Body: Body, + Type: "html", + } +} |