// 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) }