aboutsummaryrefslogtreecommitdiffstats
path: root/services/mailer/mail.go
diff options
context:
space:
mode:
authorKN4CK3R <admin@oldschoolhack.me>2023-01-14 16:57:10 +0100
committerGitHub <noreply@github.com>2023-01-14 23:57:10 +0800
commitfc037b4b825f0501a1489e10d7c822435d825cb7 (patch)
tree551590b5ec197d8efca8b7bc3a9acc5961637d9d /services/mailer/mail.go
parent20e3ffd2085d7066b3206809dfae7b6ebd59cb5d (diff)
downloadgitea-fc037b4b825f0501a1489e10d7c822435d825cb7.tar.gz
gitea-fc037b4b825f0501a1489e10d7c822435d825cb7.zip
Add support for incoming emails (#22056)
closes #13585 fixes #9067 fixes #2386 ref #6226 ref #6219 fixes #745 This PR adds support to process incoming emails to perform actions. Currently I added handling of replies and unsubscribing from issues/pulls. In contrast to #13585 the IMAP IDLE command is used instead of polling which results (in my opinion 😉) in cleaner code. Procedure: - When sending an issue/pull reply email, a token is generated which is present in the Reply-To and References header. - IMAP IDLE waits until a new email arrives - The token tells which action should be performed A possible signature and/or reply gets stripped from the content. I added a new service to the drone pipeline to test the receiving of incoming mails. If we keep this in, we may test our outgoing emails too in future. Co-authored-by: silverwind <me@silverwind.io> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Diffstat (limited to 'services/mailer/mail.go')
-rw-r--r--services/mailer/mail.go55
1 files changed, 49 insertions, 6 deletions
diff --git a/services/mailer/mail.go b/services/mailer/mail.go
index 674011bede..6af4ed249c 100644
--- a/services/mailer/mail.go
+++ b/services/mailer/mail.go
@@ -29,6 +29,8 @@ import (
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/translation"
+ incoming_payload "code.gitea.io/gitea/services/mailer/incoming/payload"
+ "code.gitea.io/gitea/services/mailer/token"
"gopkg.in/gomail.v2"
)
@@ -302,14 +304,57 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
msgID := createReference(ctx.Issue, ctx.Comment, ctx.ActionType)
reference := createReference(ctx.Issue, nil, activities_model.ActionType(0))
+ var replyPayload []byte
+ if ctx.Comment != nil && ctx.Comment.Type == issues_model.CommentTypeCode {
+ replyPayload, err = incoming_payload.CreateReferencePayload(ctx.Comment)
+ } else {
+ replyPayload, err = incoming_payload.CreateReferencePayload(ctx.Issue)
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ unsubscribePayload, err := incoming_payload.CreateReferencePayload(ctx.Issue)
+ if err != nil {
+ return nil, err
+ }
+
msgs := make([]*Message, 0, len(recipients))
for _, recipient := range recipients {
msg := NewMessageFrom([]string{recipient.Email}, ctx.Doer.DisplayName(), setting.MailService.FromEmail, subject, mailBody.String())
msg.Info = fmt.Sprintf("Subject: %s, %s", subject, info)
- msg.SetHeader("Message-ID", "<"+msgID+">")
- msg.SetHeader("In-Reply-To", "<"+reference+">")
- msg.SetHeader("References", "<"+reference+">")
+ msg.SetHeader("Message-ID", msgID)
+ msg.SetHeader("In-Reply-To", reference)
+
+ references := []string{reference}
+ listUnsubscribe := []string{"<" + ctx.Issue.HTMLURL() + ">"}
+
+ if setting.IncomingEmail.Enabled {
+ if ctx.Comment != nil {
+ token, err := token.CreateToken(token.ReplyHandlerType, recipient, replyPayload)
+ if err != nil {
+ log.Error("CreateToken failed: %v", err)
+ } else {
+ replyAddress := strings.Replace(setting.IncomingEmail.ReplyToAddress, setting.IncomingEmail.TokenPlaceholder, token, 1)
+ msg.ReplyTo = replyAddress
+ msg.SetHeader("List-Post", fmt.Sprintf("<mailto:%s>", replyAddress))
+
+ references = append(references, fmt.Sprintf("<reply-%s@%s>", token, setting.Domain))
+ }
+ }
+
+ token, err := token.CreateToken(token.UnsubscribeHandlerType, recipient, unsubscribePayload)
+ if err != nil {
+ log.Error("CreateToken failed: %v", err)
+ } else {
+ unsubAddress := strings.Replace(setting.IncomingEmail.ReplyToAddress, setting.IncomingEmail.TokenPlaceholder, token, 1)
+ listUnsubscribe = append(listUnsubscribe, "<mailto:"+unsubAddress+">")
+ }
+ }
+
+ msg.SetHeader("References", references...)
+ msg.SetHeader("List-Unsubscribe", listUnsubscribe...)
for key, value := range generateAdditionalHeaders(ctx, actType, recipient) {
msg.SetHeader(key, value)
@@ -345,7 +390,7 @@ func createReference(issue *issues_model.Issue, comment *issues_model.Comment, a
}
}
- return fmt.Sprintf("%s/%s/%d%s@%s", issue.Repo.FullName(), path, issue.Index, extra, setting.Domain)
+ return fmt.Sprintf("<%s/%s/%d%s@%s>", issue.Repo.FullName(), path, issue.Index, extra, setting.Domain)
}
func generateAdditionalHeaders(ctx *mailCommentContext, reason string, recipient *user_model.User) map[string]string {
@@ -357,8 +402,6 @@ func generateAdditionalHeaders(ctx *mailCommentContext, reason string, recipient
// https://datatracker.ietf.org/doc/html/rfc2369
"List-Archive": fmt.Sprintf("<%s>", repo.HTMLURL()),
- //"List-Post": https://github.com/go-gitea/gitea/pull/13585
- "List-Unsubscribe": ctx.Issue.HTMLURL(),
"X-Mailer": "Gitea",
"X-Gitea-Reason": reason,