From 5402bfef76ed6d0cb9a89fc9d0ea6bda72dcc288 Mon Sep 17 00:00:00 2001 From: Robin Jarry Date: Mon, 14 Nov 2022 11:45:16 +0100 Subject: config: move accounts.conf parsing in separate file The config.go file is getting too big. Move accounts.conf parsing logic into a dedicated accounts.go file. No functional change. Signed-off-by: Robin Jarry Acked-by: Moritz Poldrack --- config/config.go | 251 +------------------------------------------------------ 1 file changed, 1 insertion(+), 250 deletions(-) (limited to 'config/config.go') diff --git a/config/config.go b/config/config.go index 17c88834..6a16234b 100644 --- a/config/config.go +++ b/config/config.go @@ -4,13 +4,9 @@ import ( "errors" "fmt" "log" - "net/url" "os" - "os/exec" "path" "regexp" - "sort" - "strconv" "strings" "time" "unicode" @@ -106,93 +102,6 @@ const ( FILTER_HEADER ) -type RemoteConfig struct { - Value string - PasswordCmd string - CacheCmd bool - cache string -} - -func (c *RemoteConfig) parseValue() (*url.URL, error) { - return url.Parse(c.Value) -} - -func (c *RemoteConfig) ConnectionString() (string, error) { - if c.Value == "" || c.PasswordCmd == "" { - return c.Value, nil - } - - u, err := c.parseValue() - if err != nil { - return "", err - } - - // ignore the command if a password is specified - if _, exists := u.User.Password(); exists { - return c.Value, nil - } - - // don't attempt to parse the command if the url is a path (ie /usr/bin/sendmail) - if !u.IsAbs() { - return c.Value, nil - } - - pw := c.cache - - if pw == "" { - cmd := exec.Command("sh", "-c", c.PasswordCmd) - cmd.Stdin = os.Stdin - output, err := cmd.Output() - if err != nil { - return "", fmt.Errorf("failed to read password: %w", err) - } - pw = strings.TrimSpace(string(output)) - } - u.User = url.UserPassword(u.User.Username(), pw) - if c.CacheCmd { - c.cache = pw - } - - return u.String(), nil -} - -type AccountConfig struct { - Archive string - CopyTo string - Default string - Postpone string - From string - Aliases string - Name string - Source string - Folders []string - FoldersExclude []string - Params map[string]string - Outgoing RemoteConfig - SignatureFile string - SignatureCmd string - EnableFoldersSort bool `ini:"enable-folders-sort"` - FoldersSort []string `ini:"folders-sort" delim:","` - AddressBookCmd string `ini:"address-book-cmd"` - SendAsUTC bool `ini:"send-as-utc"` - LocalizedRe *regexp.Regexp - - // CheckMail - CheckMail time.Duration `ini:"check-mail"` - CheckMailCmd string `ini:"check-mail-cmd"` - CheckMailTimeout time.Duration `ini:"check-mail-timeout"` - CheckMailInclude []string `ini:"check-mail-include"` - CheckMailExclude []string `ini:"check-mail-exclude"` - - // PGP Config - PgpKeyId string `ini:"pgp-key-id"` - PgpAutoSign bool `ini:"pgp-auto-sign"` - PgpOpportunisticEncrypt bool `ini:"pgp-opportunistic-encrypt"` - - // AuthRes - TrustedAuthRes []string `ini:"trusted-authres" delim:","` -} - type BindingConfig struct { Global *KeyBindings AccountWizard *KeyBindings @@ -287,127 +196,6 @@ func mapName(raw string) string { return string(newstr) } -func loadAccountConfig(path string, accts []string) ([]AccountConfig, error) { - file, err := ini.Load(path) - if err != nil { - // No config triggers account configuration wizard - return nil, nil - } - file.NameMapper = mapName - - var accounts []AccountConfig - for _, _sec := range file.SectionStrings() { - if _sec == "DEFAULT" { - continue - } - if len(accts) > 0 && !contains(accts, _sec) { - continue - } - sec := file.Section(_sec) - sourceRemoteConfig := RemoteConfig{} - account := AccountConfig{ - Archive: "Archive", - Default: "INBOX", - Postpone: "Drafts", - Name: _sec, - Params: make(map[string]string), - EnableFoldersSort: true, - CheckMailTimeout: 10 * time.Second, - // localizedRe contains a list of known translations for the common Re: - LocalizedRe: regexp.MustCompile(`(?i)^((AW|RE|SV|VS|ODP|R): ?)+`), - } - if err = sec.MapTo(&account); err != nil { - return nil, err - } - for key, val := range sec.KeysHash() { - switch key { - case "folders": - folders := strings.Split(val, ",") - sort.Strings(folders) - account.Folders = folders - case "folders-exclude": - folders := strings.Split(val, ",") - sort.Strings(folders) - account.FoldersExclude = folders - case "source": - sourceRemoteConfig.Value = val - case "source-cred-cmd": - sourceRemoteConfig.PasswordCmd = val - case "outgoing": - account.Outgoing.Value = val - case "outgoing-cred-cmd": - account.Outgoing.PasswordCmd = val - case "outgoing-cred-cmd-cache": - cache, err := strconv.ParseBool(val) - if err != nil { - return nil, fmt.Errorf( - "%s=%s %w", key, val, err) - } - account.Outgoing.CacheCmd = cache - case "from": - account.From = val - case "aliases": - account.Aliases = val - case "copy-to": - account.CopyTo = val - case "archive": - account.Archive = val - case "enable-folders-sort": - account.EnableFoldersSort, _ = strconv.ParseBool(val) - case "pgp-key-id": - account.PgpKeyId = val - case "pgp-auto-sign": - account.PgpAutoSign, _ = strconv.ParseBool(val) - case "pgp-opportunistic-encrypt": - account.PgpOpportunisticEncrypt, _ = strconv.ParseBool(val) - case "address-book-cmd": - account.AddressBookCmd = val - case "subject-re-pattern": - re, err := regexp.Compile(val) - if err != nil { - return nil, fmt.Errorf( - "%s=%s %w", key, val, err) - } - account.LocalizedRe = re - default: - if key != "name" { - account.Params[key] = val - } - } - } - if account.Source == "" { - return nil, fmt.Errorf("Expected source for account %s", _sec) - } - if account.From == "" { - return nil, fmt.Errorf("Expected from for account %s", _sec) - } - - source, err := sourceRemoteConfig.ConnectionString() - if err != nil { - return nil, fmt.Errorf("Invalid source credentials for %s: %w", _sec, err) - } - account.Source = source - - _, err = account.Outgoing.parseValue() - if err != nil { - return nil, fmt.Errorf("Invalid outgoing credentials for %s: %w", _sec, err) - } - - accounts = append(accounts, account) - } - if len(accts) > 0 { - // Sort accounts struct to match the specified order, if we - // have one - if len(accounts) != len(accts) { - return nil, errors.New("account(s) not found") - } - sort.Slice(accounts, func(i, j int) bool { - return strings.ToLower(accts[i]) < strings.ToLower(accts[j]) - }) - } - return accounts, nil -} - // Set at build time var shareDir string @@ -898,23 +686,8 @@ func LoadConfigFromFile(root *string, accts []string) (*AercConfig, error) { logging.Debugf("aerc.conf: [triggers] %#v", config.Triggers) logging.Debugf("aerc.conf: [templates] %#v", config.Templates) - filename = path.Join(*root, "accounts.conf") - if !config.General.UnsafeAccountsConf { - if err := checkConfigPerms(filename); err != nil { - return nil, err - } - } - - accountsPath := path.Join(*root, "accounts.conf") - logging.Infof("Parsing accounts configuration from %s", accountsPath) - if accounts, err := loadAccountConfig(accountsPath, accts); err != nil { + if err := config.parseAccounts(*root, accts); err != nil { return nil, err - } else { - config.Accounts = accounts - } - - for _, acct := range config.Accounts { - logging.Debugf("accounts.conf: [%s] from = %s", acct.Name, acct.From) } filename = path.Join(*root, "binds.conf") @@ -1076,28 +849,6 @@ func (config *AercConfig) LoadBinds(binds *ini.File, baseName string, baseGroup return nil } -// checkConfigPerms checks for too open permissions -// printing the fix on stdout and returning an error -func checkConfigPerms(filename string) error { - info, err := os.Stat(filename) - if errors.Is(err, os.ErrNotExist) { - return nil // disregard absent files - } - if err != nil { - return err - } - - perms := info.Mode().Perm() - // group or others have read access - if perms&0o44 != 0 { - fmt.Fprintf(os.Stderr, "The file %v has too open permissions.\n", filename) - fmt.Fprintln(os.Stderr, "This is a security issue (it contains passwords).") - fmt.Fprintf(os.Stderr, "To fix it, run `chmod 600 %v`\n", filename) - return errors.New("account.conf permissions too lax") - } - return nil -} - func parseLayout(layout string) [][]string { rows := strings.Split(layout, ",") l := make([][]string, len(rows)) -- cgit