diff --git a/Dockerfile b/Dockerfile index 2051556..62cb80f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ ## Build first -FROM golang:alpine as builder +FROM golang:latest as builder RUN mkdir /builddir ADD . /builddir/ WORKDIR /builddir diff --git a/api/sendform.go b/api/sendform.go index 3a34801..c200900 100644 --- a/api/sendform.go +++ b/api/sendform.go @@ -2,13 +2,12 @@ package api import ( "fmt" + "github.com/labstack/echo/v4" + "github.com/wneessen/go-mail" "github.com/wneessen/js-mailer/form" "github.com/wneessen/js-mailer/response" "net/http" "time" - - "github.com/go-mail/mail" - "github.com/labstack/echo/v4" ) // SentSuccessful represents confirmation JSON structure for a successfully sent message @@ -55,14 +54,32 @@ func (r *Route) SendForm(c echo.Context) error { } // Compose the mail message - mailMsg := mail.NewMessage() - mailMsg.SetHeader("From", sr.FormObj.Sender) - mailMsg.SetHeader("To", sr.FormObj.Recipients...) - mailMsg.SetHeader("Subject", sr.FormObj.Content.Subject) + mailMsg := mail.NewMsg() + if err := mailMsg.From(sr.FormObj.Sender); err != nil { + c.Logger().Errorf("failed to set FROM header: %s", err) + return echo.NewHTTPError(http.StatusInternalServerError, &response.ErrorObj{ + Message: "could not set MAIL FROM header", + Data: err.Error(), + }) + } + if err := mailMsg.To(sr.FormObj.Recipients...); err != nil { + c.Logger().Errorf("failed to set TO header: %s", err) + return echo.NewHTTPError(http.StatusInternalServerError, &response.ErrorObj{ + Message: "could not set RCPT TO header", + Data: err.Error(), + }) + } + mailMsg.Subject(sr.FormObj.Content.Subject) if sr.FormObj.ReplyTo.Field != "" { sf := c.FormValue(sr.FormObj.ReplyTo.Field) if sf != "" { - mailMsg.SetHeader("Reply-To", sf) + if err := mailMsg.ReplyTo(sf); err != nil { + c.Logger().Errorf("failed to set REPLY-TO header: %s", err) + return echo.NewHTTPError(http.StatusInternalServerError, &response.ErrorObj{ + Message: "could not set REPLY-TO header", + Data: err.Error(), + }) + } } } @@ -72,24 +89,18 @@ func (r *Route) SendForm(c echo.Context) error { mailBody = fmt.Sprintf("%s\n* %s => %s", mailBody, k, v) } } - mailMsg.SetBody("text/plain", mailBody) + mailMsg.SetBodyString(mail.TypeTextPlain, mailBody) // Send the mail message - mailDailer := GetMailDailer(sr.FormObj) - mailSender, err := mailDailer.Dial() + mc, err := GetMailClient(sr.FormObj) if err != nil { - c.Logger().Errorf("Could not connect to configured mail server: %s", err) + c.Logger().Errorf("Could not create new mail client: %s", err) return echo.NewHTTPError(http.StatusInternalServerError, &response.ErrorObj{ - Message: "could not connect to configured mail server", + Message: "Cloud not create new mail client", Data: err.Error(), }) } - defer func() { - if err := mailSender.Close(); err != nil { - c.Logger().Errorf("Failed to close mail server connection: %s", err) - } - }() - if err := mail.Send(mailSender, mailMsg); err != nil { + if err := mc.DialAndSend(mailMsg); err != nil { c.Logger().Errorf("Could not send mail message: %s", err) return echo.NewHTTPError(http.StatusInternalServerError, &response.ErrorObj{ Message: "could not send mail message", @@ -111,36 +122,41 @@ func (r *Route) SendForm(c echo.Context) error { // SendFormConfirmation sends out a confirmation mail if requested in the form func SendFormConfirmation(f *form.Form, r string) error { - mailMsg := mail.NewMessage() - mailMsg.SetHeader("From", f.Sender) - mailMsg.SetHeader("To", r) - mailMsg.SetHeader("Subject", f.Confirmation.Subject) - mailMsg.SetBody("text/plain", f.Confirmation.Content) - mailDailer := GetMailDailer(f) - mailSender, err := mailDailer.Dial() + mailMsg := mail.NewMsg() + if err := mailMsg.From(f.Sender); err != nil { + return fmt.Errorf("failed to set FROM header: %w", err) + } + if err := mailMsg.To(r); err != nil { + return fmt.Errorf("failed to set TO header: %w", err) + } + mailMsg.Subject(f.Confirmation.Subject) + mailMsg.SetBodyString(mail.TypeTextPlain, f.Confirmation.Content) + mc, err := GetMailClient(f) if err != nil { - return fmt.Errorf("could not connect to configured mail server: %w", err) + return fmt.Errorf("failed to create mail client: %w", err) } - if err := mail.Send(mailSender, mailMsg); err != nil { + if err := mc.DialAndSend(mailMsg); err != nil { return fmt.Errorf("could not send confirmation mail message: %w", err) } - if err := mailSender.Close(); err != nil { - return fmt.Errorf("failed to close mail server connection: %w", err) - } return nil } -// GetMailDailer returns a new mail dailer object based on the form configuration -func GetMailDailer(f *form.Form) *mail.Dialer { +// GetMailClient returns a new mail dailer object based on the form configuration +func GetMailClient(f *form.Form) (*mail.Client, error) { var serverTimeout time.Duration serverTimeout, err := time.ParseDuration(f.Server.Timeout) if err != nil { serverTimeout = time.Second * 5 } - mailDailer := mail.NewDialer(f.Server.Host, f.Server.Port, f.Server.Username, f.Server.Password) - mailDailer.Timeout = serverTimeout - if f.Server.ForceTLS { - mailDailer.StartTLSPolicy = mail.MandatoryStartTLS + mc, err := mail.NewClient(f.Server.Host, mail.WithPort(f.Server.Port), + mail.WithUsername(f.Server.Username), mail.WithPassword(f.Server.Password), + mail.WithSMTPAuth(mail.SMTPAuthPlain), mail.WithTimeout(serverTimeout)) + + if err != nil { + return mc, err + } + if !f.Server.ForceTLS { + mc.SetTLSPolicy(mail.TLSOpportunistic) } - return mailDailer + return mc, nil } diff --git a/go.mod b/go.mod index 0c57642..5ae2dfa 100644 --- a/go.mod +++ b/go.mod @@ -5,10 +5,8 @@ go 1.16 require ( github.com/ReneKroon/ttlcache/v2 v2.11.0 github.com/cyphar/filepath-securejoin v0.2.3 - github.com/go-mail/mail v2.3.1+incompatible github.com/kkyr/fig v0.3.0 github.com/labstack/echo/v4 v4.7.2 github.com/labstack/gommon v0.3.1 - gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect - gopkg.in/mail.v2 v2.3.1 // indirect + github.com/wneessen/go-mail v0.1.8 ) diff --git a/go.sum b/go.sum index 67b9775..ff837bd 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,6 @@ github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxG github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-mail/mail v2.3.1+incompatible h1:UzNOn0k5lpfVtO31cK3hn6I4VEVGhe3lX8AJBAxXExM= -github.com/go-mail/mail v2.3.1+incompatible/go.mod h1:VPWjmmNyRsWXQZHVHT3g0YbIINUkSmuKOiLIDkWbL6M= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/kkyr/fig v0.3.0 h1:5bd1amYKp/gsK2bGEUJYzcCrQPKOZp6HZD9K21v9Guo= @@ -38,6 +36,8 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/wneessen/go-mail v0.1.8 h1:HKUs2ND8MozQ73nPwdeol0kJ7yweg6Q3UqMBAUrBzeU= +github.com/wneessen/go-mail v0.1.8/go.mod h1:HPX01Zc96mccqd9FY0RlZ/SpyAhVu+TrFfKjiBm0ffQ= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= @@ -90,13 +90,9 @@ golang.org/x/tools v0.0.0-20210112230658-8b4aab62c064/go.mod h1:emZCQorbCU4vsT4f golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= -gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/mail.v2 v2.3.1 h1:WYFn/oANrAGP2C0dcV6/pbkPzv8yGzqTjPmTeO7qoXk= -gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/server/server.go b/server/server.go index c8896a6..e81c1a6 100644 --- a/server/server.go +++ b/server/server.go @@ -17,7 +17,7 @@ import ( ) // VERSION is the global version string contstant -const VERSION = "0.2.3b" +const VERSION = "0.2.4" // Srv represents the server object type Srv struct {