Added error to Options so we can check inputs

This commit is contained in:
Winni Neessen 2022-03-15 21:10:03 +01:00
parent eb6120f475
commit 4585a53be8
Signed by: wneessen
GPG key ID: 5F3AF39B820C119D

View file

@ -23,6 +23,10 @@ const (
// DefaultTLSPolicy is the default STARTTLS policy // DefaultTLSPolicy is the default STARTTLS policy
DefaultTLSPolicy = TLSMandatory DefaultTLSPolicy = TLSMandatory
// DefaultTLSMinVersion is the minimum TLS version required for the connection
// Nowadays TLS1.2 should be the sane default
DefaultTLSMinVersion = tls.VersionTLS12
) )
// Client is the SMTP client struct // Client is the SMTP client struct
@ -71,9 +75,21 @@ type Client struct {
} }
// Option returns a function that can be used for grouping Client options // Option returns a function that can be used for grouping Client options
type Option func(*Client) type Option func(*Client) error
var ( var (
// ErrInvalidPort should be used if a port is specified that is not valid
ErrInvalidPort = errors.New("invalid port number")
// ErrInvalidTimeout should be used if a timeout is set that is zero or negative
ErrInvalidTimeout = errors.New("timeout cannot be zero or negative")
// ErrInvalidHELO should be used if an empty HELO sting is provided
ErrInvalidHELO = errors.New("invalid HELO/EHLO value - must not be empty")
// ErrInvalidTLSConfig should be used if an empty tls.Config is provided
ErrInvalidTLSConfig = errors.New("invalid TLS config")
// ErrNoHostname should be used if a Client has no hostname set // ErrNoHostname should be used if a Client has no hostname set
ErrNoHostname = errors.New("hostname for client cannot be empty") ErrNoHostname = errors.New("hostname for client cannot be empty")
@ -95,7 +111,7 @@ func NewClient(h string, o ...Option) (*Client, error) {
cto: DefaultTimeout, cto: DefaultTimeout,
host: h, host: h,
port: DefaultPort, port: DefaultPort,
tlsconfig: &tls.Config{ServerName: h}, tlsconfig: &tls.Config{ServerName: h, MinVersion: DefaultTLSMinVersion},
tlspolicy: DefaultTLSPolicy, tlspolicy: DefaultTLSPolicy,
} }
@ -109,7 +125,9 @@ func NewClient(h string, o ...Option) (*Client, error) {
if co == nil { if co == nil {
continue continue
} }
co(c) if err := co(c); err != nil {
return c, fmt.Errorf("failed to apply option: %w", err)
}
} }
// Some settings in a Client cannot be empty/unset // Some settings in a Client cannot be empty/unset
@ -123,71 +141,93 @@ func NewClient(h string, o ...Option) (*Client, error) {
// WithPort overrides the default connection port // WithPort overrides the default connection port
func WithPort(p int) Option { func WithPort(p int) Option {
return func(c *Client) { return func(c *Client) error {
if p < 1 || p > 65535 {
return ErrInvalidPort
}
c.port = p c.port = p
return nil
} }
} }
// WithTimeout overrides the default connection timeout // WithTimeout overrides the default connection timeout
func WithTimeout(t time.Duration) Option { func WithTimeout(t time.Duration) Option {
return func(c *Client) { return func(c *Client) error {
if t <= 0 {
return ErrInvalidTimeout
}
c.cto = t c.cto = t
return nil
} }
} }
// WithSSL tells the client to use a SSL/TLS connection // WithSSL tells the client to use a SSL/TLS connection
func WithSSL() Option { func WithSSL() Option {
return func(c *Client) { return func(c *Client) error {
c.ssl = true c.ssl = true
return nil
} }
} }
// WithHELO tells the client to use the provided string as HELO/EHLO greeting host // WithHELO tells the client to use the provided string as HELO/EHLO greeting host
func WithHELO(h string) Option { func WithHELO(h string) Option {
return func(c *Client) { return func(c *Client) error {
if h == "" {
return ErrInvalidHELO
}
c.helo = h c.helo = h
return nil
} }
} }
// WithTLSPolicy tells the client to use the provided TLSPolicy // WithTLSPolicy tells the client to use the provided TLSPolicy
func WithTLSPolicy(p TLSPolicy) Option { func WithTLSPolicy(p TLSPolicy) Option {
return func(c *Client) { return func(c *Client) error {
c.tlspolicy = p c.tlspolicy = p
return nil
} }
} }
// WithTLSConfig tells the client to use the provided *tls.Config // WithTLSConfig tells the client to use the provided *tls.Config
func WithTLSConfig(co *tls.Config) Option { func WithTLSConfig(co *tls.Config) Option {
return func(c *Client) { return func(c *Client) error {
if co == nil {
return ErrInvalidTLSConfig
}
c.tlsconfig = co c.tlsconfig = co
return nil
} }
} }
// WithSMTPAuth tells the client to use the provided SMTPAuthType for authentication // WithSMTPAuth tells the client to use the provided SMTPAuthType for authentication
func WithSMTPAuth(t SMTPAuthType) Option { func WithSMTPAuth(t SMTPAuthType) Option {
return func(c *Client) { return func(c *Client) error {
c.satype = t c.satype = t
return nil
} }
} }
// WithSMTPAuthCustom tells the client to use the provided smtp.Auth for SMTP authentication // WithSMTPAuthCustom tells the client to use the provided smtp.Auth for SMTP authentication
func WithSMTPAuthCustom(a smtp.Auth) Option { func WithSMTPAuthCustom(a smtp.Auth) Option {
return func(c *Client) { return func(c *Client) error {
c.sa = a c.sa = a
return nil
} }
} }
// WithUsername tells the client to use the provided string as username for authentication // WithUsername tells the client to use the provided string as username for authentication
func WithUsername(u string) Option { func WithUsername(u string) Option {
return func(c *Client) { return func(c *Client) error {
c.user = u c.user = u
return nil
} }
} }
// WithPassword tells the client to use the provided string as password/secret for authentication // WithPassword tells the client to use the provided string as password/secret for authentication
func WithPassword(p string) Option { func WithPassword(p string) Option {
return func(c *Client) { return func(c *Client) error {
c.pass = p c.pass = p
return nil
} }
} }