diff options
author | Tim Culverhouse <tim@timculverhouse.com> | 2022-05-05 12:53:15 -0500 |
---|---|---|
committer | Robin Jarry <robin@jarry.cc> | 2022-05-06 11:02:50 +0200 |
commit | 32a16dcd8dc488c1f360553d9d9f6d121af1b367 (patch) | |
tree | 3662082ccfc1df962cb4d79aec005359925df367 /widgets/compose.go | |
parent | bb400c7d88a08bc29fd635486dffbbad10f1835d (diff) | |
download | aerc-32a16dcd8dc488c1f360553d9d9f6d121af1b367.tar.gz |
pgp: check encryption keys before sending message
Add check for public keys of all message recipients (to, cc, and bcc)
before sending the message. Adds an OnFocusLost callback to header
editors to facilitate a callback for checking keys whenever a new
recipient is added (OnChange results in too many keyring checks).
Once encryption is initially set, the callbacks are registered. If a
public key is not available for any recipient, encryption is turned off.
However, notably, the callbacks are still registered meaning as s soon
as the user removes the recipients with missing keys, encryption is
turned back on.
Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
Tested-by: Koni Marti <koni.marti@gmail.com>
Diffstat (limited to 'widgets/compose.go')
-rw-r--r-- | widgets/compose.go | 79 |
1 files changed, 70 insertions, 9 deletions
diff --git a/widgets/compose.go b/widgets/compose.go index 5dab4294..49627fca 100644 --- a/widgets/compose.go +++ b/widgets/compose.go @@ -198,8 +198,21 @@ func (c *Composer) Sign() bool { } func (c *Composer) SetEncrypt(encrypt bool) *Composer { - c.encrypt = encrypt - c.updateCrypto() + if !encrypt { + c.encrypt = encrypt + c.updateCrypto() + return c + } + // Check on any attempt to encrypt, and any lost focus of "to", "cc", or + // "bcc" field. Use OnFocusLost instead of OnChange to limit keyring checks + c.encrypt = c.checkEncryptionKeys("") + if c.crypto.setEncOneShot { + // Prevent registering a lot of callbacks + c.OnFocusLost("to", c.checkEncryptionKeys) + c.OnFocusLost("cc", c.checkEncryptionKeys) + c.OnFocusLost("bcc", c.checkEncryptionKeys) + c.crypto.setEncOneShot = false + } return c } @@ -365,6 +378,15 @@ func (c *Composer) OnHeaderChange(header string, fn func(subject string)) { } } +// OnFocusLost registers an OnFocusLost callback for the specified header. +func (c *Composer) OnFocusLost(header string, fn func(input string) bool) { + if editor, ok := c.editors[strings.ToLower(header)]; ok { + editor.OnFocusLost(func() { + fn(editor.input.String()) + }) + } +} + func (c *Composer) OnClose(fn func(composer *Composer)) { c.onClose = append(c.onClose, fn) } @@ -984,6 +1006,12 @@ func (he *headerEditor) OnChange(fn func()) { }) } +func (he *headerEditor) OnFocusLost(fn func()) { + he.input.OnFocusLost(func(_ *ui.TextInput) { + fn() + }) +} + type reviewMessage struct { composer *Composer grid *ui.Grid @@ -1090,18 +1118,21 @@ func (rm *reviewMessage) Draw(ctx *ui.Context) { } type cryptoStatus struct { - title string - status *ui.Text - uiConfig *config.UIConfig - signKey string + title string + status *ui.Text + uiConfig *config.UIConfig + signKey string + setEncOneShot bool } func newCryptoStatus(uiConfig *config.UIConfig) *cryptoStatus { defaultStyle := uiConfig.GetStyle(config.STYLE_DEFAULT) return &cryptoStatus{ - title: "Security", - status: ui.NewText("", defaultStyle), - uiConfig: uiConfig, + title: "Security", + status: ui.NewText("", defaultStyle), + uiConfig: uiConfig, + signKey: "", + setEncOneShot: true, } } @@ -1124,3 +1155,33 @@ func (cs *cryptoStatus) OnInvalidate(fn func(ui.Drawable)) { fn(cs) }) } + +func (c *Composer) checkEncryptionKeys(_ string) bool { + rcpts, err := getRecipientsEmail(c) + if err != nil { + // checkEncryptionKeys gets registered as a callback and must + // explicitly call c.SetEncrypt(false) when encryption is not possible + c.SetEncrypt(false) + st := fmt.Sprintf("Cannot encrypt: %v", err) + c.aerc.statusline.PushError(st) + return false + } + var mk []string + for _, rcpt := range rcpts { + key, err := c.aerc.Crypto.GetKeyId(rcpt) + if err != nil || key == "" { + mk = append(mk, rcpt) + } + } + if len(mk) > 0 { + c.SetEncrypt(false) + st := fmt.Sprintf("Cannot encrypt, missing keys: %s", strings.Join(mk, ", ")) + c.aerc.statusline.PushError(st) + return false + } + // If callbacks were registered, encrypt will be set when user removes + // recipients with missing keys + c.encrypt = true + c.updateCrypto() + return true +} |