~rjarry/aerc

aerc/commands/account/recover.go -rw-r--r-- 2.6 KiB
9fdc7acfTim Culverhouse cache: fetch flags from UI 4 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package account

import (
	"bytes"
	"errors"
	"io"
	"os"
	"path/filepath"

	"git.sr.ht/~rjarry/aerc/commands"
	"git.sr.ht/~rjarry/aerc/logging"
	"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 make([]string, 0)
	}
	// if nothing is entered yet, return all files
	if len(args) == 0 {
		return files
	}
	switch args[0] {
	case "-":
		return []string{"-f"}
	case "-f":
		if len(args) == 1 {
			for i, file := range files {
				files[i] = args[0] + " " + file
			}
			return files
		} else {
			// only accepts one file to recover
			return commands.FilterList(files, args[1], args[0]+" ",
				aerc.SelectedAccountUiConfig().FuzzyComplete)
		}
	default:
		// only accepts one file to recover
		return commands.FilterList(files, args[0], "", aerc.SelectedAccountUiConfig().FuzzyComplete)
	}
}

func (Recover) Execute(aerc *widgets.Aerc, args []string) error {
	// Complete() expects to be passed only the arguments, not including the command name
	if len(Recover{}.Complete(aerc, args[1:])) == 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 {
		if opt.Option == '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 := io.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 func() {
		defer logging.PanicHandler()

		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
}