aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStas Rudakou <stas@garage22.net>2022-07-30 22:42:49 +0200
committerRobin Jarry <robin@jarry.cc>2022-07-31 19:53:13 +0200
commitca90343850290c256430ad64daa3d809a7016299 (patch)
tree5e5f8aed3c7b77f18764103875c67cff061c384f
parente73e5065b0f81e566f8383d30ad25950b127e96b (diff)
downloadaerc-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>
-rw-r--r--commands/compose/send.go8
-rw-r--r--config/config.go95
-rw-r--r--widgets/account-wizard.go2
3 files changed, 60 insertions, 45 deletions
diff --git a/commands/compose/send.go b/commands/compose/send.go
index cca1540f..5448b728 100644
--- a/commands/compose/send.go
+++ b/commands/compose/send.go
@@ -51,7 +51,11 @@ func (Send) Execute(aerc *widgets.Aerc, args []string) error {
tabName := tab.Name
config := composer.Config()
- if config.Outgoing == "" {
+ outgoing, err := config.Outgoing.ConnectionString()
+ if err != nil {
+ return errors.Wrap(err, "ReadCredentials(outgoing)")
+ }
+ if outgoing == "" {
return errors.New(
"No outgoing mail transport configured for this account")
}
@@ -74,7 +78,7 @@ func (Send) Execute(aerc *widgets.Aerc, args []string) error {
return errors.Wrap(err, "ParseAddress(config.From)")
}
- uri, err := url.Parse(config.Outgoing)
+ uri, err := url.Parse(outgoing)
if err != nil {
return errors.Wrap(err, "url.Parse(outgoing)")
}
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
diff --git a/widgets/account-wizard.go b/widgets/account-wizard.go
index ab691395..8b78dcdb 100644
--- a/widgets/account-wizard.go
+++ b/widgets/account-wizard.go
@@ -522,7 +522,7 @@ func (wizard *AccountWizard) finish(tutorial bool) {
Default: "INBOX",
From: sec.Key("from").String(),
Source: sec.Key("source").String(),
- Outgoing: sec.Key("outgoing").String(),
+ Outgoing: config.RemoteConfig{Value: sec.Key("outgoing").String()},
}
if wizard.smtpMode == SMTP_STARTTLS {
account.Params = map[string]string{