mirror of
https://git.sr.ht/~rjarry/aerc
synced 2025-07-06 19:30:22 +02:00

This commit fixes a critical bug found by the CI where import was allegedly misspelled Signed-off-by: Moritz Poldrack <git@moritz.sh> Acked-by: Robin Jarry <robin@jarry.cc>
177 lines
5.1 KiB
Go
177 lines
5.1 KiB
Go
// Package autoconfig provides a somewhat smart way of automatically
|
|
// determining the mailservers for a given address.
|
|
//
|
|
// It works by first checking for a Thunderbird autoconfiguration, and if not
|
|
// found checking common names (imap|smtp|mail).example.com and performing a TCP
|
|
// Ping on their ports.
|
|
//
|
|
// The first part is described here: https://wiki.mozilla.org/Thunderbird:Autoconfiguration
|
|
package autoconfig
|
|
|
|
import (
|
|
"context"
|
|
"encoding/xml"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"git.sr.ht/~rjarry/aerc/lib/log"
|
|
)
|
|
|
|
// getFromAutoconfig retrieves the config from the provider using a Mozilla
|
|
// Thunderbird autoconfig service
|
|
func getFromAutoconfig(ctx context.Context, localpart, domain string, result chan<- *Config) {
|
|
defer log.PanicHandler()
|
|
defer close(result)
|
|
|
|
res := make(chan *Config, 1)
|
|
go func(res chan *Config) {
|
|
defer log.PanicHandler()
|
|
defer close(res)
|
|
|
|
var addresses []*url.URL
|
|
u, err := url.Parse(fmt.Sprintf(
|
|
"https://autoconfig.%s/mail/config-v1.1.xml?emailaddress=%s",
|
|
domain, url.QueryEscape(localpart+"@"+domain)))
|
|
if err == nil {
|
|
addresses = append(addresses, u)
|
|
}
|
|
u, err = url.Parse(fmt.Sprintf(
|
|
"https://%s/.well-known/autoconfig/mail/config-v1.1.xml?emailaddress=%s",
|
|
domain, url.QueryEscape(localpart+"@"+domain)))
|
|
if err == nil {
|
|
addresses = append(addresses, u)
|
|
}
|
|
|
|
log.Debugf("checking for autoconfig files at %v", addresses)
|
|
cc := new(ClientConfig)
|
|
for _, u := range addresses {
|
|
response, err := httpGet(u.String())
|
|
if err != nil {
|
|
continue
|
|
}
|
|
err = xml.NewDecoder(response.Body).Decode(cc)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
// IMAP sanity check
|
|
var incoming *IncomingServer
|
|
for i := range cc.EmailProvider.IncomingServer {
|
|
if strings.ToLower(cc.EmailProvider.IncomingServer[i].Type) != "imap" {
|
|
continue
|
|
}
|
|
incoming = &cc.EmailProvider.IncomingServer[i]
|
|
break
|
|
}
|
|
if incoming == nil {
|
|
// no imap server found
|
|
continue
|
|
}
|
|
var incomingPort int
|
|
if incomingPort, err = strconv.Atoi(incoming.Port); err != nil {
|
|
continue
|
|
}
|
|
inenc := EncryptionSTARTTLS
|
|
switch strings.ToLower(incoming.SocketType) {
|
|
case "plain":
|
|
inenc = EncryptionInsecure
|
|
case "ssl":
|
|
inenc = EncryptionTLS
|
|
}
|
|
if strings.ToLower(incoming.Username) == "%emailaddress%" {
|
|
incoming.Username = localpart + "@" + domain
|
|
}
|
|
|
|
var outport int
|
|
if outport, err = strconv.Atoi(cc.EmailProvider.OutgoingServer.Port); err != nil {
|
|
continue
|
|
}
|
|
outenc := EncryptionSTARTTLS
|
|
switch strings.ToLower(cc.EmailProvider.OutgoingServer.SocketType) {
|
|
case "plain":
|
|
outenc = EncryptionInsecure
|
|
case "ssl":
|
|
outenc = EncryptionTLS
|
|
}
|
|
if strings.ToLower(cc.EmailProvider.OutgoingServer.Username) == "%emailaddress%" {
|
|
cc.EmailProvider.OutgoingServer.Username = localpart + "@" + domain
|
|
}
|
|
|
|
log.Debugf("found HTTP-based autoconfig at %s", u)
|
|
|
|
res <- &Config{
|
|
Found: ProtocolIMAP,
|
|
IMAP: Credentials{
|
|
Encryption: inenc,
|
|
Address: incoming.Hostname,
|
|
Port: incomingPort,
|
|
Username: incoming.Username,
|
|
},
|
|
SMTP: Credentials{
|
|
Encryption: outenc,
|
|
Address: cc.EmailProvider.OutgoingServer.Hostname,
|
|
Port: outport,
|
|
Username: cc.EmailProvider.OutgoingServer.Username,
|
|
},
|
|
}
|
|
return
|
|
}
|
|
}(res)
|
|
|
|
select {
|
|
case r, isValue := <-res:
|
|
if isValue {
|
|
result <- r
|
|
}
|
|
case <-ctx.Done():
|
|
}
|
|
}
|
|
|
|
var httpGet = http.Get
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Autogenerated struct… you probably don't want to touch this. //
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
type ClientConfig struct {
|
|
XMLName xml.Name `xml:"clientConfig"`
|
|
Text string `xml:",chardata"`
|
|
Version string `xml:"version,attr"`
|
|
EmailProvider EmailProvider `xml:"emailProvider"`
|
|
}
|
|
|
|
type EmailProvider struct {
|
|
Text string `xml:",chardata"`
|
|
ID string `xml:"id,attr"`
|
|
Domain []string `xml:"domain"`
|
|
IncomingServer []IncomingServer `xml:"incomingServer"`
|
|
OutgoingServer OutgoingServer `xml:"outgoingServer"`
|
|
}
|
|
|
|
type IncomingServer struct {
|
|
Text string `xml:",chardata"`
|
|
// can only be "imap" or "pop3", only imap is supported by aerc
|
|
Type string `xml:"type,attr"`
|
|
Hostname string `xml:"hostname"`
|
|
Port string `xml:"port"`
|
|
SocketType string `xml:"socketType"`
|
|
Username string `xml:"username"`
|
|
Authentication string `xml:"authentication"`
|
|
Password string `xml:"password"`
|
|
}
|
|
|
|
type OutgoingServer struct {
|
|
Text string `xml:",chardata"`
|
|
Type string `xml:"type,attr"`
|
|
Hostname string `xml:"hostname"`
|
|
Port string `xml:"port"`
|
|
SocketType string `xml:"socketType"`
|
|
Username string `xml:"username"`
|
|
Authentication string `xml:"authentication"`
|
|
Restriction string `xml:"restriction"`
|
|
AddThisServer string `xml:"addThisServer"`
|
|
UseGlobalPreferredServer string `xml:"useGlobalPreferredServer"`
|
|
Password string `xml:"password"`
|
|
}
|