aerc/commands/msg/forward.go
Reto Brunner c846307144 base models.Address on the mail.Address type
This allows us to hook into the std libs implementation of parsing related stuff.
For this, we need to get rid of the distinction between a mailbox and a host
to just a single "address" field.

However this is already the common case. All but one users immediately
concatenated the mbox/domain to a single address.

So this in effects makes it simpler for most cases and we simply do the
transformation in the special case.
2020-08-20 19:18:57 +02:00

156 lines
3.3 KiB
Go

package msg
import (
"bytes"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"strings"
"git.sr.ht/~sircmpwn/aerc/lib"
"git.sr.ht/~sircmpwn/aerc/lib/format"
"git.sr.ht/~sircmpwn/aerc/models"
"git.sr.ht/~sircmpwn/aerc/widgets"
"git.sr.ht/~sircmpwn/aerc/worker/types"
"git.sr.ht/~sircmpwn/getopt"
)
type forward struct{}
func init() {
register(forward{})
}
func (forward) Aliases() []string {
return []string{"forward"}
}
func (forward) Complete(aerc *widgets.Aerc, args []string) []string {
return nil
}
func (forward) Execute(aerc *widgets.Aerc, args []string) error {
opts, optind, err := getopt.Getopts(args, "AT:")
if err != nil {
return err
}
attach := false
template := ""
for _, opt := range opts {
switch opt.Option {
case 'A':
attach = true
case 'T':
template = opt.Value
}
}
to := ""
if len(args) != 1 {
to = strings.Join(args[optind:], ", ")
}
widget := aerc.SelectedTab().(widgets.ProvidesMessage)
acct := widget.SelectedAccount()
if acct == nil {
return errors.New("No account selected")
}
store := widget.Store()
if store == nil {
return errors.New("Cannot perform action. Messages still loading")
}
msg, err := widget.SelectedMessage()
if err != nil {
return err
}
acct.Logger().Println("Forwarding email " + msg.Envelope.MessageId)
subject := "Fwd: " + msg.Envelope.Subject
defaults := map[string]string{
"To": to,
"Subject": subject,
}
original := models.OriginalMail{}
addTab := func() (*widgets.Composer, error) {
if template != "" {
original.From = format.FormatAddresses(msg.Envelope.From)
original.Date = msg.Envelope.Date
}
composer, err := widgets.NewComposer(aerc, acct, aerc.Config(), acct.AccountConfig(),
acct.Worker(), template, defaults, original)
if err != nil {
aerc.PushError("Error: " + err.Error())
return nil, err
}
tab := aerc.NewTab(composer, subject)
if to == "" {
composer.FocusRecipient()
} else {
composer.FocusTerminal()
}
composer.OnHeaderChange("Subject", func(subject string) {
if subject == "" {
tab.Name = "New email"
} else {
tab.Name = subject
}
tab.Content.Invalidate()
})
return composer, nil
}
if attach {
tmpDir, err := ioutil.TempDir("", "aerc-tmp-attachment")
if err != nil {
return err
}
tmpFileName := path.Join(tmpDir,
strings.ReplaceAll(fmt.Sprintf("%s.eml", msg.Envelope.Subject), "/", "-"))
store.FetchFull([]uint32{msg.Uid}, func(fm *types.FullMessage) {
tmpFile, err := os.Create(tmpFileName)
if err != nil {
println(err)
// TODO: Do something with the error
addTab()
return
}
defer tmpFile.Close()
io.Copy(tmpFile, fm.Content.Reader)
composer, err := addTab()
if err != nil {
return
}
composer.AddAttachment(tmpFileName)
composer.OnClose(func(composer *widgets.Composer) {
os.RemoveAll(tmpDir)
})
})
} else {
if template == "" {
template = aerc.Config().Templates.Forwards
}
// TODO: add attachments!
part := lib.FindPlaintext(msg.BodyStructure, nil)
if part == nil {
part = lib.FindFirstNonMultipart(msg.BodyStructure, nil)
// if it's still nil here, we don't have a multipart msg, that's fine
}
store.FetchBodyPart(msg.Uid, part, func(reader io.Reader) {
buf := new(bytes.Buffer)
buf.ReadFrom(reader)
original.Text = buf.String()
addTab()
})
}
return nil
}