Slow progress

This commit is contained in:
Winni Neessen 2022-03-07 16:24:49 +01:00
parent 2fcc0f59cd
commit b3554a9578
Signed by: wneessen
GPG key ID: 385AC9889632126E
4 changed files with 139 additions and 33 deletions

View file

@ -28,8 +28,11 @@ type Client struct {
// Use SSL for the connection // Use SSL for the connection
ssl bool ssl bool
// Sets the client cto use STARTTTLS for the connection (is disabled when SSL is set) // tlspolicy sets the client to use the provided TLSPolicy for the STARTTLS protocol
starttls bool tlspolicy TLSPolicy
// tlsconfig represents the tls.Config setting for the STARTTLS connection
tlsconfig *tls.Config
// Timeout for the SMTP server connection // Timeout for the SMTP server connection
cto time.Duration cto time.Duration
@ -48,8 +51,8 @@ var (
// 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")
// ErrInvalidHostname should be used if a Client has an invalid hostname set // ErrNoSTARTTLS should be used if the target server does not support the STARTTLS protocol
//ErrInvalidHostname = errors.New("hostname for client is invalid") ErrNoSTARTTLS = errors.New("target host does not support STARTTLS")
) )
// NewClient returns a new Session client object // NewClient returns a new Session client object
@ -58,6 +61,8 @@ func NewClient(h string, o ...Option) (*Client, error) {
host: h, host: h,
port: DefaultPort, port: DefaultPort,
cto: DefaultTimeout, cto: DefaultTimeout,
tlspolicy: TLSMandatory,
tlsconfig: &tls.Config{ServerName: h},
} }
// Set default HELO/EHLO hostname // Set default HELO/EHLO hostname
@ -110,6 +115,36 @@ func WithHELO(h string) Option {
} }
} }
// TLSPolicy returns the currently set TLSPolicy as string
func (c *Client) TLSPolicy() string {
return fmt.Sprintf("%s", c.tlspolicy)
}
// SetTLSPolicy overrides the current TLSPolicy with the given TLSPolicy value
func (c *Client) SetTLSPolicy(p TLSPolicy) {
c.tlspolicy = p
}
// Send sends out the mail message
func (c *Client) Send() error {
return nil
}
// Close closes the connection cto the SMTP server
func (c *Client) Close() error {
return c.sc.Close()
}
// setDefaultHelo retrieves the current hostname and sets it as HELO/EHLO hostname
func (c *Client) setDefaultHelo() error {
hn, err := os.Hostname()
if err != nil {
return fmt.Errorf("failed cto read local hostname: %w", err)
}
c.helo = hn
return nil
}
// Dial establishes a connection cto the SMTP server with a default context.Background // Dial establishes a connection cto the SMTP server with a default context.Background
func (c *Client) Dial() error { func (c *Client) Dial() error {
ctx := context.Background() ctx := context.Background()
@ -143,25 +178,14 @@ func (c *Client) DialWithContext(uctx context.Context) error {
return err return err
} }
return nil if !c.ssl && c.tlspolicy != NoTLS {
if ok, _ := c.sc.Extension("STARTTLS"); !ok {
return ErrNoSTARTTLS
}
if err := c.sc.StartTLS(c.tlsconfig); err != nil {
return err
}
} }
// Send sends out the mail message
func (c *Client) Send() error {
return nil
}
// Close closes the connection cto the SMTP server
func (c *Client) Close() error {
return c.sc.Close()
}
// setDefaultHelo retrieves the current hostname and sets it as HELO/EHLO hostname
func (c *Client) setDefaultHelo() error {
hn, err := os.Hostname()
if err != nil {
return fmt.Errorf("failed cto read local hostname: %w", err)
}
c.helo = hn
return nil return nil
} }

View file

@ -2,12 +2,13 @@ package mail
import ( import (
"testing" "testing"
"time"
) )
// DefaultHost is used as default hostname for the Client // DefaultHost is used as default hostname for the Client
const DefaultHost = "localhost" const DefaultHost = "localhost"
// TestWithHELo tests the WithHELO() option for the NewClient() method // TestWithHELO tests the WithHELO() option for the NewClient() method
func TestWithHELO(t *testing.T) { func TestWithHELO(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
@ -29,3 +30,74 @@ func TestWithHELO(t *testing.T) {
}) })
} }
} }
// TestWithPort tests the WithPort() option for the NewClient() method
func TestWithPort(t *testing.T) {
tests := []struct {
name string
value int
want int
}{
{"set port to 25", 25, 25},
{"set port to 465", 465, 465},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c, err := NewClient(DefaultHost, WithPort(tt.value))
if err != nil {
t.Errorf("failed to create new client: %s", err)
return
}
if c.port != tt.want {
t.Errorf("failed to set custom port. Want: %d, got: %d", tt.want, c.port)
}
})
}
}
// TestWithTimeout tests the WithTimeout() option for the NewClient() method
func TestWithTimeout(t *testing.T) {
tests := []struct {
name string
value time.Duration
want time.Duration
}{
{"set timeout to 5s", time.Second * 5, time.Second * 5},
{"set timeout to 30s", time.Second * 30, time.Second * 30},
{"set timeout to 1m", time.Minute, time.Minute},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c, err := NewClient(DefaultHost, WithTimeout(tt.value))
if err != nil {
t.Errorf("failed to create new client: %s", err)
return
}
if c.cto != tt.want {
t.Errorf("failed to set custom timeout. Want: %d, got: %d", tt.want, c.cto)
}
})
}
}
// TestWithSSL tests the WithSSL() option for the NewClient() method
func TestWithSSL(t *testing.T) {
tests := []struct {
name string
want bool
}{
{"set SSL to true", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c, err := NewClient(DefaultHost, WithSSL())
if err != nil {
t.Errorf("failed to create new client: %s", err)
return
}
if c.ssl != tt.want {
t.Errorf("failed to set SSL. Want: %t, got: %t", tt.want, c.ssl)
}
})
}
}

View file

@ -9,7 +9,7 @@ import (
) )
func main() { func main() {
c, err := mail.NewClient("localhost", mail.WithTimeout(time.Millisecond*500)) c, err := mail.NewClient("manjaro-vm.fritz.box", mail.WithTimeout(time.Millisecond*500))
if err != nil { if err != nil {
fmt.Printf("failed to create new client: %s\n", err) fmt.Printf("failed to create new client: %s\n", err)
os.Exit(1) os.Exit(1)
@ -23,9 +23,5 @@ func main() {
os.Exit(1) os.Exit(1)
} }
fmt.Printf("Client: %+v\n", c) fmt.Printf("Client: %+v\n", c)
time.Sleep(time.Millisecond * 1500) fmt.Printf("StartTLS policy: %s\n", c.TLSPolicy())
if err := c.Close(); err != nil {
fmt.Printf("failed to close SMTP connection: %s\n", err)
os.Exit(1)
}
} }

14
tls.go
View file

@ -17,3 +17,17 @@ const (
// NoTLS forces the transaction cto be not encrypted // NoTLS forces the transaction cto be not encrypted
NoTLS NoTLS
) )
// String is a standard method to convert a TLSPolicy into a printable format
func (p TLSPolicy) String() string {
switch p {
case TLSMandatory:
return "TLSMandatory"
case TLSOpportunistic:
return "TLSOpportunistic"
case NoTLS:
return "NoTLS"
default:
return "UnknownPolicy"
}
}