recover: recover emails from tempdir after a crash
implements a recover command that searches the local temp dir for aerc emails. If a file is found and selected, a new composer tab will open with the file contents as the message body. No header data is stored in the temp file and thus this information cannot be recovered. Recover will not remove the temporary file unless the force flag (-f) is explicitly used. This recovery method only works when the editor buffer is saved to disk and the Close() function of the composer has not been called yet. Sending, postponing or quitting will call the Close() function which removes the temporary file completely. After Close() is called, no recovery is possible anymore. Signed-off-by: Koni Marti <koni.marti@gmail.com>
This commit is contained in:
parent
fc07495ee8
commit
cdec23323c
|
@ -0,0 +1,110 @@
|
||||||
|
package account
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.sr.ht/~rjarry/aerc/models"
|
||||||
|
"git.sr.ht/~rjarry/aerc/widgets"
|
||||||
|
"git.sr.ht/~sircmpwn/getopt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Recover struct{}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
register(Recover{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Recover) Aliases() []string {
|
||||||
|
return []string{"recover"}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Recover) Complete(aerc *widgets.Aerc, args []string) []string {
|
||||||
|
// file name of temp file is hard-coded in the NewComposer() function
|
||||||
|
files, err := filepath.Glob(
|
||||||
|
filepath.Join(os.TempDir(), "aerc-compose-*.eml"),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
arg := strings.Join(args, " ")
|
||||||
|
if arg != "" {
|
||||||
|
for i, file := range files {
|
||||||
|
files[i] = strings.Join([]string{arg, file}, " ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return files
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Recover) Execute(aerc *widgets.Aerc, args []string) error {
|
||||||
|
if len(Recover{}.Complete(aerc, args)) == 0 {
|
||||||
|
return errors.New("No messages to recover.")
|
||||||
|
}
|
||||||
|
|
||||||
|
force := false
|
||||||
|
|
||||||
|
opts, optind, err := getopt.Getopts(args, "f")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, opt := range opts {
|
||||||
|
switch opt.Option {
|
||||||
|
case 'f':
|
||||||
|
force = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) <= optind {
|
||||||
|
return errors.New("Usage: recover [-f] <file>")
|
||||||
|
}
|
||||||
|
|
||||||
|
acct := aerc.SelectedAccount()
|
||||||
|
if acct == nil {
|
||||||
|
return errors.New("No account selected")
|
||||||
|
}
|
||||||
|
|
||||||
|
readData := func() ([]byte, error) {
|
||||||
|
recoverFile, err := os.Open(args[optind])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer recoverFile.Close()
|
||||||
|
data, err := ioutil.ReadAll(recoverFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
data, err := readData()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
composer, err := widgets.NewComposer(aerc, acct,
|
||||||
|
aerc.Config(), acct.AccountConfig(), acct.Worker(),
|
||||||
|
"", nil, models.OriginalMail{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tab := aerc.NewTab(composer, "Recovered")
|
||||||
|
composer.OnHeaderChange("Subject", func(subject string) {
|
||||||
|
tab.Name = subject
|
||||||
|
tab.Content.Invalidate()
|
||||||
|
})
|
||||||
|
go composer.AppendContents(bytes.NewReader(data))
|
||||||
|
|
||||||
|
// remove file if force flag is set
|
||||||
|
if force {
|
||||||
|
err = os.Remove(args[optind])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in New Issue