mirror of
https://git.sr.ht/~sircmpwn/aerc
synced 2026-02-05 06:53:37 +01:00
Previously the workers returned a mixture of decoded / encoded parts. This lead to a whole bunch of issues. This commit changes the msgviewer and the commands to assume parts to already be decoded
136 lines
3.0 KiB
Go
136 lines
3.0 KiB
Go
package msgview
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"mime/quotedprintable"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.sr.ht/~sircmpwn/getopt"
|
|
"github.com/mitchellh/go-homedir"
|
|
|
|
"git.sr.ht/~sircmpwn/aerc/commands"
|
|
"git.sr.ht/~sircmpwn/aerc/widgets"
|
|
)
|
|
|
|
type Save struct{}
|
|
|
|
func init() {
|
|
register(Save{})
|
|
}
|
|
|
|
func (Save) Aliases() []string {
|
|
return []string{"save"}
|
|
}
|
|
|
|
func (Save) Complete(aerc *widgets.Aerc, args []string) []string {
|
|
path := strings.Join(args, " ")
|
|
return commands.CompletePath(path)
|
|
}
|
|
|
|
func (Save) Execute(aerc *widgets.Aerc, args []string) error {
|
|
if len(args) == 1 {
|
|
return errors.New("Usage: :save [-p] <path>")
|
|
}
|
|
opts, optind, err := getopt.Getopts(args, "p")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var (
|
|
mkdirs bool
|
|
path string = strings.Join(args[optind:], " ")
|
|
)
|
|
|
|
for _, opt := range opts {
|
|
switch opt.Option {
|
|
case 'p':
|
|
mkdirs = true
|
|
}
|
|
}
|
|
if defaultPath := aerc.Config().General.DefaultSavePath; defaultPath != "" {
|
|
path = defaultPath
|
|
}
|
|
|
|
mv := aerc.SelectedTab().(*widgets.MessageViewer)
|
|
p := mv.SelectedMessagePart()
|
|
|
|
p.Store.FetchBodyPart(p.Msg.Uid, p.Msg.BodyStructure, p.Index, func(reader io.Reader) {
|
|
// email parts are encoded as 7bit (plaintext), quoted-printable, or base64
|
|
|
|
if strings.EqualFold(p.Part.Encoding, "base64") {
|
|
reader = base64.NewDecoder(base64.StdEncoding, reader)
|
|
} else if strings.EqualFold(p.Part.Encoding, "quoted-printable") {
|
|
reader = quotedprintable.NewReader(reader)
|
|
}
|
|
|
|
var pathIsDir bool
|
|
if path[len(path)-1:] == "/" {
|
|
pathIsDir = true
|
|
}
|
|
// Note: path expansion has to happen after test for trailing /,
|
|
// since it is stripped when path is expanded
|
|
path, err := homedir.Expand(path)
|
|
if err != nil {
|
|
aerc.PushError(" " + err.Error())
|
|
}
|
|
|
|
pathinfo, err := os.Stat(path)
|
|
if err == nil && pathinfo.IsDir() {
|
|
pathIsDir = true
|
|
} else if os.IsExist(err) && pathIsDir {
|
|
aerc.PushError("The given directory is an existing file")
|
|
}
|
|
var (
|
|
save_file string
|
|
save_dir string
|
|
)
|
|
if pathIsDir {
|
|
save_dir = path
|
|
if filename, ok := p.Part.DispositionParams["filename"]; ok {
|
|
save_file = filename
|
|
} else if filename, ok := p.Part.Params["name"]; ok {
|
|
save_file = filename
|
|
} else {
|
|
timestamp := time.Now().Format("2006-01-02-150405")
|
|
save_file = fmt.Sprintf("aerc_%v", timestamp)
|
|
}
|
|
} else {
|
|
save_file = filepath.Base(path)
|
|
save_dir = filepath.Dir(path)
|
|
}
|
|
if _, err := os.Stat(save_dir); os.IsNotExist(err) {
|
|
if mkdirs {
|
|
os.MkdirAll(save_dir, 0755)
|
|
} else {
|
|
aerc.PushError("Target directory does not exist, use " +
|
|
":save with the -p option to create it")
|
|
return
|
|
}
|
|
}
|
|
target := filepath.Clean(filepath.Join(save_dir, save_file))
|
|
|
|
f, err := os.Create(target)
|
|
if err != nil {
|
|
aerc.PushError(" " + err.Error())
|
|
return
|
|
}
|
|
defer f.Close()
|
|
|
|
_, err = io.Copy(f, reader)
|
|
if err != nil {
|
|
aerc.PushError(" " + err.Error())
|
|
return
|
|
}
|
|
|
|
aerc.PushStatus("Saved to "+target, 10*time.Second)
|
|
})
|
|
|
|
return nil
|
|
}
|