Skip to content

Commit

Permalink
smtp: add option to request DSN for successful deliveries
Browse files Browse the repository at this point in the history
In some cases it can be desirable to receive a success notification for
a message from the server. This currently only works on SMTP.

Changelog-added: Add option to request full DSN for SMTP sent messages.
Signed-off-by: Moritz Poldrack <[email protected]>
Acked-by: Robin Jarry <[email protected]>
  • Loading branch information
mpldr authored and rjarry committed Mar 8, 2025
1 parent 03061fe commit 1d6eb2d
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 9 deletions.
14 changes: 10 additions & 4 deletions commands/compose/send.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ type Send struct {

CopyToReplied bool `opt:"-r" desc:"Save sent message to current folder."`
NoCopyToReplied bool `opt:"-R" desc:"Do not save sent message to current folder."`

RequestDSN bool `opt:"--dsn" desc:"Request full delivery status notification (including success)."`
}

func init() {
Expand Down Expand Up @@ -156,7 +158,8 @@ func (s Send) Execute(args []string) error {
if text == "n" || text == "N" {
sendHelper(composer, header, uri, domain,
from, rcpts, tab.Name, s.CopyTo,
s.Archive, copyToReplied)
s.Archive, copyToReplied,
s.RequestDSN)
}
}, func(ctx context.Context, cmd string) ([]opt.Completion, string) {
var comps []opt.Completion
Expand All @@ -171,15 +174,15 @@ func (s Send) Execute(args []string) error {
app.PushPrompt(prompt)
} else {
sendHelper(composer, header, uri, domain, from, rcpts, tab.Name,
s.CopyTo, s.Archive, copyToReplied)
s.CopyTo, s.Archive, copyToReplied, s.RequestDSN)
}

return nil
}

func sendHelper(composer *app.Composer, header *mail.Header, uri *url.URL, domain string,
from *mail.Address, rcpts []*mail.Address, tabName string, copyTo []string,
archive string, copyToReplied bool,
archive string, copyToReplied bool, requestDSN bool,
) {
// we don't want to block the UI thread while we are sending
// so we do everything in a goroutine and hide the composer from the user
Expand All @@ -203,7 +206,10 @@ func sendHelper(composer *app.Composer, header *mail.Header, uri *url.URL, domai
folders = append(folders, composer.Parent().Folder)
}
sender, err := send.NewSender(
composer.Worker(), uri, domain, from, rcpts, folders)
composer.Worker(), uri, domain,
from, rcpts,
folders, requestDSN,
)
if err != nil {
failCh <- errors.Wrap(err, "send:")
return
Expand Down
2 changes: 1 addition & 1 deletion commands/msg/bounce.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ func (b Bounce) Execute(args []string) error {
msg.Envelope.MessageId, addresses)

if sender, err = send.NewSender(acct.Worker(), uri,
domain, config.From, rcpts, nil); err != nil {
domain, config.From, rcpts, nil, false); err != nil {
return
}
defer func() {
Expand Down
4 changes: 2 additions & 2 deletions lib/send/sender.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
func NewSender(
worker *types.Worker, uri *url.URL, domain string,
from *mail.Address, rcpts []*mail.Address,
copyTo []string,
copyTo []string, requestDSN bool,
) (io.WriteCloser, error) {
protocol, auth, err := parseScheme(uri)
if err != nil {
Expand All @@ -29,7 +29,7 @@ func NewSender(

switch protocol {
case "smtp", "smtp+insecure", "smtps":
w, err = newSmtpSender(protocol, auth, uri, domain, from, rcpts)
w, err = newSmtpSender(protocol, auth, uri, domain, from, rcpts, requestDSN)
case "jmap":
w, err = newJmapSender(worker, from, rcpts, copyTo)
case "":
Expand Down
14 changes: 12 additions & 2 deletions lib/send/smtp.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func (s *smtpSender) Close() error {

func newSmtpSender(
protocol string, auth string, uri *url.URL, domain string,
from *mail.Address, rcpts []*mail.Address,
from *mail.Address, rcpts []*mail.Address, requestDSN bool,
) (io.WriteCloser, error) {
var err error
var conn *smtp.Client
Expand Down Expand Up @@ -119,8 +119,18 @@ func newSmtpSender(
conn.Close()
return nil, errors.Wrap(err, "conn.Mail")
}
var rcptOptions *smtp.RcptOptions
if requestDSN {
rcptOptions = &smtp.RcptOptions{
Notify: []smtp.DSNNotify{
smtp.DSNNotifySuccess,
smtp.DSNNotifyDelayed,
smtp.DSNNotifyFailure,
},
}
}
for _, rcpt := range rcpts {
if err := s.conn.Rcpt(rcpt.Address, nil); err != nil {
if err := s.conn.Rcpt(rcpt.Address, rcptOptions); err != nil {
conn.Close()
return nil, errors.Wrap(err, "conn.Rcpt")
}
Expand Down

0 comments on commit 1d6eb2d

Please sign in to comment.