From abf6ec7f02edbf8721fb32e4e760ebda2c20bc50 Mon Sep 17 00:00:00 2001 From: Robin Jarry Date: Thu, 8 Sep 2022 20:18:31 +0200 Subject: config: keep cache of resolved credential commands outgoing-cred-cmd is used to retrieve the password from a password manager such as UNIX pass or bitwarden CLI. These tools often prompt for a passphrase to secure the passwords and it is annoying having to enter it every time sending an email with aerc. Add a new option outgoing-cred-cmd-cache (default to true) to control whether aerc will keep a cache of the password or run outgoing-cred-cmd every time an email needs to be sent. NB: If the cached password is incorrect, the only way to change it is to restart aerc. Fixes: ca9034385029 ("outgoing-cred-cmd: delay execution until an email needs to be sent") Signed-off-by: Robin Jarry Acked-by: Moritz Poldrack --- config/config.go | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) (limited to 'config/config.go') diff --git a/config/config.go b/config/config.go index 9f2af8f8..e7e4de03 100644 --- a/config/config.go +++ b/config/config.go @@ -98,13 +98,15 @@ const ( type RemoteConfig struct { Value string PasswordCmd string + CacheCmd bool + cache string } -func (c RemoteConfig) parseValue() (*url.URL, error) { +func (c *RemoteConfig) parseValue() (*url.URL, error) { return url.Parse(c.Value) } -func (c RemoteConfig) ConnectionString() (string, error) { +func (c *RemoteConfig) ConnectionString() (string, error) { if c.Value == "" || c.PasswordCmd == "" { return c.Value, nil } @@ -124,18 +126,23 @@ func (c RemoteConfig) ConnectionString() (string, error) { return c.Value, nil } - 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 := c.cache - pw := strings.TrimSpace(string(output)) + 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) - c.Value = u.String() + if c.CacheCmd { + c.cache = pw + } - return c.Value, nil + return u.String(), nil } type AccountConfig struct { @@ -314,6 +321,13 @@ func loadAccountConfig(path string, accts []string) ([]AccountConfig, error) { 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": -- cgit