Add List-Unsubscribe header to opt-in confirmation mails. Closes #2224.

This commit is contained in:
Kailash Nadh 2025-01-19 11:41:17 +05:30
parent 2e8a5ace1d
commit e8fd12bddf
3 changed files with 19 additions and 4 deletions

View file

@ -76,6 +76,7 @@ type constants struct {
AllowExport bool `koanf:"allow_export"` AllowExport bool `koanf:"allow_export"`
AllowWipe bool `koanf:"allow_wipe"` AllowWipe bool `koanf:"allow_wipe"`
RecordOptinIP bool `koanf:"record_optin_ip"` RecordOptinIP bool `koanf:"record_optin_ip"`
UnsubHeader bool `koanf:"unsubscribe_header"`
Exportable map[string]bool `koanf:"-"` Exportable map[string]bool `koanf:"-"`
DomainBlocklist []string `koanf:"-"` DomainBlocklist []string `koanf:"-"`
} `koanf:"privacy"` } `koanf:"privacy"`
@ -483,7 +484,7 @@ func initI18n(lang string, fs stuffbin.FileSystem) *i18n.I18n {
// initCampaignManager initializes the campaign manager. // initCampaignManager initializes the campaign manager.
func initCampaignManager(q *models.Queries, cs *constants, app *App) *manager.Manager { func initCampaignManager(q *models.Queries, cs *constants, app *App) *manager.Manager {
campNotifCB := func(subject string, data interface{}) error { campNotifCB := func(subject string, data interface{}) error {
return app.sendNotification(cs.NotifyEmails, subject, notifTplCampaign, data) return app.sendNotification(cs.NotifyEmails, subject, notifTplCampaign, data, nil)
} }
if ko.Bool("passive") { if ko.Bool("passive") {
@ -541,7 +542,7 @@ func initImporter(q *models.Queries, db *sqlx.DB, core *core.Core, app *App) *su
// Refresh cached subscriber counts and stats. // Refresh cached subscriber counts and stats.
core.RefreshMatViews(true) core.RefreshMatViews(true)
app.sendNotification(app.constants.NotifyEmails, subject, notifTplImport, data) app.sendNotification(app.constants.NotifyEmails, subject, notifTplImport, data, nil)
return nil return nil
}, },
}, db.DB, app.i18n) }, db.DB, app.i18n)

View file

@ -2,6 +2,7 @@ package main
import ( import (
"bytes" "bytes"
"net/textproto"
"regexp" "regexp"
"strings" "strings"
@ -27,7 +28,7 @@ type notifData struct {
} }
// sendNotification sends out an e-mail notification to admins. // sendNotification sends out an e-mail notification to admins.
func (app *App) sendNotification(toEmails []string, subject, tplName string, data interface{}) error { func (app *App) sendNotification(toEmails []string, subject, tplName string, data interface{}, headers textproto.MIMEHeader) error {
if len(toEmails) == 0 { if len(toEmails) == 0 {
return nil return nil
} }
@ -48,6 +49,7 @@ func (app *App) sendNotification(toEmails []string, subject, tplName string, dat
m.Subject = subject m.Subject = subject
m.Body = body m.Body = body
m.Messenger = emailMsgr m.Messenger = emailMsgr
m.Headers = headers
if err := app.manager.PushMessage(m); err != nil { if err := app.manager.PushMessage(m); err != nil {
app.log.Printf("error sending admin notification (%s): %v", subject, err) app.log.Printf("error sending admin notification (%s): %v", subject, err)
return err return err

View file

@ -6,6 +6,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"net/textproto"
"net/url" "net/url"
"strconv" "strconv"
"strings" "strings"
@ -663,8 +664,19 @@ func sendOptinConfirmationHook(app *App) func(sub models.Subscriber, listIDs []i
out.OptinURL = fmt.Sprintf(app.constants.OptinURL, sub.UUID, qListIDs.Encode()) out.OptinURL = fmt.Sprintf(app.constants.OptinURL, sub.UUID, qListIDs.Encode())
out.UnsubURL = fmt.Sprintf(app.constants.UnsubURL, dummyUUID, sub.UUID) out.UnsubURL = fmt.Sprintf(app.constants.UnsubURL, dummyUUID, sub.UUID)
// Unsub headers.
h := textproto.MIMEHeader{}
h.Set(models.EmailHeaderSubscriberUUID, sub.UUID)
// Attach List-Unsubscribe headers?
if app.constants.Privacy.UnsubHeader {
unsubURL := fmt.Sprintf(app.constants.UnsubURL, dummyUUID, sub.UUID)
h.Set("List-Unsubscribe-Post", "List-Unsubscribe=One-Click")
h.Set("List-Unsubscribe", `<`+unsubURL+`>`)
}
// Send the e-mail. // Send the e-mail.
if err := app.sendNotification([]string{sub.Email}, app.i18n.T("subscribers.optinSubject"), notifSubscriberOptin, out); err != nil { if err := app.sendNotification([]string{sub.Email}, app.i18n.T("subscribers.optinSubject"), notifSubscriberOptin, out, h); err != nil {
app.log.Printf("error sending opt-in e-mail for subscriber %d (%s): %s", sub.ID, sub.UUID, err) app.log.Printf("error sending opt-in e-mail for subscriber %d (%s): %s", sub.ID, sub.UUID, err)
return 0, err return 0, err
} }