diff options
author | Stas Rudakou <stas@garage22.net> | 2022-07-30 22:42:49 +0200 |
---|---|---|
committer | Robin Jarry <robin@jarry.cc> | 2022-07-31 19:53:13 +0200 |
commit | ca90343850290c256430ad64daa3d809a7016299 (patch) | |
tree | 5e5f8aed3c7b77f18764103875c67cff061c384f /config | |
parent | e73e5065b0f81e566f8383d30ad25950b127e96b (diff) | |
download | aerc-ca90343850290c256430ad64daa3d809a7016299.tar.gz |
outgoing-cred-cmd: delay execution until an email needs to be sent
This can be useful in cases when:
1. outgoing-cred-cmd requires a user action or confirmation (e.g. when
using pass with a Yubikey or similar smart card that requires a user
to enter a pin or touch the device when decrypting the password)
2. A user starts aerc frequently, but not all the sessions end up with
sending emails
3. So the user only wants to execute outgoing-cred-cmd when the password
is really used, so the user doesn't have to enter pin or touch their
Yubikey each time aerc starts
Signed-off-by: Stas Rudakou <stas@garage22.net>
Acked-by: Robin Jarry <robin@jarry.cc>
Diffstat (limited to 'config')
-rw-r--r-- | config/config.go | 95 |
1 files changed, 53 insertions, 42 deletions
diff --git a/config/config.go b/config/config.go index 233f2e0d..75519ab3 100644 --- a/config/config.go +++ b/config/config.go @@ -95,6 +95,50 @@ const ( FILTER_HEADER ) +type RemoteConfig struct { + Value string + PasswordCmd 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 + } + + 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: %s", err) + } + + pw := strings.TrimSpace(string(output)) + u.User = url.UserPassword(u.User.Username(), pw) + c.Value = u.String() + c.PasswordCmd = "" + + return c.Value, nil +} + type AccountConfig struct { Archive string CopyTo string @@ -104,12 +148,10 @@ type AccountConfig struct { Aliases string Name string Source string - SourceCredCmd string Folders []string FoldersExclude []string Params map[string]string - Outgoing string - OutgoingCredCmd string + Outgoing RemoteConfig SignatureFile string SignatureCmd string EnableFoldersSort bool `ini:"enable-folders-sort"` @@ -239,6 +281,7 @@ func loadAccountConfig(path string) ([]AccountConfig, error) { continue } sec := file.Section(_sec) + sourceRemoteConfig := RemoteConfig{} account := AccountConfig{ Archive: "Archive", Default: "INBOX", @@ -260,12 +303,14 @@ func loadAccountConfig(path string) ([]AccountConfig, error) { folders := strings.Split(val, ",") sort.Strings(folders) account.FoldersExclude = folders + } else if key == "source" { + sourceRemoteConfig.Value = val } else if key == "source-cred-cmd" { - account.SourceCredCmd = val + sourceRemoteConfig.PasswordCmd = val } else if key == "outgoing" { - account.Outgoing = val + account.Outgoing.Value = val } else if key == "outgoing-cred-cmd" { - account.OutgoingCredCmd = val + account.Outgoing.PasswordCmd = val } else if key == "from" { account.From = val } else if key == "aliases" { @@ -295,56 +340,22 @@ func loadAccountConfig(path string) ([]AccountConfig, error) { return nil, fmt.Errorf("Expected from for account %s", _sec) } - source, err := parseCredential(account.Source, account.SourceCredCmd) + source, err := sourceRemoteConfig.ConnectionString() 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) + _, err = account.Outgoing.parseValue() if err != nil { return nil, fmt.Errorf("Invalid outgoing credentials for %s: %s", _sec, err) } - account.Outgoing = outgoing accounts = append(accounts, account) } 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) - cmd.Stdin = os.Stdin - 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 -} - // Set at build time var shareDir string |