package main import ( "os" "path/filepath" "strings" "gopkg.in/yaml.v3" ) const configFileName = "config.yml" type Config struct { From string `yaml:"from"` Signature string `yaml:"signature"` SMTP SMTPConfig `yaml:"smtp"` UnsafeHTML bool `yaml:"unsafe_html"` Accounts []Account `yaml:"accounts"` } type Account struct { Name string `yaml:"name"` Email string `yaml:"email"` Provider string `yaml:"provider"` Username string `yaml:"username"` Password string `yaml:"password"` IMAP IMAPConfig `yaml:"imap"` SMTP SMTPConfig `yaml:"smtp"` SMTPEncryption string `yaml:"smtp_encryption"` } type IMAPConfig struct { Host string `yaml:"host"` Port int `yaml:"port"` Username string `yaml:"username"` Password string `yaml:"password"` Encryption string `yaml:"encryption"` InsecureSkipVerify bool `yaml:"insecure"` } type SMTPConfig struct { Host string `yaml:"host"` Port int `yaml:"port"` Username string `yaml:"username"` Password string `yaml:"password"` Encryption string `yaml:"encryption"` InsecureSkipVerify bool `yaml:"insecure"` } var defaultConfig = Config{ From: "", Signature: "", SMTP: SMTPConfig{ Host: "", Port: 587, Username: "", Password: "", Encryption: "starttls", InsecureSkipVerify: false, }, UnsafeHTML: false, } var emailProviders = map[string]SMTPConfig{ "163": { Host: "smtp.163.com", Port: 465, Encryption: "ssl", }, "QQ": { Host: "smtp.qq.com", Port: 465, Encryption: "ssl", }, "Gmail": { Host: "smtp.gmail.com", Port: 465, Encryption: "ssl", }, "Outlook": { Host: "smtp.office365.com", Port: 587, Encryption: "starttls", }, } var providerDefaults = map[string]struct { IMAPHost string IMAPPort int IMAPEncryption string SMTPHost string SMTPPort int SMTPEncryption string }{ "163": { IMAPHost: "imap.163.com", IMAPPort: 993, IMAPEncryption: "ssl", SMTPHost: "smtp.163.com", SMTPPort: 465, SMTPEncryption: "ssl", }, "188": { IMAPHost: "imap.188.com", IMAPPort: 993, IMAPEncryption: "ssl", SMTPHost: "smtp.188.com", SMTPPort: 465, SMTPEncryption: "ssl", }, "QQ": { IMAPHost: "imap.qq.com", IMAPPort: 993, IMAPEncryption: "ssl", SMTPHost: "smtp.qq.com", SMTPPort: 465, SMTPEncryption: "ssl", }, "Gmail": { IMAPHost: "imap.gmail.com", IMAPPort: 993, IMAPEncryption: "ssl", SMTPHost: "smtp.gmail.com", SMTPPort: 465, SMTPEncryption: "ssl", }, "Outlook": { IMAPHost: "outlook.office365.com", IMAPPort: 993, IMAPEncryption: "ssl", SMTPHost: "smtp.office365.com", SMTPPort: 587, SMTPEncryption: "starttls", }, } var imapProviders = map[string]IMAPConfig{ "163": { Host: "imap.163.com", Port: 993, Username: "", Password: "", }, "QQ": { Host: "imap.qq.com", Port: 993, Username: "", Password: "", }, "Gmail": { Host: "imap.gmail.com", Port: 993, Username: "", Password: "", }, "Outlook": { Host: "outlook.office365.com", Port: 993, Username: "", Password: "", }, } func normalizeAccount(acc Account) Account { if acc.Provider == "" { acc.Provider = getProviderName(acc.Email) } if defaults, ok := providerDefaults[acc.Provider]; ok { if acc.IMAP.Host == "" { acc.IMAP.Host = defaults.IMAPHost acc.IMAP.Port = defaults.IMAPPort } if acc.IMAP.Encryption == "" && defaults.IMAPEncryption != "" { acc.IMAP.Encryption = defaults.IMAPEncryption } if acc.SMTP.Host == "" { acc.SMTP.Host = defaults.SMTPHost acc.SMTP.Port = defaults.SMTPPort } if acc.SMTPEncryption == "" { acc.SMTPEncryption = defaults.SMTPEncryption } if acc.SMTP.Encryption == "" && acc.SMTPEncryption != "" { acc.SMTP.Encryption = acc.SMTPEncryption } } if acc.Username == "" { acc.Username = acc.Email } if acc.IMAP.Username == "" { acc.IMAP.Username = acc.Username } if acc.IMAP.Password == "" { acc.IMAP.Password = acc.Password } if acc.SMTP.Username == "" { acc.SMTP.Username = acc.Username } if acc.SMTP.Password == "" { acc.SMTP.Password = acc.Password } if acc.Name == "" { acc.Name = acc.Provider } return acc } func isValidEmail(email string) bool { return strings.Contains(email, "@") && strings.Contains(email, ".") } func getValidFromAddress(from, smtpUsername string) string { if isValidEmail(from) { return from } if isValidEmail(smtpUsername) { return smtpUsername } return from } func getConfigPath() (string, error) { homeDir, err := os.UserHomeDir() if err != nil { return "", err } configDir := filepath.Join(homeDir, ".config", "pop") return filepath.Join(configDir, configFileName), nil } func getConfigDir() (string, error) { homeDir, err := os.UserHomeDir() if err != nil { return "", err } return filepath.Join(homeDir, ".config", "pop"), nil } func loadConfig() (Config, error) { configPath, err := getConfigPath() if err != nil { return defaultConfig, err } data, err := os.ReadFile(configPath) if err != nil { if os.IsNotExist(err) { return defaultConfig, nil } return defaultConfig, err } var cfg Config if err := yaml.Unmarshal(data, &cfg); err != nil { return defaultConfig, err } return cfg, nil } func saveConfig(cfg Config) error { configDir, err := getConfigDir() if err != nil { return err } if err := os.MkdirAll(configDir, 0755); err != nil { return err } configPath, err := getConfigPath() if err != nil { return err } data, err := yaml.Marshal(&cfg) if err != nil { return err } return os.WriteFile(configPath, data, 0644) } func configExists() bool { configPath, err := getConfigPath() if err != nil { return false } _, err = os.Stat(configPath) return err == nil } func getAccounts() ([]Account, error) { cfg, err := loadConfig() if err != nil { return nil, err } var accounts []Account if cfg.SMTP.Username != "" { accounts = append(accounts, Account{ Name: getProviderName(cfg.SMTP.Username), Email: cfg.SMTP.Username, SMTP: cfg.SMTP, IMAP: IMAPConfig{ Host: getIMAPHost(cfg.SMTP.Username), Port: 993, Username: cfg.SMTP.Username, Password: cfg.SMTP.Password, }, }) } accounts = append(accounts, cfg.Accounts...) for i := range accounts { accounts[i] = normalizeAccount(accounts[i]) } return accounts, nil } func getProviderName(email string) string { if strings.HasSuffix(email, "@163.com") || strings.HasSuffix(email, "@vip.163.com") { return "163" } if strings.HasSuffix(email, "@188.com") || strings.HasSuffix(email, "@vip.188.com") { return "188" } if strings.HasSuffix(email, "@qq.com") || strings.HasSuffix(email, "@vip.qq.com") { return "QQ" } if strings.HasSuffix(email, "@gmail.com") { return "Gmail" } if strings.HasSuffix(email, "@outlook.com") || strings.HasSuffix(email, "@office365.com") { return "Outlook" } parts := strings.Split(email, "@") if len(parts) > 1 { return strings.Split(parts[1], ".")[0] } return "Email" } func getIMAPHost(email string) string { if strings.HasSuffix(email, "@163.com") { return "imap.163.com" } if strings.HasSuffix(email, "@qq.com") { return "imap.qq.com" } if strings.HasSuffix(email, "@gmail.com") { return "imap.gmail.com" } if strings.HasSuffix(email, "@outlook.com") || strings.HasSuffix(email, "@office365.com") { return "outlook.office365.com" } return "" }