ultisuite-backend/internal/mail/autoconfig/mozilla.go
2026-05-24 00:03:36 +02:00

141 lines
3.5 KiB
Go

package autoconfig
import (
"context"
"encoding/xml"
"fmt"
"io"
"net/http"
"strings"
"time"
)
const autoconfigTimeout = 4 * time.Second
type mozillaConfig struct {
XMLName xml.Name `xml:"clientConfig"`
EmailProvider struct {
ID string `xml:"id,attr"`
Domain []string `xml:"domain"`
DisplayName string `xml:"displayName"`
IncomingServers []mozillaServer `xml:"incomingServer"`
OutgoingServers []mozillaServer `xml:"outgoingServer"`
} `xml:"emailProvider"`
}
type mozillaServer struct {
Type string `xml:"type,attr"`
Hostname string `xml:"hostname"`
Port int `xml:"port"`
SocketType string `xml:"socketType"`
Username string `xml:"username"`
}
func fetchMozillaAutoconfig(ctx context.Context, domain string) (Result, bool) {
urls := []string{
fmt.Sprintf("https://autoconfig.%s/mail/config-v1.1.xml", domain),
fmt.Sprintf("https://%s/.well-known/autoconfig/mail/config-v1.1.xml", domain),
fmt.Sprintf("https://autoconfig.thunderbird.net/v1.1/%s", domain),
}
client := &http.Client{Timeout: autoconfigTimeout}
for _, u := range urls {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
if err != nil {
continue
}
req.Header.Set("User-Agent", "Ultimail-Autoconfig/1.0")
resp, err := client.Do(req)
if err != nil {
continue
}
body, readErr := io.ReadAll(io.LimitReader(resp.Body, 256<<10))
_ = resp.Body.Close()
if readErr != nil || resp.StatusCode != http.StatusOK {
continue
}
if r, ok := parseMozillaConfig(body, domain); ok {
return r, true
}
}
return Result{}, false
}
func parseMozillaConfig(data []byte, domain string) (Result, bool) {
var cfg mozillaConfig
if err := xml.Unmarshal(data, &cfg); err != nil {
return Result{}, false
}
var imapHost string
var imapPort int
var imapTLS bool
var smtpHost string
var smtpPort int
var smtpTLS bool
for _, s := range cfg.EmailProvider.IncomingServers {
if strings.EqualFold(s.Type, "imap") && imapHost == "" {
imapHost = strings.TrimSpace(s.Hostname)
imapPort = s.Port
imapTLS = socketUsesTLS(s.SocketType)
if imapPort == 0 {
imapPort = 993
}
}
}
for _, s := range cfg.EmailProvider.OutgoingServers {
if strings.EqualFold(s.Type, "smtp") && smtpHost == "" {
smtpHost = strings.TrimSpace(s.Hostname)
smtpPort = s.Port
smtpTLS = socketUsesTLS(s.SocketType)
if smtpPort == 0 {
smtpPort = 587
}
}
}
if imapHost == "" && smtpHost == "" {
return Result{}, false
}
name := strings.TrimSpace(cfg.EmailProvider.DisplayName)
if name == "" {
name = cfg.EmailProvider.ID
}
if name == "" {
name = domain
}
return Result{
Domain: domain,
ProviderID: "autoconfig",
ProviderName: name,
Source: "autoconfig",
Confidence: "medium",
IMAPHost: imapHost,
IMAPPort: imapPort,
IMAPTLS: imapTLS,
SMTPHost: smtpHost,
SMTPPort: smtpPort,
SMTPTLS: smtpTLS,
UsernameHint: mozillaUsernameHint(cfg.EmailProvider.IncomingServers),
AuthMethods: []string{"password"},
Notes: []string{"Configuration issue de l'autoconfiguration Mozilla/Thunderbird."},
}, true
}
func socketUsesTLS(socketType string) bool {
switch strings.ToUpper(strings.TrimSpace(socketType)) {
case "SSL", "TLS", "STARTTLS":
return true
default:
return false
}
}
func mozillaUsernameHint(servers []mozillaServer) string {
for _, s := range servers {
u := strings.ToLower(strings.TrimSpace(s.Username))
if strings.Contains(u, "%emailaddress%") {
return "email"
}
}
return "email"
}