diff options
author | Lunny Xiao <xiaolunwen@gmail.com> | 2019-09-24 13:02:49 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-09-24 13:02:49 +0800 |
commit | 5a438ee3c0303efcb9d1935ff521917fe8a109e8 (patch) | |
tree | 8f36452631fc8a6d077addbbf857da6d50024a0a /modules | |
parent | 730065a3dc72683460b95ba1486dba8ec355a373 (diff) | |
download | gitea-5a438ee3c0303efcb9d1935ff521917fe8a109e8.tar.gz gitea-5a438ee3c0303efcb9d1935ff521917fe8a109e8.zip |
Move all mail related codes from models to services/mailer (#7200)
* move all mail related codes from models to modules/mailer
* fix lint
* use DBContext instead Engine
* use WithContext not WithEngine
* Use DBContext instead of Engine
* don't use defer when sess.Close()
* move DBContext to context.go and add some methods
* move mailer from modules/ to services
* fix lint
* fix tests
* fix fmt
* add gitea copyright
* fix tests
* don't expose db functions
* make code clear
* add DefaultDBContext
* fix build
* fix bug
Diffstat (limited to 'modules')
-rw-r--r-- | modules/mailer/mailer.go | 303 | ||||
-rw-r--r-- | modules/notification/mail/mail.go | 11 |
2 files changed, 6 insertions, 308 deletions
diff --git a/modules/mailer/mailer.go b/modules/mailer/mailer.go deleted file mode 100644 index d19ae7b2f4..0000000000 --- a/modules/mailer/mailer.go +++ /dev/null @@ -1,303 +0,0 @@ -// Copyright 2014 The Gogs Authors. All rights reserved. -// Copyright 2017 The Gitea 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 ( - "bytes" - "crypto/tls" - "fmt" - "io" - "net" - "net/smtp" - "os" - "os/exec" - "strings" - "time" - - "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - - "github.com/jaytaylor/html2text" - "gopkg.in/gomail.v2" -) - -// Message mail body and log info -type Message struct { - Info string // Message information for log purpose. - *gomail.Message -} - -// 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) - - msg := gomail.NewMessage() - msg.SetAddressHeader("From", fromAddress, fromDisplayName) - msg.SetHeader("To", to...) - if len(setting.MailService.SubjectPrefix) > 0 { - msg.SetHeader("Subject", setting.MailService.SubjectPrefix+" "+subject) - } else { - msg.SetHeader("Subject", subject) - } - msg.SetDateHeader("Date", time.Now()) - msg.SetHeader("X-Auto-Response-Suppress", "All") - - plainBody, err := html2text.FromString(body) - if err != nil || setting.MailService.SendAsPlainText { - if strings.Contains(base.TruncateString(body, 100), "<html>") { - log.Warn("Mail contains HTML but configured to send as plain text.") - } - msg.SetBody("text/plain", plainBody) - } else { - msg.SetBody("text/plain", plainBody) - msg.AddAlternative("text/html", body) - } - - return &Message{ - Message: msg, - } -} - -// NewMessage creates new mail message object with default From header. -func NewMessage(to []string, subject, body string) *Message { - return NewMessageFrom(to, setting.MailService.FromName, setting.MailService.FromEmail, subject, body) -} - -type loginAuth struct { - username, password string -} - -// LoginAuth SMTP AUTH LOGIN Auth Handler -func LoginAuth(username, password string) smtp.Auth { - return &loginAuth{username, password} -} - -// Start start SMTP login auth -func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) { - return "LOGIN", []byte{}, nil -} - -// Next next step of SMTP login auth -func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) { - if more { - switch string(fromServer) { - case "Username:": - return []byte(a.username), nil - case "Password:": - return []byte(a.password), nil - default: - return nil, fmt.Errorf("unknown fromServer: %s", string(fromServer)) - } - } - return nil, nil -} - -// Sender SMTP mail sender -type smtpSender struct { -} - -// Send send email -func (s *smtpSender) Send(from string, to []string, msg io.WriterTo) error { - opts := setting.MailService - - host, port, err := net.SplitHostPort(opts.Host) - if err != nil { - return err - } - - tlsconfig := &tls.Config{ - InsecureSkipVerify: opts.SkipVerify, - ServerName: host, - } - - if opts.UseCertificate { - cert, err := tls.LoadX509KeyPair(opts.CertFile, opts.KeyFile) - if err != nil { - return err - } - tlsconfig.Certificates = []tls.Certificate{cert} - } - - conn, err := net.Dial("tcp", net.JoinHostPort(host, port)) - if err != nil { - return err - } - defer conn.Close() - - isSecureConn := opts.IsTLSEnabled || (strings.HasSuffix(port, "465")) - // Start TLS directly if the port ends with 465 (SMTPS protocol) - if isSecureConn { - conn = tls.Client(conn, tlsconfig) - } - - client, err := smtp.NewClient(conn, host) - if err != nil { - return fmt.Errorf("NewClient: %v", err) - } - - if !opts.DisableHelo { - hostname := opts.HeloHostname - if len(hostname) == 0 { - hostname, err = os.Hostname() - if err != nil { - return err - } - } - - if err = client.Hello(hostname); err != nil { - return fmt.Errorf("Hello: %v", err) - } - } - - // If not using SMTPS, always use STARTTLS if available - hasStartTLS, _ := client.Extension("STARTTLS") - if !isSecureConn && hasStartTLS { - if err = client.StartTLS(tlsconfig); err != nil { - return fmt.Errorf("StartTLS: %v", err) - } - } - - canAuth, options := client.Extension("AUTH") - if canAuth && len(opts.User) > 0 { - var auth smtp.Auth - - if strings.Contains(options, "CRAM-MD5") { - auth = smtp.CRAMMD5Auth(opts.User, opts.Passwd) - } else if strings.Contains(options, "PLAIN") { - auth = smtp.PlainAuth("", opts.User, opts.Passwd, host) - } else if strings.Contains(options, "LOGIN") { - // Patch for AUTH LOGIN - auth = LoginAuth(opts.User, opts.Passwd) - } - - if auth != nil { - if err = client.Auth(auth); err != nil { - return fmt.Errorf("Auth: %v", err) - } - } - } - - if err = client.Mail(from); err != nil { - return fmt.Errorf("Mail: %v", err) - } - - for _, rec := range to { - if err = client.Rcpt(rec); err != nil { - return fmt.Errorf("Rcpt: %v", err) - } - } - - w, err := client.Data() - if err != nil { - return fmt.Errorf("Data: %v", err) - } else if _, err = msg.WriteTo(w); err != nil { - return fmt.Errorf("WriteTo: %v", err) - } else if err = w.Close(); err != nil { - return fmt.Errorf("Close: %v", err) - } - - return client.Quit() -} - -// Sender sendmail mail sender -type sendmailSender struct { -} - -// Send send email -func (s *sendmailSender) Send(from string, to []string, msg io.WriterTo) error { - var err error - var closeError error - var waitError error - - args := []string{"-F", from, "-i"} - args = append(args, setting.MailService.SendmailArgs...) - args = append(args, to...) - log.Trace("Sending with: %s %v", setting.MailService.SendmailPath, args) - cmd := exec.Command(setting.MailService.SendmailPath, args...) - pipe, err := cmd.StdinPipe() - - if err != nil { - return err - } - - if err = cmd.Start(); err != nil { - return err - } - - _, err = msg.WriteTo(pipe) - - // we MUST close the pipe or sendmail will hang waiting for more of the message - // Also we should wait on our sendmail command even if something fails - closeError = pipe.Close() - waitError = cmd.Wait() - if err != nil { - return err - } else if closeError != nil { - return closeError - } else { - return waitError - } -} - -// Sender sendmail mail sender -type dummySender struct { -} - -// Send send email -func (s *dummySender) Send(from string, to []string, msg io.WriterTo) error { - buf := bytes.Buffer{} - if _, err := msg.WriteTo(&buf); err != nil { - return err - } - log.Info("Mail From: %s To: %v Body: %s", from, to, buf.String()) - return nil -} - -func processMailQueue() { - 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 -var Sender gomail.Sender - -// NewContext start mail queue service -func NewContext() { - // Need to check if mailQueue is nil because in during reinstall (user had installed - // before but swithed install lock off), this function will be called again - // while mail queue is already processing tasks, and produces a race condition. - if setting.MailService == nil || mailQueue != nil { - return - } - - switch setting.MailService.MailerType { - case "smtp": - Sender = &smtpSender{} - case "sendmail": - Sender = &sendmailSender{} - case "dummy": - Sender = &dummySender{} - } - - mailQueue = make(chan *Message, setting.MailService.QueueLength) - go processMailQueue() -} - -// SendAsync send mail asynchronous -func SendAsync(msg *Message) { - go func() { - mailQueue <- msg - }() -} diff --git a/modules/notification/mail/mail.go b/modules/notification/mail/mail.go index 9d0db4f415..e1ae391f78 100644 --- a/modules/notification/mail/mail.go +++ b/modules/notification/mail/mail.go @@ -8,6 +8,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/notification/base" + "code.gitea.io/gitea/services/mailer" ) type mailNotifier struct { @@ -36,13 +37,13 @@ func (m *mailNotifier) NotifyCreateIssueComment(doer *models.User, repo *models. act = models.ActionCommentIssue } - if err := comment.MailParticipants(act, issue); err != nil { + if err := mailer.MailParticipantsComment(comment, act, issue); err != nil { log.Error("MailParticipants: %v", err) } } func (m *mailNotifier) NotifyNewIssue(issue *models.Issue) { - if err := issue.MailParticipants(issue.Poster, models.ActionCreateIssue); err != nil { + if err := mailer.MailParticipants(issue, issue.Poster, models.ActionCreateIssue); err != nil { log.Error("MailParticipants: %v", err) } } @@ -63,13 +64,13 @@ func (m *mailNotifier) NotifyIssueChangeStatus(doer *models.User, issue *models. } } - if err := issue.MailParticipants(doer, actionType); err != nil { + if err := mailer.MailParticipants(issue, doer, actionType); err != nil { log.Error("MailParticipants: %v", err) } } func (m *mailNotifier) NotifyNewPullRequest(pr *models.PullRequest) { - if err := pr.Issue.MailParticipants(pr.Issue.Poster, models.ActionCreatePullRequest); err != nil { + if err := mailer.MailParticipants(pr.Issue, pr.Issue.Poster, models.ActionCreatePullRequest); err != nil { log.Error("MailParticipants: %v", err) } } @@ -83,7 +84,7 @@ func (m *mailNotifier) NotifyPullRequestReview(pr *models.PullRequest, r *models } else if comment.Type == models.CommentTypeComment { act = models.ActionCommentIssue } - if err := comment.MailParticipants(act, pr.Issue); err != nil { + if err := mailer.MailParticipantsComment(comment, act, pr.Issue); err != nil { log.Error("MailParticipants: %v", err) } } |