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: ca90343850 ("outgoing-cred-cmd: delay execution until an email needs to be sent")
Signed-off-by: Robin Jarry <robin@jarry.cc>
Acked-by: Moritz Poldrack <moritz@poldrack.dev>
This commit is contained in:
Robin Jarry 2022-09-08 20:18:31 +02:00
parent f6a7a64fa7
commit abf6ec7f02
3 changed files with 40 additions and 12 deletions

View file

@ -5,6 +5,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased](https://git.sr.ht/~rjarry/aerc/log/master) ## [Unreleased](https://git.sr.ht/~rjarry/aerc/log/master)
### Fixed
- `outgoing-cred-cmd` will no longer be executed every time an email needs to
be sent. The output will be stored until aerc is shut down. This behaviour
can be disabled by setting `outgoing-cred-cmd-cache=false` in
`accounts.conf`.
## [0.12.0](https://git.sr.ht/~rjarry/aerc/refs/0.12.0) - 2022-09-01 ## [0.12.0](https://git.sr.ht/~rjarry/aerc/refs/0.12.0) - 2022-09-01
### Added ### Added

View file

@ -98,13 +98,15 @@ const (
type RemoteConfig struct { type RemoteConfig struct {
Value string Value string
PasswordCmd 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) return url.Parse(c.Value)
} }
func (c RemoteConfig) ConnectionString() (string, error) { func (c *RemoteConfig) ConnectionString() (string, error) {
if c.Value == "" || c.PasswordCmd == "" { if c.Value == "" || c.PasswordCmd == "" {
return c.Value, nil return c.Value, nil
} }
@ -124,18 +126,23 @@ func (c RemoteConfig) ConnectionString() (string, error) {
return c.Value, nil return c.Value, nil
} }
cmd := exec.Command("sh", "-c", c.PasswordCmd) pw := c.cache
cmd.Stdin = os.Stdin
output, err := cmd.Output() if pw == "" {
if err != nil { cmd := exec.Command("sh", "-c", c.PasswordCmd)
return "", fmt.Errorf("failed to read password: %w", err) 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
} }
pw := strings.TrimSpace(string(output)) return u.String(), nil
u.User = url.UserPassword(u.User.Username(), pw)
c.Value = u.String()
return c.Value, nil
} }
type AccountConfig struct { type AccountConfig struct {
@ -314,6 +321,13 @@ func loadAccountConfig(path string, accts []string) ([]AccountConfig, error) {
account.Outgoing.Value = val account.Outgoing.Value = val
case "outgoing-cred-cmd": case "outgoing-cred-cmd":
account.Outgoing.PasswordCmd = val 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": case "from":
account.From = val account.From = val
case "aliases": case "aliases":

View file

@ -657,6 +657,13 @@ Note that many of these configuration options are written for you, such as
Default: none Default: none
*outgoing-cred-cmd-cache*
By default, the credentials returned by the command will be cached until
aerc is shut down. If set to false, *outgoing-cred-cmd* will be executed
every time an email is to be sent.
Default: true
*pgp-auto-sign* *pgp-auto-sign*
If true, all outgoing emails from this account will be signed (if a signing If true, all outgoing emails from this account will be signed (if a signing
key is available) key is available)