Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ sog follows [gog](https://github.com/steipete/gog) and [mog](https://github.com/
| Provider | IMAP/SMTP | CalDAV | CardDAV | WebDAV |
|----------|-----------|--------|---------|--------|
| **Fastmail** | ✅ | ✅ | ✅ | ✅ |
| **ICloud** | ✅ | | | |

*Other standards-compliant providers should work but have not been tested yet.*

Expand Down
92 changes: 49 additions & 43 deletions internal/smtp/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,18 +139,7 @@ func (c *Client) Send(ctx context.Context, msg *Message) error {
InsecureSkipVerify: c.insecure,
}

var client *smtp.Client
var err error

if c.noTLS {
client, err = smtp.Dial(addr)
} else if c.tls {
client, err = smtp.DialTLS(addr, tlsConfig)
} else if c.startTLS {
client, err = smtp.DialStartTLS(addr, tlsConfig)
} else {
client, err = smtp.Dial(addr)
}
client, err := c.selectConnectionType(addr, tlsConfig)
if err != nil {
return fmt.Errorf("failed to connect: %w", err)
}
Expand Down Expand Up @@ -198,40 +187,57 @@ func generateBoundary() string {
return fmt.Sprintf("----=_Part_%x", b)
}

// TestConnection tests the SMTP connection.
func (c *Client) TestConnection() error {
addr := fmt.Sprintf("%s:%d", c.host, c.port)
func (c *Client) selectConnectionType(addr string, tlsConfig *tls.Config) (*smtp.Client, error) {
if c.noTLS {
return smtp.Dial(addr)
}

var client *smtp.Client
var err error
if c.tls {
client, err := smtp.DialTLS(addr, tlsConfig)

tlsConfig := &tls.Config{
ServerName: c.host,
InsecureSkipVerify: c.insecure,
}
if err != nil {
if c.startTLS {
return smtp.DialStartTLS(addr, tlsConfig)
}
return nil, err
}

if c.noTLS {
// Plain text connection
client, err = smtp.Dial(addr)
} else if c.tls {
// Direct TLS (SMTPS, port 465)
client, err = smtp.DialTLS(addr, tlsConfig)
} else if c.startTLS {
// STARTTLS (port 587)
client, err = smtp.DialStartTLS(addr, tlsConfig)
} else {
client, err = smtp.Dial(addr)
}
if err != nil {
return fmt.Errorf("failed to connect: %w", err)
}
defer client.Close()
return client, nil
}

// Authenticate
auth := sasl.NewPlainClient("", c.email, c.password)
if err := client.Auth(auth); err != nil {
return fmt.Errorf("failed to authenticate: %w", err)
}
if c.startTLS {
return smtp.DialStartTLS(addr, tlsConfig)
}

return client.Quit()
return smtp.Dial(addr)
}

func (c *Client) authenticateAndQuit(client *smtp.Client) error {
defer client.Close()

// Authenticate
auth := sasl.NewPlainClient("", c.email, c.password)
if err := client.Auth(auth); err != nil {
return fmt.Errorf("failed to authenticate: %w", err)
}

return client.Quit()
}

// TestConnection tests the SMTP connection.
func (c *Client) TestConnection() error {
addr := fmt.Sprintf("%s:%d", c.host, c.port)

tlsConfig := &tls.Config{
ServerName: c.host,
InsecureSkipVerify: c.insecure,
}

client, err := c.selectConnectionType(addr, tlsConfig)
if err != nil {
return err
}

return c.authenticateAndQuit(client)
}