20ec2c8eeb
Prior to this commit, the composer was based on a map[string]string. While this approach was very versatile, it lead to a constant encoding / decoding of addresses and other headers. This commit switches to a different model, where the composer is based on a header. Commands which want to interact with it can simply set some defaults they would like to have. Users can overwrite them however they like. In order to get access to the functions generating / getting the msgid go-message was upgraded.
158 lines
3.5 KiB
Go
158 lines
3.5 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"
|
|
"github.com/emersion/go-message/mail"
|
|
|
|
"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
|
|
}
|
|
}
|
|
|
|
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)
|
|
|
|
h := &mail.Header{}
|
|
subject := "Fwd: " + msg.Envelope.Subject
|
|
h.SetSubject(subject)
|
|
|
|
if len(args) != 1 {
|
|
to := strings.Join(args[optind:], ", ")
|
|
tolist, err := mail.ParseAddressList(to)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid to address(es): %v", err)
|
|
}
|
|
h.SetAddressList("to", tolist)
|
|
}
|
|
|
|
original := models.OriginalMail{
|
|
From: format.FormatAddresses(msg.Envelope.From),
|
|
Date: msg.Envelope.Date,
|
|
RFC822Headers: msg.RFC822Headers,
|
|
}
|
|
|
|
addTab := func() (*widgets.Composer, error) {
|
|
composer, err := widgets.NewComposer(aerc, acct, aerc.Config(),
|
|
acct.AccountConfig(), acct.Worker(), template, h, original)
|
|
if err != nil {
|
|
aerc.PushError("Error: " + err.Error())
|
|
return nil, err
|
|
}
|
|
|
|
tab := aerc.NewTab(composer, subject)
|
|
if !h.Has("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
|
|
}
|