1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
|
package autoconfig
import (
"context"
"fmt"
"net"
"sync"
"git.sr.ht/~rjarry/aerc/lib/log"
)
type portEncryption struct {
port int
enc encryption
}
// guessMailserver tries to guess mailserver configuration based on commonly
// used settings
//
// It tries to find imap, smtp, and mail.domain.com and attempts to guess the
// encryption based on a TCP ping to the relevant port.
func guessMailserver(ctx context.Context, localpart, domain string, result chan *Config) {
defer log.PanicHandler()
defer close(result)
res := make(chan *Config)
go func(res chan *Config) {
defer log.PanicHandler()
defer close(res)
var imapConfig, smtpConfig *Credentials
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer log.PanicHandler()
defer wg.Done()
imapConfig = guessServername(
domain,
[]string{"imap", "mail"},
[]portEncryption{
{143, EncryptionSTARTTLS},
{993, EncryptionTLS},
},
)
}()
go func() {
defer log.PanicHandler()
defer wg.Done()
smtpConfig = guessServername(
domain,
[]string{"smtp", "mail"},
[]portEncryption{
{587, EncryptionSTARTTLS},
{465, EncryptionTLS},
},
)
}()
wg.Wait()
if imapConfig == nil || smtpConfig == nil {
return
}
log.Debugf("successfully guessed server: %#v %#v", imapConfig, smtpConfig)
imapConfig.Username = localpart + "@" + domain
smtpConfig.Username = localpart + "@" + domain
res <- &Config{
Found: ProtocolIMAP,
IMAP: *imapConfig,
SMTP: *smtpConfig,
}
}(res)
select {
case r, next := <-res:
if next {
result <- r
}
case <-ctx.Done():
}
}
func guessServername(
domain string,
subdomains []string,
portenc []portEncryption,
) *Credentials {
for _, subdomain := range subdomains {
cred := tryPort(subdomain+"."+domain, portenc)
if cred != nil {
return cred
}
}
return nil
}
func tryPort(host string, ports []portEncryption) *Credentials {
var res Credentials
for _, pe := range ports {
res.Address = host
res.Port = pe.port
res.Encryption = pe.enc
c, err := netDial("tcp", fmt.Sprintf("%s:%d", host, pe.port))
if err != nil {
continue
}
c.Close()
return &res
}
return nil
}
var netDial = net.Dial
|