mirror of
https://github.com/axllent/mailpit.git
synced 2025-01-24 03:47:38 +02:00
191 lines
4.6 KiB
Go
191 lines
4.6 KiB
Go
// Package cmd is the sendmail cli
|
|
package cmd
|
|
|
|
/**
|
|
* Bare bones sendmail drop-in replacement borrowed from MailHog
|
|
*
|
|
* It uses a bit of a hack for flag parsing in order to be compatible
|
|
* with the cobra sendmail subcommand, as sendmail uses `-bc` which
|
|
* is not POSIX compatible.
|
|
*
|
|
* The -bs command-line switch causes sendmail to run a single SMTP session in the
|
|
* foreground over its standard input and output, and then exit. The SMTP session
|
|
* is exactly like a network SMTP session. Usually, one or more messages are
|
|
* submitted to sendmail for delivery.
|
|
*/
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"net/mail"
|
|
"net/smtp"
|
|
"os"
|
|
"os/user"
|
|
"strings"
|
|
|
|
"github.com/axllent/mailpit/config"
|
|
"github.com/axllent/mailpit/internal/logger"
|
|
"github.com/reiver/go-telnet"
|
|
flag "github.com/spf13/pflag"
|
|
)
|
|
|
|
var (
|
|
// SMTPAddr address
|
|
SMTPAddr = "localhost:1025"
|
|
// FromAddr email address
|
|
FromAddr string
|
|
|
|
// UseB - used to set from `-bs`
|
|
UseB bool
|
|
// UseS - used to set from `-bs`
|
|
UseS bool
|
|
)
|
|
|
|
func init() {
|
|
host, err := os.Hostname()
|
|
if err != nil {
|
|
host = "localhost"
|
|
}
|
|
|
|
username := "nobody"
|
|
user, err := user.Current()
|
|
if err == nil && user != nil && len(user.Username) > 0 {
|
|
username = user.Username
|
|
}
|
|
|
|
if FromAddr == "" {
|
|
FromAddr = username + "@" + host
|
|
}
|
|
}
|
|
|
|
// Run the Mailpit sendmail replacement.
|
|
func Run() {
|
|
var recipients []string
|
|
|
|
// defaults from envars if provided
|
|
if len(os.Getenv("MP_SENDMAIL_SMTP_ADDR")) > 0 {
|
|
SMTPAddr = os.Getenv("MP_SENDMAIL_SMTP_ADDR")
|
|
}
|
|
if len(os.Getenv("MP_SENDMAIL_FROM")) > 0 {
|
|
FromAddr = os.Getenv("MP_SENDMAIL_FROM")
|
|
}
|
|
|
|
flag.StringVarP(&FromAddr, "from", "f", FromAddr, "SMTP sender")
|
|
flag.StringVarP(&SMTPAddr, "smtp-addr", "S", SMTPAddr, "SMTP server address")
|
|
flag.BoolVarP(&UseB, "long-b", "b", false, "Handle SMTP commands on standard input (use as -bs)")
|
|
flag.BoolVarP(&UseS, "long-s", "s", false, "Handle SMTP commands on standard input (use as -bs)")
|
|
flag.BoolP("verbose", "v", false, "Ignored")
|
|
flag.BoolP("long-i", "i", false, "Ignored")
|
|
flag.BoolP("long-o", "o", false, "Ignored")
|
|
flag.BoolP("long-t", "t", false, "Ignored")
|
|
|
|
// set the default help
|
|
flag.Usage = func() {
|
|
fmt.Println(HelpTemplate(os.Args[0:1]))
|
|
}
|
|
|
|
var showHelp bool
|
|
// avoid 'pflag: help requested' error
|
|
flag.BoolVarP(&showHelp, "help", "h", false, "")
|
|
|
|
flag.Parse()
|
|
|
|
// allow recipients to be passed as an argument
|
|
recipients = flag.Args()
|
|
|
|
// if run via `mailpit sendmail ...` then remove `sendmail` from "recipients"
|
|
if len(recipients) > 0 && recipients[0] == "sendmail" {
|
|
recipients = recipients[1:]
|
|
}
|
|
|
|
if showHelp {
|
|
flag.Usage()
|
|
os.Exit(0)
|
|
}
|
|
|
|
// ensure -bs is set
|
|
if UseB && !UseS || !UseB && UseS {
|
|
fmt.Printf("error: use -bs")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// handles `sendmail -bs`
|
|
if UseB && UseS {
|
|
var caller telnet.Caller = telnet.StandardCaller
|
|
|
|
// telnet directly to SMTP
|
|
if err := telnet.DialToAndCall(SMTPAddr, caller); err != nil {
|
|
fmt.Println(err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
body, err := io.ReadAll(os.Stdin)
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "error reading stdin")
|
|
os.Exit(11)
|
|
}
|
|
|
|
msg, err := mail.ReadMessage(bytes.NewReader(body))
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "error parsing message body: %si\n", err)
|
|
os.Exit(11)
|
|
}
|
|
|
|
addresses := []string{}
|
|
|
|
if len(recipients) > 0 {
|
|
addresses = recipients
|
|
} else {
|
|
// get all recipients in To, Cc and Bcc
|
|
if to, err := msg.Header.AddressList("To"); err == nil {
|
|
for _, a := range to {
|
|
addresses = append(addresses, a.Address)
|
|
}
|
|
}
|
|
if cc, err := msg.Header.AddressList("Cc"); err == nil {
|
|
for _, a := range cc {
|
|
addresses = append(addresses, a.Address)
|
|
}
|
|
}
|
|
if bcc, err := msg.Header.AddressList("Bcc"); err == nil {
|
|
for _, a := range bcc {
|
|
addresses = append(addresses, a.Address)
|
|
}
|
|
}
|
|
}
|
|
|
|
from, err := mail.ParseAddress(FromAddr)
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "invalid from address")
|
|
os.Exit(11)
|
|
}
|
|
|
|
err = smtp.SendMail(SMTPAddr, nil, from.Address, addresses, body)
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "error sending mail")
|
|
logger.Log().Fatal(err)
|
|
}
|
|
}
|
|
|
|
// HelpTemplate returns a string of the help
|
|
func HelpTemplate(args []string) string {
|
|
return fmt.Sprintf(`A sendmail command replacement for Mailpit (%s)
|
|
|
|
Usage: %s [flags] [recipients] < message
|
|
|
|
See: https://github.com/axllent/mailpit
|
|
|
|
Flags:
|
|
-S string SMTP server address (default "localhost:1025")
|
|
-f string Set the envelope sender address (default "%s")
|
|
-bs Handle SMTP commands on standard input
|
|
-t Ignored
|
|
-i Ignored
|
|
-o Ignored
|
|
-v Ignored
|
|
`, config.Version, strings.Join(args, " "), FromAddr)
|
|
}
|