package imapx import ( "context" "crypto/tls" "fmt" "time" "github.com/emersion/go-imap/v2/imapclient" ) type Endpoint struct { Host string Port int TLSMode string // ssl | starttls | plain } func (e Endpoint) addr() string { return fmt.Sprintf("%s:%d", e.Host, e.Port) } func dialOnce(ep Endpoint) (*imapclient.Client, error) { switch ep.TLSMode { case "ssl": return imapclient.DialTLS(ep.addr(), &imapclient.Options{ TLSConfig: &tls.Config{ServerName: ep.Host}, }) case "starttls": return imapclient.DialStartTLS(ep.addr(), &imapclient.Options{ TLSConfig: &tls.Config{ServerName: ep.Host}, }) case "plain": return imapclient.DialInsecure(ep.addr(), nil) default: return nil, fmt.Errorf("unknown tls_mode %q", ep.TLSMode) } } func Connect(ctx context.Context, ep Endpoint) (*imapclient.Client, error) { const attempts = 3 var lastErr error for i := 0; i < attempts; i++ { if err := ctx.Err(); err != nil { return nil, err } c, err := dialOnce(ep) if err == nil { return c, nil } lastErr = err if i < attempts-1 { backoff := time.Duration(200*(i+1)) * time.Millisecond select { case <-ctx.Done(): return nil, ctx.Err() case <-time.After(backoff): } } } return nil, lastErr } func TestEndpoint(ctx context.Context, ep Endpoint) error { c, err := Connect(ctx, ep) if err != nil { return err } return c.Logout().Wait() }