aboutsummaryrefslogtreecommitdiffstats
path: root/widgets/aerc.go
diff options
context:
space:
mode:
Diffstat (limited to 'widgets/aerc.go')
-rw-r--r--widgets/aerc.go908
1 files changed, 0 insertions, 908 deletions
diff --git a/widgets/aerc.go b/widgets/aerc.go
deleted file mode 100644
index efa13194..00000000
--- a/widgets/aerc.go
+++ /dev/null
@@ -1,908 +0,0 @@
-package widgets
-
-import (
- "errors"
- "fmt"
- "io"
- "net/url"
- "os/exec"
- "sort"
- "strings"
- "time"
-
- "github.com/ProtonMail/go-crypto/openpgp"
- "github.com/emersion/go-message/mail"
- "github.com/gdamore/tcell/v2"
- "github.com/google/shlex"
-
- "git.sr.ht/~rjarry/aerc/config"
- "git.sr.ht/~rjarry/aerc/lib"
- "git.sr.ht/~rjarry/aerc/lib/crypto"
- "git.sr.ht/~rjarry/aerc/lib/ui"
- "git.sr.ht/~rjarry/aerc/log"
- "git.sr.ht/~rjarry/aerc/models"
- "git.sr.ht/~rjarry/aerc/worker/types"
-)
-
-type Aerc struct {
- accounts map[string]*AccountView
- cmd func([]string, *config.AccountConfig, *models.MessageInfo) error
- cmdHistory lib.History
- complete func(cmd string) ([]string, string)
- focused ui.Interactive
- grid *ui.Grid
- simulating int
- statusbar *ui.Stack
- statusline *StatusLine
- pasting bool
- pendingKeys []config.KeyStroke
- prompts *ui.Stack
- tabs *ui.Tabs
- ui *ui.UI
- beep func() error
- dialog ui.DrawableInteractive
-
- Crypto crypto.Provider
-}
-
-type Choice struct {
- Key string
- Text string
- Command []string
-}
-
-func NewAerc(
- crypto crypto.Provider,
- cmd func([]string, *config.AccountConfig, *models.MessageInfo) error,
- complete func(cmd string) ([]string, string), cmdHistory lib.History,
- deferLoop chan struct{},
-) *Aerc {
- tabs := ui.NewTabs(config.Ui)
-
- statusbar := ui.NewStack(config.Ui)
- statusline := &StatusLine{}
- statusbar.Push(statusline)
-
- grid := ui.NewGrid().Rows([]ui.GridSpec{
- {Strategy: ui.SIZE_EXACT, Size: ui.Const(1)},
- {Strategy: ui.SIZE_WEIGHT, Size: ui.Const(1)},
- {Strategy: ui.SIZE_EXACT, Size: ui.Const(1)},
- }).Columns([]ui.GridSpec{
- {Strategy: ui.SIZE_WEIGHT, Size: ui.Const(1)},
- })
- grid.AddChild(tabs.TabStrip)
- grid.AddChild(tabs.TabContent).At(1, 0)
- grid.AddChild(statusbar).At(2, 0)
-
- aerc := &Aerc{
- accounts: make(map[string]*AccountView),
- cmd: cmd,
- cmdHistory: cmdHistory,
- complete: complete,
- grid: grid,
- statusbar: statusbar,
- statusline: statusline,
- prompts: ui.NewStack(config.Ui),
- tabs: tabs,
- Crypto: crypto,
- }
-
- statusline.SetAerc(aerc)
-
- for _, acct := range config.Accounts {
- view, err := NewAccountView(aerc, acct, aerc, deferLoop)
- if err != nil {
- tabs.Add(errorScreen(err.Error()), acct.Name, nil)
- } else {
- aerc.accounts[acct.Name] = view
- view.tab = tabs.Add(view, acct.Name, view.UiConfig())
- }
- }
-
- if len(config.Accounts) == 0 {
- wizard := NewAccountWizard(aerc)
- wizard.Focus(true)
- aerc.NewTab(wizard, "New account")
- }
-
- tabs.Select(0)
-
- tabs.CloseTab = func(index int) {
- tab := aerc.tabs.Get(index)
- if tab == nil {
- return
- }
- switch content := tab.Content.(type) {
- case *AccountView:
- return
- case *AccountWizard:
- return
- default:
- aerc.RemoveTab(content, true)
- }
- }
-
- aerc.showConfigWarnings()
-
- return aerc
-}
-
-func (aerc *Aerc) showConfigWarnings() {
- var dialogs []ui.DrawableInteractive
-
- callback := func(string, error) {
- aerc.CloseDialog()
- if len(dialogs) > 0 {
- d := dialogs[0]
- dialogs = dialogs[1:]
- aerc.AddDialog(d)
- }
- }
-
- for _, w := range config.Warnings {
- dialogs = append(dialogs, NewSelectorDialog(
- w.Title, w.Body, []string{"OK"}, 0,
- aerc.SelectedAccountUiConfig(),
- callback,
- ))
- }
-
- callback("", nil)
-}
-
-func (aerc *Aerc) OnBeep(f func() error) {
- aerc.beep = f
-}
-
-func (aerc *Aerc) Beep() {
- if aerc.beep == nil {
- log.Warnf("should beep, but no beeper")
- return
- }
- if err := aerc.beep(); err != nil {
- log.Errorf("tried to beep, but could not: %v", err)
- }
-}
-
-func (aerc *Aerc) HandleMessage(msg types.WorkerMessage) {
- if acct, ok := aerc.accounts[msg.Account()]; ok {
- acct.onMessage(msg)
- }
-}
-
-func (aerc *Aerc) Invalidate() {
- ui.Invalidate()
-}
-
-func (aerc *Aerc) Focus(focus bool) {
- // who cares
-}
-
-func (aerc *Aerc) Draw(ctx *ui.Context) {
- if len(aerc.prompts.Children()) > 0 {
- previous := aerc.focused
- prompt := aerc.prompts.Pop().(*ExLine)
- prompt.finish = func() {
- aerc.statusbar.Pop()
- aerc.focus(previous)
- }
-
- aerc.statusbar.Push(prompt)
- aerc.focus(prompt)
- }
- aerc.grid.Draw(ctx)
- if aerc.dialog != nil {
- if w, h := ctx.Width(), ctx.Height(); w > 8 && h > 4 {
- if d, ok := aerc.dialog.(Dialog); ok {
- start, height := d.ContextHeight()
- aerc.dialog.Draw(
- ctx.Subcontext(4, start(h),
- w-8, height(h)))
- } else {
- aerc.dialog.Draw(ctx.Subcontext(4, h/2-2, w-8, 4))
- }
- }
- }
-}
-
-func (aerc *Aerc) HumanReadableBindings() []string {
- var result []string
- binds := aerc.getBindings()
- format := func(s string) string {
- return strings.ReplaceAll(s, "%", "%%")
- }
- fmtStr := "%10s %s"
- for _, bind := range binds.Bindings {
- result = append(result, fmt.Sprintf(fmtStr,
- format(config.FormatKeyStrokes(bind.Input)),
- format(config.FormatKeyStrokes(bind.Output)),
- ))
- }
- if binds.Globals && config.Binds.Global != nil {
- for _, bind := range config.Binds.Global.Bindings {
- result = append(result, fmt.Sprintf(fmtStr+" (Globals)",
- format(config.FormatKeyStrokes(bind.Input)),
- format(config.FormatKeyStrokes(bind.Output)),
- ))
- }
- }
- result = append(result, fmt.Sprintf(fmtStr,
- "$ex",
- fmt.Sprintf("'%c'", binds.ExKey.Rune),
- ))
- result = append(result, fmt.Sprintf(fmtStr,
- "Globals",
- fmt.Sprintf("%v", binds.Globals),
- ))
- sort.Strings(result)
- return result
-}
-
-func (aerc *Aerc) getBindings() *config.KeyBindings {
- selectedAccountName := ""
- if aerc.SelectedAccount() != nil {
- selectedAccountName = aerc.SelectedAccount().acct.Name
- }
- switch view := aerc.SelectedTabContent().(type) {
- case *AccountView:
- binds := config.Binds.MessageList.ForAccount(selectedAccountName)
- return binds.ForFolder(view.SelectedDirectory())
- case *AccountWizard:
- return config.Binds.AccountWizard
- case *Composer:
- switch view.Bindings() {
- case "compose::editor":
- return config.Binds.ComposeEditor.ForAccount(
- selectedAccountName)
- case "compose::review":
- return config.Binds.ComposeReview.ForAccount(
- selectedAccountName)
- default:
- return config.Binds.Compose.ForAccount(
- selectedAccountName)
- }
- case *MessageViewer:
- switch view.Bindings() {
- case "view::passthrough":
- return config.Binds.MessageViewPassthrough.ForAccount(
- selectedAccountName)
- default:
- return config.Binds.MessageView.ForAccount(
- selectedAccountName)
- }
- case *Terminal:
- return config.Binds.Terminal
- default:
- return config.Binds.Global
- }
-}
-
-func (aerc *Aerc) simulate(strokes []config.KeyStroke) {
- aerc.pendingKeys = []config.KeyStroke{}
- aerc.simulating += 1
- for _, stroke := range strokes {
- simulated := tcell.NewEventKey(
- stroke.Key, stroke.Rune, tcell.ModNone)
- aerc.Event(simulated)
- }
- aerc.simulating -= 1
- // If we are still focused on the exline, turn on tab complete
- if exline, ok := aerc.focused.(*ExLine); ok {
- exline.TabComplete(func(cmd string) ([]string, string) {
- return aerc.complete(cmd)
- })
- // send tab to text input to trigger completion
- exline.Event(tcell.NewEventKey(tcell.KeyTab, 0, tcell.ModNone))
- }
-}
-
-func (aerc *Aerc) Event(event tcell.Event) bool {
- if aerc.dialog != nil {
- return aerc.dialog.Event(event)
- }
-
- if aerc.focused != nil {
- return aerc.focused.Event(event)
- }
-
- switch event := event.(type) {
- case *tcell.EventKey:
- // If we are in a bracketed paste, don't process the keys for
- // bindings
- if aerc.pasting {
- interactive, ok := aerc.SelectedTabContent().(ui.Interactive)
- if ok {
- return interactive.Event(event)
- }
- return false
- }
- aerc.statusline.Expire()
- aerc.pendingKeys = append(aerc.pendingKeys, config.KeyStroke{
- Modifiers: event.Modifiers(),
- Key: event.Key(),
- Rune: event.Rune(),
- })
- ui.Invalidate()
- bindings := aerc.getBindings()
- incomplete := false
- result, strokes := bindings.GetBinding(aerc.pendingKeys)
- switch result {
- case config.BINDING_FOUND:
- aerc.simulate(strokes)
- return true
- case config.BINDING_INCOMPLETE:
- incomplete = true
- case config.BINDING_NOT_FOUND:
- }
- if bindings.Globals {
- result, strokes = config.Binds.Global.GetBinding(aerc.pendingKeys)
- switch result {
- case config.BINDING_FOUND:
- aerc.simulate(strokes)
- return true
- case config.BINDING_INCOMPLETE:
- incomplete = true
- case config.BINDING_NOT_FOUND:
- }
- }
- if !incomplete {
- aerc.pendingKeys = []config.KeyStroke{}
- exKey := bindings.ExKey
- if aerc.simulating > 0 {
- // Keybindings still use : even if you change the ex key
- exKey = config.Binds.Global.ExKey
- }
- if aerc.isExKey(event, exKey) {
- aerc.BeginExCommand("")
- return true
- }
- interactive, ok := aerc.SelectedTabContent().(ui.Interactive)
- if ok {
- return interactive.Event(event)
- }
- return false
- }
- case *tcell.EventMouse:
- x, y := event.Position()
- aerc.grid.MouseEvent(x, y, event)
- return true
- case *tcell.EventPaste:
- if event.Start() {
- aerc.pasting = true
- }
- if event.End() {
- aerc.pasting = false
- }
- interactive, ok := aerc.SelectedTabContent().(ui.Interactive)
- if ok {
- return interactive.Event(event)
- }
- return false
- }
- return false
-}
-
-func (aerc *Aerc) SelectedAccount() *AccountView {
- return aerc.account(aerc.SelectedTabContent())
-}
-
-func (aerc *Aerc) Account(name string) (*AccountView, error) {
- if acct, ok := aerc.accounts[name]; ok {
- return acct, nil
- }
- return nil, fmt.Errorf("account <%s> not found", name)
-}
-
-func (aerc *Aerc) PrevAccount() (*AccountView, error) {
- cur := aerc.SelectedAccount()
- if cur == nil {
- return nil, fmt.Errorf("no account selected, cannot get prev")
- }
- for i, conf := range config.Accounts {
- if conf.Name == cur.Name() {
- i -= 1
- if i == -1 {
- i = len(config.Accounts) - 1
- }
- conf = config.Accounts[i]
- return aerc.Account(conf.Name)
- }
- }
- return nil, fmt.Errorf("no prev account")
-}
-
-func (aerc *Aerc) NextAccount() (*AccountView, error) {
- cur := aerc.SelectedAccount()
- if cur == nil {
- return nil, fmt.Errorf("no account selected, cannot get next")
- }
- for i, conf := range config.Accounts {
- if conf.Name == cur.Name() {
- i += 1
- if i == len(config.Accounts) {
- i = 0
- }
- conf = config.Accounts[i]
- return aerc.Account(conf.Name)
- }
- }
- return nil, fmt.Errorf("no next account")
-}
-
-func (aerc *Aerc) AccountNames() []string {
- results := make([]string, 0)
- for name := range aerc.accounts {
- results = append(results, name)
- }
- return results
-}
-
-func (aerc *Aerc) account(d ui.Drawable) *AccountView {
- switch tab := d.(type) {
- case *AccountView:
- return tab
- case *MessageViewer:
- return tab.SelectedAccount()
- case *Composer:
- return tab.Account()
- }
- return nil
-}
-
-func (aerc *Aerc) SelectedAccountUiConfig() *config.UIConfig {
- acct := aerc.SelectedAccount()
- if acct == nil {
- return config.Ui
- }
- return acct.UiConfig()
-}
-
-func (aerc *Aerc) SelectedTabContent() ui.Drawable {
- tab := aerc.tabs.Selected()
- if tab == nil {
- return nil
- }
- return tab.Content
-}
-
-func (aerc *Aerc) SelectedTab() *ui.Tab {
- return aerc.tabs.Selected()
-}
-
-func (aerc *Aerc) NewTab(clickable ui.Drawable, name string) *ui.Tab {
- uiConf := config.Ui
- if acct := aerc.account(clickable); acct != nil {
- uiConf = acct.UiConfig()
- }
- tab := aerc.tabs.Add(clickable, name, uiConf)
- aerc.UpdateStatus()
- return tab
-}
-
-func (aerc *Aerc) RemoveTab(tab ui.Drawable, closeContent bool) {
- aerc.tabs.Remove(tab)
- aerc.UpdateStatus()
- if content, ok := tab.(ui.Closeable); ok && closeContent {
- content.Close()
- }
-}
-
-func (aerc *Aerc) ReplaceTab(tabSrc ui.Drawable, tabTarget ui.Drawable, name string, closeSrc bool) {
- aerc.tabs.Replace(tabSrc, tabTarget, name)
- if content, ok := tabSrc.(ui.Closeable); ok && closeSrc {
- content.Close()
- }
-}
-
-func (aerc *Aerc) MoveTab(i int, relative bool) {
- aerc.tabs.MoveTab(i, relative)
-}
-
-func (aerc *Aerc) PinTab() {
- aerc.tabs.PinTab()
-}
-
-func (aerc *Aerc) UnpinTab() {
- aerc.tabs.UnpinTab()
-}
-
-func (aerc *Aerc) NextTab() {
- aerc.tabs.NextTab()
-}
-
-func (aerc *Aerc) PrevTab() {
- aerc.tabs.PrevTab()
-}
-
-func (aerc *Aerc) SelectTab(name string) bool {
- ok := aerc.tabs.SelectName(name)
- if ok {
- aerc.UpdateStatus()
- }
- return ok
-}
-
-func (aerc *Aerc) SelectTabIndex(index int) bool {
- ok := aerc.tabs.Select(index)
- if ok {
- aerc.UpdateStatus()
- }
- return ok
-}
-
-func (aerc *Aerc) TabNames() []string {
- return aerc.tabs.Names()
-}
-
-func (aerc *Aerc) SelectPreviousTab() bool {
- return aerc.tabs.SelectPrevious()
-}
-
-func (aerc *Aerc) UpdateStatus() {
- if acct := aerc.SelectedAccount(); acct != nil {
- aerc.statusline.Update(acct)
- } else {
- aerc.statusline.Clear()
- }
-}
-
-func (aerc *Aerc) SetError(err string) {
- aerc.statusline.SetError(err)
-}
-
-func (aerc *Aerc) PushStatus(text string, expiry time.Duration) *StatusMessage {
- return aerc.statusline.Push(text, expiry)
-}
-
-func (aerc *Aerc) PushError(text string) *StatusMessage {
- return aerc.statusline.PushError(text)
-}
-
-func (aerc *Aerc) PushWarning(text string) *StatusMessage {
- return aerc.statusline.PushWarning(text)
-}
-
-func (aerc *Aerc) PushSuccess(text string) *StatusMessage {
- return aerc.statusline.PushSuccess(text)
-}
-
-func (aerc *Aerc) focus(item ui.Interactive) {
- if aerc.focused == item {
- return
- }
- if aerc.focused != nil {
- aerc.focused.Focus(false)
- }
- aerc.focused = item
- interactive, ok := aerc.SelectedTabContent().(ui.Interactive)
- if item != nil {
- item.Focus(true)
- if ok {
- interactive.Focus(false)
- }
- } else if ok {
- interactive.Focus(true)
- }
-}
-
-func (aerc *Aerc) BeginExCommand(cmd string) {
- previous := aerc.focused
- var tabComplete func(string) ([]string, string)
- if aerc.simulating != 0 {
- // Don't try to draw completions for simulated events
- tabComplete = nil
- } else {
- tabComplete = func(cmd string) ([]string, string) {
- return aerc.complete(cmd)
- }
- }
- exline := NewExLine(cmd, func(cmd string) {
- parts, err := shlex.Split(cmd)
- if err != nil {
- aerc.PushError(err.Error())
- }
- err = aerc.cmd(parts, nil, nil)
- if err != nil {
- aerc.PushError(err.Error())
- }
- // only add to history if this is an unsimulated command,
- // ie one not executed from a keybinding
- if aerc.simulating == 0 {
- aerc.cmdHistory.Add(cmd)
- }
- }, func() {
- aerc.statusbar.Pop()
- aerc.focus(previous)
- }, tabComplete, aerc.cmdHistory)
- aerc.statusbar.Push(exline)
- aerc.focus(exline)
-}
-
-func (aerc *Aerc) PushPrompt(prompt *ExLine) {
- aerc.prompts.Push(prompt)
-}
-
-func (aerc *Aerc) RegisterPrompt(prompt string, cmd []string) {
- p := NewPrompt(prompt, func(text string) {
- if text != "" {
- cmd = append(cmd, text)
- }
- err := aerc.cmd(cmd, nil, nil)
- if err != nil {
- aerc.PushError(err.Error())
- }
- }, func(cmd string) ([]string, string) {
- return nil, "" // TODO: completions
- })
- aerc.prompts.Push(p)
-}
-
-func (aerc *Aerc) RegisterChoices(choices []Choice) {
- cmds := make(map[string][]string)
- texts := []string{}
- for _, c := range choices {
- text := fmt.Sprintf("[%s] %s", c.Key, c.Text)
- if strings.Contains(c.Text, c.Key) {
- text = strings.Replace(c.Text, c.Key, "["+c.Key+"]", 1)
- }
- texts = append(texts, text)
- cmds[c.Key] = c.Command
- }
- prompt := strings.Join(texts, ", ") + "? "
- p := NewPrompt(prompt, func(text string) {
- cmd, ok := cmds[text]
- if !ok {
- return
- }
- err := aerc.cmd(cmd, nil, nil)
- if err != nil {
- aerc.PushError(err.Error())
- }
- }, func(cmd string) ([]string, string) {
- return nil, "" // TODO: completions
- })
- aerc.prompts.Push(p)
-}
-
-func (aerc *Aerc) Mailto(addr *url.URL) error {
- var subject string
- var body string
- var acctName string
- var attachments []string
- h := &mail.Header{}
- to, err := mail.ParseAddressList(addr.Opaque)
- if err != nil && addr.Opaque != "" {
- return fmt.Errorf("Could not parse to: %w", err)
- }
- h.SetAddressList("to", to)
- template := config.Templates.NewMessage
- for key, vals := range addr.Query() {
- switch strings.ToLower(key) {
- case "account":
- acctName = strings.Join(vals, "")
- case "bcc":
- list, err := mail.ParseAddressList(strings.Join(vals, ","))
- if err != nil {
- break
- }
- h.SetAddressList("Bcc", list)
- case "body":
- body = strings.Join(vals, "\n")
- case "cc":
- list, err := mail.ParseAddressList(strings.Join(vals, ","))
- if err != nil {
- break
- }
- h.SetAddressList("Cc", list)
- case "in-reply-to":
- for i, msgID := range vals {
- if len(msgID) > 1 && msgID[0] == '<' &&
- msgID[len(msgID)-1] == '>' {
- vals[i] = msgID[1 : len(msgID)-1]
- }
- }
- h.SetMsgIDList("In-Reply-To", vals)
- case "subject":
- subject = strings.Join(vals, ",")
- h.SetText("Subject", subject)
- case "template":
- template = strings.Join(vals, "")
- log.Tracef("template set to %s", template)
- case "attach":
- for _, path := range vals {
- // remove a potential file:// prefix.
- attachments = append(attachments, strings.TrimPrefix(path, "file://"))
- }
- default:
- // any other header gets ignored on purpose to avoid control headers
- // being injected
- }
- }
-
- acct := aerc.SelectedAccount()
- if acctName != "" {
- if a, ok := aerc.accounts[acctName]; ok && a != nil {
- acct = a
- }
- }
-
- if acct == nil {
- return errors.New("No account selected")
- }
-
- defer ui.Invalidate()
-
- composer, err := NewComposer(aerc, acct,
- acct.AccountConfig(), acct.Worker(),
- config.Compose.EditHeaders, template, h, nil,
- strings.NewReader(body))
- if err != nil {
- return err
- }
- composer.FocusEditor("subject")
- title := "New email"
- if subject != "" {
- title = subject
- composer.FocusTerminal()
- }
- if to == nil {
- composer.FocusEditor("to")
- }
- composer.Tab = aerc.NewTab(composer, title)
-
- for _, file := range attachments {
- composer.AddAttachment(file)
- }
- return nil
-}
-
-func (aerc *Aerc) Mbox(source string) error {
- acctConf := config.AccountConfig{}
- if selectedAcct := aerc.SelectedAccount(); selectedAcct != nil {
- acctConf = *selectedAcct.acct
- info := fmt.Sprintf("Loading outgoing mbox mail settings from account [%s]", selectedAcct.Name())
- aerc.PushStatus(info, 10*time.Second)
- log.Debugf(info)
- } else {
- acctConf.From = &mail.Address{Address: "user@localhost"}
- }
- acctConf.Name = "mbox"
- acctConf.Source = source
- acctConf.Default = "INBOX"
- acctConf.Archive = "Archive"
- acctConf.Postpone = "Drafts"
- acctConf.CopyTo = "Sent"
-
- defer ui.Invalidate()
-
- mboxView, err := NewAccountView(aerc, &acctConf, aerc, nil)
- if err != nil {
- aerc.NewTab(errorScreen(err.Error()), acctConf.Name)
- } else {
- aerc.accounts[acctConf.Name] = mboxView
- aerc.NewTab(mboxView, acctConf.Name)
- }
- return nil
-}
-
-func (aerc *Aerc) Command(args []string) error {
- defer ui.Invalidate()
- return aerc.cmd(args, nil, nil)
-}
-
-func (aerc *Aerc) CloseBackends() error {
- var returnErr error
- for _, acct := range aerc.accounts {
- var raw interface{} = acct.worker.Backend
- c, ok := raw.(io.Closer)
- if !ok {
- continue
- }
- err := c.Close()
- if err != nil {
- returnErr = err
- log.Errorf("Closing backend failed for %s: %v", acct.Name(), err)
- }
- }
- return returnErr
-}
-
-func (aerc *Aerc) AddDialog(d ui.DrawableInteractive) {
- aerc.dialog = d
- aerc.Invalidate()
-}
-
-func (aerc *Aerc) CloseDialog() {
- aerc.dialog = nil
- aerc.Invalidate()
-}
-
-func (aerc *Aerc) GetPassword(title string, prompt string) (chText chan string, chErr chan error) {
- chText = make(chan string, 1)
- chErr = make(chan error, 1)
- getPasswd := NewGetPasswd(title, prompt, func(pw string, err error) {
- defer func() {
- close(chErr)
- close(chText)
- aerc.CloseDialog()
- }()
- if err != nil {
- chErr <- err
- return
- }
- chErr <- nil
- chText <- pw
- })
- aerc.AddDialog(getPasswd)
-
- return
-}
-
-func (aerc *Aerc) Initialize(ui *ui.UI) {
- aerc.ui = ui
-}
-
-func (aerc *Aerc) DecryptKeys(keys []openpgp.Key, symmetric bool) (b []byte, err error) {
- for _, key := range keys {
- ident := key.Entity.PrimaryIdentity()
- chPass, chErr := aerc.GetPassword("Decrypt PGP private key",
- fmt.Sprintf("Enter password for %s (%8X)\nPress <ESC> to cancel",
- ident.Name, key.PublicKey.KeyId))
-
- for err := range chErr {
- if err != nil {
- return nil, err
- }
- pass := <-chPass
- err = key.PrivateKey.Decrypt([]byte(pass))
- return nil, err
- }
- }
- return nil, err
-}
-
-// errorScreen is a widget that draws an error in the middle of the context
-func errorScreen(s string) ui.Drawable {
- errstyle := config.Ui.GetStyle(config.STYLE_ERROR)
- text := ui.NewText(s, errstyle).Strategy(ui.TEXT_CENTER)
- grid := ui.NewGrid().Rows([]ui.GridSpec{
- {Strategy: ui.SIZE_WEIGHT, Size: ui.Const(1)},
- {Strategy: ui.SIZE_EXACT, Size: ui.Const(1)},
- {Strategy: ui.SIZE_WEIGHT, Size: ui.Const(1)},
- }).Columns([]ui.GridSpec{
- {Strategy: ui.SIZE_WEIGHT, Size: ui.Const(1)},
- })
- grid.AddChild(ui.NewFill(' ', tcell.StyleDefault)).At(0, 0)
- grid.AddChild(text).At(1, 0)
- grid.AddChild(ui.NewFill(' ', tcell.StyleDefault)).At(2, 0)
- return grid
-}
-
-func (aerc *Aerc) isExKey(event *tcell.EventKey, exKey config.KeyStroke) bool {
- if event.Key() == tcell.KeyRune {
- // Compare runes if it's a KeyRune
- return event.Modifiers() == exKey.Modifiers && event.Rune() == exKey.Rune
- }
- return event.Modifiers() == exKey.Modifiers && event.Key() == exKey.Key
-}
-
-// CmdFallbackSearch checks cmds for the first executable availabe in PATH. An error is
-// returned if none are found
-func (aerc *Aerc) CmdFallbackSearch(cmds []string) (string, error) {
- var tried []string
- for _, cmd := range cmds {
- if cmd == "" {
- continue
- }
- params := strings.Split(cmd, " ")
- _, err := exec.LookPath(params[0])
- if err != nil {
- tried = append(tried, cmd)
- warn := fmt.Sprintf("cmd '%s' not found in PATH, using fallback", cmd)
- aerc.PushWarning(warn)
- continue
- }
- return cmd, nil
- }
- return "", fmt.Errorf("no command found in PATH: %s", tried)
-}