Skip to content

Commit 54da622

Browse files
author
Winni Neessen
committed
Added WriteToSendmail()/WriteToSendmailWithContext() to allow sending mails via local sendmail binary
1 parent 3e43819 commit 54da622

File tree

1 file changed

+64
-0
lines changed

1 file changed

+64
-0
lines changed

msg.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
package mail
22

33
import (
4+
"bufio"
45
"bytes"
6+
"context"
57
"errors"
68
"fmt"
79
"io"
810
"math/rand"
911
"mime"
1012
"net/mail"
1113
"os"
14+
"os/exec"
1215
"path/filepath"
1316
"time"
1417
)
@@ -54,6 +57,9 @@ type Msg struct {
5457
embeds []*File
5558
}
5659

60+
// SendmailPath is the default system path to the sendmail binary
61+
const SendmailPath = "/usr/bin/sendmail"
62+
5763
// MsgOption returns a function that can be used for grouping Msg options
5864
type MsgOption func(*Msg)
5965

@@ -450,6 +456,64 @@ func (m *Msg) appendFile(c []*File, f *File, o ...FileOption) []*File {
450456
return append(c, f)
451457
}
452458

459+
// WriteToSendmail returns WriteToSendmailWithContext with a default timeout of 5 seconds
460+
func (m *Msg) WriteToSendmail() error {
461+
tctx, tcfn := context.WithTimeout(context.Background(), time.Second*5)
462+
defer tcfn()
463+
return m.WriteToSendmailWithContext(tctx, SendmailPath)
464+
}
465+
466+
// WriteToSendmailWithContext opens an pipe to the local sendmail binary and tries to send the
467+
// mail though that. It takes a context.Context, the path to the sendmail binary and additional
468+
// arguments for the sendmail binary as parameters
469+
func (m *Msg) WriteToSendmailWithContext(ctx context.Context, sp string, a ...string) error {
470+
ec := exec.CommandContext(ctx, sp)
471+
ec.Args = append(ec.Args, "-oi", "-t")
472+
ec.Args = append(ec.Args, a...)
473+
474+
var ebuf bytes.Buffer
475+
ec.Stderr = &ebuf
476+
ses := bufio.NewScanner(&ebuf)
477+
478+
si, err := ec.StdinPipe()
479+
if err != nil {
480+
return fmt.Errorf("failed to set STDIN pipe: %w", err)
481+
}
482+
483+
// Start the execution and write to STDIN
484+
if err := ec.Start(); err != nil {
485+
return fmt.Errorf("could not start sendmail execution: %w", err)
486+
}
487+
_, err = m.Write(si)
488+
_, err = si.Write([]byte(".\r\n"))
489+
if err != nil {
490+
_ = si.Close()
491+
return fmt.Errorf("failed to write mail to buffer: %w", err)
492+
}
493+
if err := si.Close(); err != nil {
494+
return fmt.Errorf("failed to close STDIN pipe: %w", err)
495+
}
496+
497+
// Read the stderr buffer for possible errors
498+
var serr string
499+
for ses.Scan() {
500+
serr += ses.Text()
501+
}
502+
if err := ses.Err(); err != nil {
503+
return fmt.Errorf("failed to read STDERR pipe: %w", err)
504+
}
505+
if serr != "" {
506+
return fmt.Errorf("sendmail execution failed: %s", serr)
507+
}
508+
509+
// Wait for completion or cancellation of the sendmail executable
510+
if err := ec.Wait(); err != nil {
511+
return fmt.Errorf("sendmail command execution failed: %w", err)
512+
}
513+
514+
return nil
515+
}
516+
453517
// encodeString encodes a string based on the configured message encoder and the corresponding
454518
// charset for the Msg
455519
func (m *Msg) encodeString(s string) string {

0 commit comments

Comments
 (0)