aerc: always check SelectedAccount return value

aerc.SelectedAccount() is used in lots of places. Most of them without
checking the return value.

In some cases, the currently selected tab is not related to any account
(widget.Terminal for example). This can lead to unexpected crashes when
accessing account specific configuration.

When possible, return an error when no account is currently selected.
If no error can be returned, fallback to non-account specific
configuration.

Signed-off-by: Robin Jarry <robin@jarry.cc>
Reviewed-by: Koni Marti <koni.marti@gmail.com>
This commit is contained in:
Robin Jarry 2022-02-25 00:21:06 +01:00
parent 91ead11c47
commit c26d08103b
9 changed files with 65 additions and 29 deletions

View file

@ -30,6 +30,9 @@ func (Compose) Execute(aerc *widgets.Aerc, args []string) error {
return err return err
} }
acct := aerc.SelectedAccount() acct := aerc.SelectedAccount()
if acct == nil {
return errors.New("No account selected")
}
if template == "" { if template == "" {
template = aerc.Config().Templates.NewMessage template = aerc.Config().Templates.NewMessage
} }

View file

@ -26,6 +26,9 @@ func (ViewMessage) Execute(aerc *widgets.Aerc, args []string) error {
return errors.New("Usage: view-message") return errors.New("Usage: view-message")
} }
acct := aerc.SelectedAccount() acct := aerc.SelectedAccount()
if acct == nil {
return errors.New("No account selected")
}
if acct.Messages().Empty() { if acct.Messages().Empty() {
return nil return nil
} }

View file

@ -117,11 +117,15 @@ func (cmds *Commands) GetCompletions(aerc *widgets.Aerc, cmd string) []string {
func GetFolders(aerc *widgets.Aerc, args []string) []string { func GetFolders(aerc *widgets.Aerc, args []string) []string {
out := make([]string, 0) out := make([]string, 0)
if len(args) == 0 { acct := aerc.SelectedAccount()
return aerc.SelectedAccount().Directories().List() if acct == nil {
return out
} }
for _, dir := range aerc.SelectedAccount().Directories().List() { if len(args) == 0 {
if foundInString(dir, args[0], aerc.SelectedAccount().UiConfig().FuzzyFolderComplete) { return acct.Directories().List()
}
for _, dir := range acct.Directories().List() {
if foundInString(dir, args[0], acct.UiConfig().FuzzyFolderComplete) {
out = append(out, dir) out = append(out, dir)
} }
} }
@ -144,8 +148,12 @@ func CompletionFromList(valid []string, args []string) []string {
} }
func GetLabels(aerc *widgets.Aerc, args []string) []string { func GetLabels(aerc *widgets.Aerc, args []string) []string {
acct := aerc.SelectedAccount()
if acct == nil {
return make([]string, 0)
}
if len(args) == 0 { if len(args) == 0 {
return aerc.SelectedAccount().Labels() return acct.Labels()
} }
// + and - are used to denote tag addition / removal and need to be striped // + and - are used to denote tag addition / removal and need to be striped
@ -165,7 +173,7 @@ func GetLabels(aerc *widgets.Aerc, args []string) []string {
trimmed := strings.TrimLeft(last, "+-") trimmed := strings.TrimLeft(last, "+-")
out := make([]string, 0) out := make([]string, 0)
for _, label := range aerc.SelectedAccount().Labels() { for _, label := range acct.Labels() {
if hasCaseSmartPrefix(label, trimmed) { if hasCaseSmartPrefix(label, trimmed) {
var prev string var prev string
if len(others) > 0 { if len(others) > 0 {

View file

@ -31,6 +31,10 @@ func (Postpone) Execute(aerc *widgets.Aerc, args []string) error {
if len(args) != 1 { if len(args) != 1 {
return errors.New("Usage: postpone") return errors.New("Usage: postpone")
} }
acct := aerc.SelectedAccount()
if acct == nil {
return errors.New("No account selected")
}
composer, _ := aerc.SelectedTab().(*widgets.Composer) composer, _ := aerc.SelectedTab().(*widgets.Composer)
config := composer.Config() config := composer.Config()
tabName := aerc.TabNames()[aerc.SelectedTabIndex()] tabName := aerc.TabNames()[aerc.SelectedTabIndex()]
@ -48,7 +52,7 @@ func (Postpone) Execute(aerc *widgets.Aerc, args []string) error {
header.SetContentType("text/plain", map[string]string{"charset": "UTF-8"}) header.SetContentType("text/plain", map[string]string{"charset": "UTF-8"})
header.Set("Content-Transfer-Encoding", "quoted-printable") header.Set("Content-Transfer-Encoding", "quoted-printable")
worker := composer.Worker() worker := composer.Worker()
dirs := aerc.SelectedAccount().Directories().List() dirs := acct.Directories().List()
alreadyCreated := false alreadyCreated := false
for _, dir := range dirs { for _, dir := range dirs {
if dir == config.Postpone { if dir == config.Postpone {

View file

@ -89,6 +89,9 @@ func parseUnsubscribeMethods(header string) (methods []*url.URL) {
func unsubscribeMailto(aerc *widgets.Aerc, u *url.URL) error { func unsubscribeMailto(aerc *widgets.Aerc, u *url.URL) error {
widget := aerc.SelectedTab().(widgets.ProvidesMessage) widget := aerc.SelectedTab().(widgets.ProvidesMessage)
acct := widget.SelectedAccount() acct := widget.SelectedAccount()
if acct == nil {
return errors.New("No account selected")
}
h := &mail.Header{} h := &mail.Header{}
h.SetSubject(u.Query().Get("subject")) h.SetSubject(u.Query().Get("subject"))

View file

@ -1,6 +1,8 @@
package msgview package msgview
import ( import (
"errors"
"git.sr.ht/~rjarry/aerc/commands/account" "git.sr.ht/~rjarry/aerc/commands/account"
"git.sr.ht/~rjarry/aerc/lib" "git.sr.ht/~rjarry/aerc/lib"
"git.sr.ht/~rjarry/aerc/widgets" "git.sr.ht/~rjarry/aerc/widgets"
@ -27,6 +29,9 @@ func (NextPrevMsg) Execute(aerc *widgets.Aerc, args []string) error {
} }
mv, _ := aerc.SelectedTab().(*widgets.MessageViewer) mv, _ := aerc.SelectedTab().(*widgets.MessageViewer)
acct := mv.SelectedAccount() acct := mv.SelectedAccount()
if acct == nil {
return errors.New("No account selected")
}
store := mv.Store() store := mv.Store()
err = account.ExecuteNextPrevMessage(args, acct, pct, n) err = account.ExecuteNextPrevMessage(args, acct, pct, n)
if err != nil { if err != nil {

View file

@ -309,6 +309,14 @@ func (aerc *Aerc) SelectedAccount() *AccountView {
return nil return nil
} }
func (aerc *Aerc) SelectedAccountUiConfig() config.UIConfig {
acct := aerc.SelectedAccount()
if acct == nil {
return aerc.conf.Ui
}
return acct.UiConfig()
}
func (aerc *Aerc) SelectedTab() ui.Drawable { func (aerc *Aerc) SelectedTab() ui.Drawable {
return aerc.tabs.Tabs[aerc.tabs.Selected].Content return aerc.tabs.Tabs[aerc.tabs.Selected].Content
} }

View file

@ -127,15 +127,15 @@ func (c *Composer) buildComposeHeader(aerc *Aerc, cmpl *completer.Completer) {
c.layout = aerc.conf.Compose.HeaderLayout c.layout = aerc.conf.Compose.HeaderLayout
c.editors = make(map[string]*headerEditor) c.editors = make(map[string]*headerEditor)
c.focusable = make([]ui.MouseableDrawableInteractive, 0) c.focusable = make([]ui.MouseableDrawableInteractive, 0)
uiConfig := aerc.SelectedAccountUiConfig()
for i, row := range c.layout { for i, row := range c.layout {
for j, h := range row { for j, h := range row {
h = strings.ToLower(h) h = strings.ToLower(h)
c.layout[i][j] = h // normalize to lowercase c.layout[i][j] = h // normalize to lowercase
e := newHeaderEditor(h, c.header, aerc.SelectedAccount().UiConfig()) e := newHeaderEditor(h, c.header, uiConfig)
if aerc.conf.Ui.CompletionPopovers { if aerc.conf.Ui.CompletionPopovers {
e.input.TabComplete(cmpl.ForHeader(h), e.input.TabComplete(cmpl.ForHeader(h), uiConfig.CompletionDelay)
aerc.SelectedAccount().UiConfig().CompletionDelay)
} }
c.editors[h] = e c.editors[h] = e
switch h { switch h {
@ -152,9 +152,9 @@ func (c *Composer) buildComposeHeader(aerc *Aerc, cmpl *completer.Completer) {
for _, h := range []string{"cc", "bcc"} { for _, h := range []string{"cc", "bcc"} {
if c.header.Has(h) { if c.header.Has(h) {
if _, ok := c.editors[h]; !ok { if _, ok := c.editors[h]; !ok {
e := newHeaderEditor(h, c.header, aerc.SelectedAccount().UiConfig()) e := newHeaderEditor(h, c.header, uiConfig)
if aerc.conf.Ui.CompletionPopovers { if aerc.conf.Ui.CompletionPopovers {
e.input.TabComplete(cmpl.ForHeader(h), aerc.SelectedAccount().UiConfig().CompletionDelay) e.input.TabComplete(cmpl.ForHeader(h), uiConfig.CompletionDelay)
} }
c.editors[h] = e c.editors[h] = e
c.focusable = append(c.focusable, e) c.focusable = append(c.focusable, e)
@ -741,11 +741,10 @@ func (c *Composer) AddEditor(header string, value string, appendHeader bool) {
e.storeValue() // flush modifications from the user to the header e.storeValue() // flush modifications from the user to the header
editor = e editor = e
} else { } else {
e := newHeaderEditor(header, c.header, uiConfig := c.aerc.SelectedAccountUiConfig()
c.aerc.SelectedAccount().UiConfig()) e := newHeaderEditor(header, c.header, uiConfig)
if c.config.Ui.CompletionPopovers { if uiConfig.CompletionPopovers {
e.input.TabComplete(c.completer.ForHeader(header), e.input.TabComplete(c.completer.ForHeader(header), uiConfig.CompletionDelay)
c.config.Ui.CompletionDelay)
} }
c.editors[header] = e c.editors[header] = e
c.layout = append(c.layout, []string{header}) c.layout = append(c.layout, []string{header})

View file

@ -53,11 +53,13 @@ func (ml *MessageList) Invalidate() {
func (ml *MessageList) Draw(ctx *ui.Context) { func (ml *MessageList) Draw(ctx *ui.Context) {
ml.height = ctx.Height() ml.height = ctx.Height()
uiConfig := ml.aerc.SelectedAccountUiConfig()
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ',
ml.aerc.SelectedAccount().UiConfig().GetStyle(config.STYLE_MSGLIST_DEFAULT)) uiConfig.GetStyle(config.STYLE_MSGLIST_DEFAULT))
acct := ml.aerc.SelectedAccount()
store := ml.Store() store := ml.Store()
if store == nil { if store == nil || acct == nil {
if ml.isInitalizing { if ml.isInitalizing {
ml.spinner.Draw(ctx) ml.spinner.Draw(ctx)
return return
@ -89,7 +91,7 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
row int = 0 row int = 0
) )
if ml.aerc.SelectedAccount().UiConfig().ThreadingEnabled || store.BuildThreads() { if uiConfig.ThreadingEnabled || store.BuildThreads() {
threads := store.Threads threads := store.Threads
counter := len(store.Uids()) counter := len(store.Uids())
@ -119,8 +121,8 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
} }
} }
fmtCtx := format.Ctx{ fmtCtx := format.Ctx{
FromAddress: ml.aerc.SelectedAccount().acct.From, FromAddress: acct.acct.From,
AccountName: ml.aerc.SelectedAccount().Name(), AccountName: acct.Name(),
MsgInfo: msg, MsgInfo: msg,
MsgNum: row, MsgNum: row,
MsgIsMarked: store.IsMarked(t.Uid), MsgIsMarked: store.IsMarked(t.Uid),
@ -144,8 +146,8 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
uid := uids[i] uid := uids[i]
msg := store.Messages[uid] msg := store.Messages[uid]
fmtCtx := format.Ctx{ fmtCtx := format.Ctx{
FromAddress: ml.aerc.SelectedAccount().acct.From, FromAddress: acct.acct.From,
AccountName: ml.aerc.SelectedAccount().Name(), AccountName: acct.Name(),
MsgInfo: msg, MsgInfo: msg,
MsgNum: row, MsgNum: row,
MsgIsMarked: store.IsMarked(uid), MsgIsMarked: store.IsMarked(uid),
@ -183,8 +185,9 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
func (ml *MessageList) drawRow(textWidth int, ctx *ui.Context, uid uint32, row int, needsHeaders *[]uint32, fmtCtx format.Ctx) bool { func (ml *MessageList) drawRow(textWidth int, ctx *ui.Context, uid uint32, row int, needsHeaders *[]uint32, fmtCtx format.Ctx) bool {
store := ml.store store := ml.store
msg := store.Messages[uid] msg := store.Messages[uid]
acct := ml.aerc.SelectedAccount()
if row >= ctx.Height() { if row >= ctx.Height() || acct == nil {
return true return true
} }
@ -195,8 +198,8 @@ func (ml *MessageList) drawRow(textWidth int, ctx *ui.Context, uid uint32, row i
} }
confParams := map[config.ContextType]string{ confParams := map[config.ContextType]string{
config.UI_CONTEXT_ACCOUNT: ml.aerc.SelectedAccount().AccountConfig().Name, config.UI_CONTEXT_ACCOUNT: acct.AccountConfig().Name,
config.UI_CONTEXT_FOLDER: ml.aerc.SelectedAccount().Directories().Selected(), config.UI_CONTEXT_FOLDER: acct.Directories().Selected(),
} }
if msg.Envelope != nil { if msg.Envelope != nil {
confParams[config.UI_CONTEXT_SUBJECT] = msg.Envelope.Subject confParams[config.UI_CONTEXT_SUBJECT] = msg.Envelope.Subject
@ -288,7 +291,7 @@ func (ml *MessageList) MouseEvent(localX int, localY int, event tcell.Event) {
if ok { if ok {
ml.Select(selectedMsg) ml.Select(selectedMsg)
acct := ml.aerc.SelectedAccount() acct := ml.aerc.SelectedAccount()
if acct.Messages().Empty() { if acct == nil || acct.Messages().Empty() {
return return
} }
store := acct.Messages().Store() store := acct.Messages().Store()
@ -433,7 +436,7 @@ func (ml *MessageList) ensureScroll() {
} }
func (ml *MessageList) drawEmptyMessage(ctx *ui.Context) { func (ml *MessageList) drawEmptyMessage(ctx *ui.Context) {
uiConfig := ml.aerc.SelectedAccount().UiConfig() uiConfig := ml.aerc.SelectedAccountUiConfig()
msg := uiConfig.EmptyMessage msg := uiConfig.EmptyMessage
ctx.Printf((ctx.Width()/2)-(len(msg)/2), 0, ctx.Printf((ctx.Width()/2)-(len(msg)/2), 0,
uiConfig.GetStyle(config.STYLE_MSGLIST_DEFAULT), "%s", msg) uiConfig.GetStyle(config.STYLE_MSGLIST_DEFAULT), "%s", msg)