Skip to content

Commit 4696bcb

Browse files
authored
Use FullName in Emails to address the recipient if possible (#31527)
Before we had just the plain mail address as recipient. But now we provide additional Information for the Mail clients. --- *Sponsored by Kithara Software GmbH*
1 parent d7c7a78 commit 4696bcb

File tree

5 files changed

+66
-13
lines changed

5 files changed

+66
-13
lines changed

models/user/user.go

+30
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"context"
99
"encoding/hex"
1010
"fmt"
11+
"mime"
12+
"net/mail"
1113
"net/url"
1214
"path/filepath"
1315
"regexp"
@@ -413,6 +415,34 @@ func (u *User) DisplayName() string {
413415
return u.Name
414416
}
415417

418+
var emailToReplacer = strings.NewReplacer(
419+
"\n", "",
420+
"\r", "",
421+
"<", "",
422+
">", "",
423+
",", "",
424+
":", "",
425+
";", "",
426+
)
427+
428+
// EmailTo returns a string suitable to be put into a e-mail `To:` header.
429+
func (u *User) EmailTo() string {
430+
sanitizedDisplayName := emailToReplacer.Replace(u.DisplayName())
431+
432+
// should be an edge case but nice to have
433+
if sanitizedDisplayName == u.Email {
434+
return u.Email
435+
}
436+
437+
to := fmt.Sprintf("%s <%s>", sanitizedDisplayName, u.Email)
438+
add, err := mail.ParseAddress(to)
439+
if err != nil {
440+
return u.Email
441+
}
442+
443+
return fmt.Sprintf("%s <%s>", mime.QEncoding.Encode("utf-8", add.Name), add.Address)
444+
}
445+
416446
// GetDisplayName returns full name if it's not empty and DEFAULT_SHOW_FULL_NAME is set,
417447
// returns username otherwise.
418448
func (u *User) GetDisplayName() string {

models/user/user_test.go

+23
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,29 @@ func Test_NormalizeUserFromEmail(t *testing.T) {
529529
}
530530
}
531531

532+
func TestEmailTo(t *testing.T) {
533+
testCases := []struct {
534+
fullName string
535+
mail string
536+
result string
537+
}{
538+
{"Awareness Hub", "[email protected]", "Awareness Hub <[email protected]>"},
539+
540+
{"Hi Its <Mee>", "[email protected]", "Hi Its Mee <[email protected]>"},
541+
{"Sinéad.O'Connor", "[email protected]", "=?utf-8?q?Sin=C3=A9ad.O'Connor?= <[email protected]>"},
542+
{"Æsir", "[email protected]", "=?utf-8?q?=C3=86sir?= <[email protected]>"},
543+
{"new😀user", "[email protected]", "=?utf-8?q?new=F0=9F=98=80user?= <[email protected]>"},
544+
{`"quoted"`, "[email protected]", "quoted <[email protected]>"},
545+
}
546+
547+
for _, testCase := range testCases {
548+
t.Run(testCase.result, func(t *testing.T) {
549+
testUser := &user_model.User{FullName: testCase.fullName, Email: testCase.mail}
550+
assert.EqualValues(t, testCase.result, testUser.EmailTo())
551+
})
552+
}
553+
}
554+
532555
func TestDisabledUserFeatures(t *testing.T) {
533556
assert.NoError(t, unittest.PrepareTestDatabase())
534557

services/mailer/mail.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func sendUserMail(language string, u *user_model.User, tpl base.TplName, code, s
8282
return
8383
}
8484

85-
msg := NewMessage(u.Email, subject, content.String())
85+
msg := NewMessage(u.EmailTo(), subject, content.String())
8686
msg.Info = fmt.Sprintf("UID: %d, %s", u.ID, info)
8787

8888
SendAsync(msg)
@@ -158,7 +158,7 @@ func SendRegisterNotifyMail(u *user_model.User) {
158158
return
159159
}
160160

161-
msg := NewMessage(u.Email, locale.TrString("mail.register_notify"), content.String())
161+
msg := NewMessage(u.EmailTo(), locale.TrString("mail.register_notify"), content.String())
162162
msg.Info = fmt.Sprintf("UID: %d, registration notify", u.ID)
163163

164164
SendAsync(msg)
@@ -189,7 +189,7 @@ func SendCollaboratorMail(u, doer *user_model.User, repo *repo_model.Repository)
189189
return
190190
}
191191

192-
msg := NewMessage(u.Email, subject, content.String())
192+
msg := NewMessage(u.EmailTo(), subject, content.String())
193193
msg.Info = fmt.Sprintf("UID: %d, add collaborator", u.ID)
194194

195195
SendAsync(msg)

services/mailer/mail_release.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ func MailNewRelease(ctx context.Context, rel *repo_model.Release) {
4040
return
4141
}
4242

43-
langMap := make(map[string][]string)
43+
langMap := make(map[string][]*user_model.User)
4444
for _, user := range recipients {
4545
if user.ID != rel.PublisherID {
46-
langMap[user.Language] = append(langMap[user.Language], user.Email)
46+
langMap[user.Language] = append(langMap[user.Language], user)
4747
}
4848
}
4949

@@ -52,7 +52,7 @@ func MailNewRelease(ctx context.Context, rel *repo_model.Release) {
5252
}
5353
}
5454

55-
func mailNewRelease(ctx context.Context, lang string, tos []string, rel *repo_model.Release) {
55+
func mailNewRelease(ctx context.Context, lang string, tos []*user_model.User, rel *repo_model.Release) {
5656
locale := translation.NewLocale(lang)
5757

5858
var err error
@@ -89,7 +89,7 @@ func mailNewRelease(ctx context.Context, lang string, tos []string, rel *repo_mo
8989
publisherName := rel.Publisher.DisplayName()
9090
msgID := generateMessageIDForRelease(rel)
9191
for _, to := range tos {
92-
msg := NewMessageFrom(to, publisherName, setting.MailService.FromEmail, subject, mailBody.String())
92+
msg := NewMessageFrom(to.EmailTo(), publisherName, setting.MailService.FromEmail, subject, mailBody.String())
9393
msg.Info = subject
9494
msg.SetHeader("Message-ID", msgID)
9595
msgs = append(msgs, msg)

services/mailer/mail_repo.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,13 @@ func SendRepoTransferNotifyMail(ctx context.Context, doer, newOwner *user_model.
2828
return err
2929
}
3030

31-
langMap := make(map[string][]string)
31+
langMap := make(map[string][]*user_model.User)
3232
for _, user := range users {
3333
if !user.IsActive {
3434
// don't send emails to inactive users
3535
continue
3636
}
37-
langMap[user.Language] = append(langMap[user.Language], user.Email)
37+
langMap[user.Language] = append(langMap[user.Language], user)
3838
}
3939

4040
for lang, tos := range langMap {
@@ -46,11 +46,11 @@ func SendRepoTransferNotifyMail(ctx context.Context, doer, newOwner *user_model.
4646
return nil
4747
}
4848

49-
return sendRepoTransferNotifyMailPerLang(newOwner.Language, newOwner, doer, []string{newOwner.Email}, repo)
49+
return sendRepoTransferNotifyMailPerLang(newOwner.Language, newOwner, doer, []*user_model.User{newOwner}, repo)
5050
}
5151

5252
// sendRepoTransferNotifyMail triggers a notification e-mail when a pending repository transfer was created for each language
53-
func sendRepoTransferNotifyMailPerLang(lang string, newOwner, doer *user_model.User, emails []string, repo *repo_model.Repository) error {
53+
func sendRepoTransferNotifyMailPerLang(lang string, newOwner, doer *user_model.User, emailTos []*user_model.User, repo *repo_model.Repository) error {
5454
var (
5555
locale = translation.NewLocale(lang)
5656
content bytes.Buffer
@@ -78,8 +78,8 @@ func sendRepoTransferNotifyMailPerLang(lang string, newOwner, doer *user_model.U
7878
return err
7979
}
8080

81-
for _, to := range emails {
82-
msg := NewMessage(to, subject, content.String())
81+
for _, to := range emailTos {
82+
msg := NewMessage(to.EmailTo(), subject, content.String())
8383
msg.Info = fmt.Sprintf("UID: %d, repository pending transfer notification", newOwner.ID)
8484

8585
SendAsync(msg)

0 commit comments

Comments
 (0)