From b8208509f47301bcb03bc222f5e5765833478250 Mon Sep 17 00:00:00 2001 From: Galen Abell Date: Sat, 18 May 2019 15:29:26 -0400 Subject: [PATCH] Implement loading passwords from external commands * Resolves #80 --- config/config.go | 69 ++++++++++++++++++++++++++++++++++++++----- doc/aerc-config.5.scd | 13 ++++++++ doc/aerc-imap.5.scd | 10 +++++++ doc/aerc-smtp.5.scd | 10 +++++++ 4 files changed, 94 insertions(+), 8 deletions(-) diff --git a/config/config.go b/config/config.go index aef796c..d864d2c 100644 --- a/config/config.go +++ b/config/config.go @@ -3,7 +3,9 @@ package config import ( "errors" "fmt" + "net/url" "os" + "os/exec" "path" "regexp" "strings" @@ -29,14 +31,16 @@ const ( ) type AccountConfig struct { - CopyTo string - Default string - From string - Name string - Source string - Folders []string - Params map[string]string - Outgoing string + CopyTo string + Default string + From string + Name string + Source string + SourceCredCmd string + Folders []string + Params map[string]string + Outgoing string + OutgoingCredCmd string } type BindingConfig struct { @@ -115,8 +119,12 @@ func loadAccountConfig(path string) ([]AccountConfig, error) { for key, val := range sec.KeysHash() { if key == "folders" { account.Folders = strings.Split(val, ",") + } else if key == "source_cred_cmd" { + account.SourceCredCmd = val } else if key == "outgoing" { account.Outgoing = val + } else if key == "outgoing_cred_cmd" { + account.OutgoingCredCmd = val } else if key == "from" { account.From = val } else if key == "copy-to" { @@ -128,6 +136,19 @@ func loadAccountConfig(path string) ([]AccountConfig, error) { if account.Source == "" { return nil, fmt.Errorf("Expected source for account %s", _sec) } + + source, err := parseCredential(account.Source, account.SourceCredCmd) + if err != nil { + return nil, fmt.Errorf("Invalid source credentials for %s: %s", _sec, err) + } + account.Source = source + + outgoing, err := parseCredential(account.Outgoing, account.OutgoingCredCmd) + if err != nil { + return nil, fmt.Errorf("Invalid outgoing credentials for %s: %s", _sec, err) + } + account.Outgoing = outgoing + accounts = append(accounts, account) } if len(accounts) == 0 { @@ -137,6 +158,38 @@ func loadAccountConfig(path string) ([]AccountConfig, error) { return accounts, nil } +func parseCredential(cred, command string) (string, error) { + if cred == "" || command == "" { + return cred, nil + } + + u, err := url.Parse(cred) + if err != nil { + return "", err + } + + // ignore the command if a password is specified + if _, exists := u.User.Password(); exists { + return cred, nil + } + + // don't attempt to parse the command if the url is a path (ie /usr/bin/sendmail) + if !u.IsAbs() { + return cred, nil + } + + cmd := exec.Command("sh", "-c", command) + output, err := cmd.Output() + if err != nil { + return "", fmt.Errorf("failed to read password: %s", err) + } + + pw := strings.TrimSpace(string(output)) + u.User = url.UserPassword(u.User.Username(), pw) + + return u.String(), nil +} + func LoadConfig(root *string) (*AercConfig, error) { if root == nil { _root := path.Join(xdg.ConfigHome(), "aerc") diff --git a/doc/aerc-config.5.scd b/doc/aerc-config.5.scd index a221723..ecf7720 100644 --- a/doc/aerc-config.5.scd +++ b/doc/aerc-config.5.scd @@ -144,6 +144,12 @@ Note that many of these configuration options are written for you, such as - *aerc-smtp*(5) +*outgoing_cred_cmd* + Specifies an optional command that is run to get the outgoing account's + password. See each protocol's man page for more details: + + - *aerc-smtp*(5) + *source* Specifies the source for reading incoming emails on this account. This key is required for all accounts. It should be a connection string, and the @@ -154,6 +160,13 @@ Note that many of these configuration options are written for you, such as Default: none +*source_cred_cmd* + Specifies an optional command that is run to get the source account's + password. See each protocol's man page for more details: + + - *aerc-imap*(5) + + # BINDS.CONF This file is used for configuring keybindings used in the aerc interactive diff --git a/doc/aerc-imap.5.scd b/doc/aerc-imap.5.scd index 5899a34..d676c7c 100644 --- a/doc/aerc-imap.5.scd +++ b/doc/aerc-imap.5.scd @@ -35,6 +35,16 @@ available: *imaps*: IMAP with TLS/SSL +*source_cred_cmd* + Specifies the command to run to get the password for the IMAP + account. This command will be run using `sh -c [command]`. If a + password is specified in the *source* option, the password will + take precedence over this command. + + Example: + + `pass hostname/username` + # SEE ALSO *aerc*(1) *aerc-config*(5) *aerc-smtp*(5) diff --git a/doc/aerc-smtp.5.scd b/doc/aerc-smtp.5.scd index 7d07125..17eb627 100644 --- a/doc/aerc-smtp.5.scd +++ b/doc/aerc-smtp.5.scd @@ -39,6 +39,16 @@ available: Authenticate with a username and password using AUTH PLAIN. This is the default behavior. +*outgoing_cred_cmd* + Specifies the command to run to get the password for the SMTP + account. This command will be run using `sh -c [command]`. If a + password is specified in the *outgoing* option, the password will + take precedence over this command. + + Example: + + `pass hostname/username` + # SEE ALSO *aerc*(1) *aerc-config*(5) *aerc-smtp*(5)