From 31e77bac08eedc3d9bf64274b341466f66acf548 Mon Sep 17 00:00:00 2001 From: Peter Sanchez Date: Thu, 18 Nov 2021 05:45:13 -0800 Subject: [PATCH] Add support for mail setups that don't support TLS or Auth --- email/send.go | 107 ++++++++++++++++++++++++++++++++++++++++++++------ go.mod | 2 +- go.sum | 4 ++ 3 files changed, 99 insertions(+), 14 deletions(-) diff --git a/email/send.go b/email/send.go index df0f20afcd3a4640ee36828a435806c3552b8577..6171dcf2c292f24c1f30cb6134e1de1ef0e4638a 100644 --- a/email/send.go +++ b/email/send.go @@ -14,33 +14,114 @@ import ( "git.sr.ht/~sircmpwn/core-go/config" ) -// Sends an email. Blocks until it's sent or an error occurs. -func Send(ctx context.Context, msg io.Reader, rcpts []string) error { +type mailConfig struct { + host string + port int + from string + + enctype string + authtype string + + user string + pass string +} + +func mailSetup(ctx context.Context) (*smtp.Client, *mail.Address, error) { conf := config.ForContext(ctx) + mailconf := &mailConfig{enctype: "starttls", authtype: "plain"} + var err error portStr, ok := conf.Get("mail", "smtp-port") if !ok { panic(fmt.Errorf("[mail]smtp-port unset")) } - port, err := strconv.Atoi(portStr) + mailconf.port, err = strconv.Atoi(portStr) if err != nil { panic(fmt.Errorf("Unable to parse [mail]smtp-port (must be integer)")) } - host, ok1 := conf.Get("mail", "smtp-host") - user, ok2 := conf.Get("mail", "smtp-user") - pass, ok3 := conf.Get("mail", "smtp-password") - from, ok4 := conf.Get("mail", "smtp-from") - if !ok1 || !ok2 || !ok3 || !ok4 { - panic(fmt.Errorf("Missing SMTP configuration options")) + if mailconf.host, ok = conf.Get("mail", "smtp-host"); !ok { + panic(fmt.Errorf("Missing SMTP configuration options [smtp-host]")) + } + if mailconf.from, ok = conf.Get("mail", "smtp-from"); !ok { + panic(fmt.Errorf("Missing SMTP configuration options [smtp-from]")) } - sender, err := mail.ParseAddress(from) + sender, err := mail.ParseAddress(mailconf.from) if err != nil { panic(err) } - auth := sasl.NewPlainClient("", user, pass) - return smtp.SendMail(fmt.Sprintf("%s:%d", host, port), - auth, sender.Address, rcpts, msg) + if enctype, ok := conf.Get("mail", "smtp-encryption"); ok { + switch enctype { + case "starttls", "tls", "insecure": + mailconf.enctype = enctype + default: + panic(fmt.Errorf("Invalid SMTP configuration value for [smtp-encryption]")) + } + } + + if authtype, ok := conf.Get("mail", "smtp-auth"); ok { + switch authtype { + case "none", "plain": + mailconf.enctype = authtype + default: + panic(fmt.Errorf("Invalid SMTP configuration value for [smtp-auth]")) + } + } + if mailconf.authtype == "plain" { + if mailconf.user, ok = conf.Get("mail", "smtp-user"); !ok { + panic(fmt.Errorf("Missing SMTP configuration options [smtp-user]")) + } + if mailconf.pass, ok = conf.Get("mail", "smtp-password"); !ok { + panic(fmt.Errorf("Missing SMTP configuration options [smtp-password]")) + } + } + + var c *smtp.Client + addr := fmt.Sprintf("%s:%d", mailconf.host, mailconf.port) + + if mailconf.enctype == "tls" { + c, err = smtp.DialTLS(addr, nil) + } else { + c, err = smtp.Dial(addr) + } + if err != nil { + return nil, nil, err + } + + if err = c.Hello("localhost"); err != nil { + return nil, nil, err + } + + if mailconf.enctype == "starttls" { + if ok, _ := c.Extension("STARTTLS"); !ok { + panic(fmt.Errorf("smtp: server doesn't support STARTTLS")) + } + if err = c.StartTLS(nil); err != nil { + return nil, nil, err + } + } + + if mailconf.authtype == "plain" { + auth := sasl.NewPlainClient("", mailconf.user, mailconf.pass) + if ok, _ := c.Extension("AUTH"); !ok { + panic(fmt.Errorf("smtp: server doesn't support AUTH")) + } + if err = c.Auth(auth); err != nil { + return nil, nil, err + } + } + + return c, sender, nil +} + +// Sends an email. Blocks until it's sent or an error occurs. +func Send(ctx context.Context, msg io.Reader, rcpts []string) error { + c, sender, err := mailSetup(ctx) + if err != nil { + return err + } + defer c.Close() + return c.SendMail(sender.Address, rcpts, msg) } diff --git a/go.mod b/go.mod index c694f8144f80220faf8ed262dd62df0072c08e60..c94fcd4ffa90cadb0340727ea123d7fee92129f5 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/emersion/go-message v0.13.1-0.20201112194930-f77964fe28bd github.com/emersion/go-pgpmail v0.1.0 github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 - github.com/emersion/go-smtp v0.14.0 + github.com/emersion/go-smtp v0.15.1-0.20211103212524-30169acc42e7 github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 // indirect github.com/fernet/fernet-go v0.0.0-20191111064656-eff2850e6001 github.com/go-chi/chi v4.1.2+incompatible diff --git a/go.sum b/go.sum index 38d5e7d5db84c8095d4a09a9bcc2e7046f4348b8..b2481e7defad9656b0f312e3b365754d84a471f4 100644 --- a/go.sum +++ b/go.sum @@ -131,6 +131,10 @@ github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 h1:OJyUGMJTzHTd1X github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= github.com/emersion/go-smtp v0.14.0 h1:RYW203p+EcPjL8Z/ZpT9lZ6iOc8MG1MQzEx1UKEkXlA= github.com/emersion/go-smtp v0.14.0/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ= +github.com/emersion/go-smtp v0.15.0 h1:3+hMGMGrqP/lqd7qoxZc1hTU8LY8gHV9RFGWlqSDmP8= +github.com/emersion/go-smtp v0.15.0/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ= +github.com/emersion/go-smtp v0.15.1-0.20211103212524-30169acc42e7 h1:y2h9HJElyAP5kgYujXAJC1DvlcTgCUhtYr/BlXnlGfs= +github.com/emersion/go-smtp v0.15.1-0.20211103212524-30169acc42e7/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ= github.com/emersion/go-textwrapper v0.0.0-20160606182133-d0e65e56babe h1:40SWqY0zE3qCi6ZrtTf5OUdNm5lDnGnjRSq9GgmeTrg= github.com/emersion/go-textwrapper v0.0.0-20160606182133-d0e65e56babe/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U= github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 h1:IbFBtwoTQyw0fIM5xv1HF+Y+3ZijDR839WMulgxCcUY=