Remove ability to specify headers in the editor
Due to headers being essentially free text, we constantly run into issues with parts of the body being interpreted as headers. Remove the ability to overwrite headers to avoid that, while keeping the ability to specify headers in the template files. Fixes #383
This commit is contained in:
parent
3fa9ae3edb
commit
acf69b7490
3 changed files with 49 additions and 153 deletions
|
@ -46,7 +46,7 @@ func (Compose) Execute(aerc *widgets.Aerc, args []string) error {
|
|||
}
|
||||
tab.Content.Invalidate()
|
||||
})
|
||||
go composer.PrependContents(strings.NewReader(body))
|
||||
go composer.AppendContents(strings.NewReader(body))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package templates
|
|||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"net/mail"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
@ -185,7 +186,7 @@ func findTemplate(templateName string, templateDirs []string) (string, error) {
|
|||
return "", errors.New("Can't find template - " + templateName)
|
||||
}
|
||||
|
||||
func ParseTemplateFromFile(templateName string, templateDirs []string, data interface{}) ([]byte, error) {
|
||||
func ParseTemplateFromFile(templateName string, templateDirs []string, data interface{}) (io.Reader, error) {
|
||||
templateFile, err := findTemplate(templateName, templateDirs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -196,11 +197,11 @@ func ParseTemplateFromFile(templateName string, templateDirs []string, data inte
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var outString bytes.Buffer
|
||||
if err := emailTemplate.Execute(&outString, data); err != nil {
|
||||
var body bytes.Buffer
|
||||
if err := emailTemplate.Execute(&body, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return outString.Bytes(), nil
|
||||
return &body, nil
|
||||
}
|
||||
|
||||
func ParseTemplate(templateText string, data interface{}) ([]byte, error) {
|
||||
|
|
|
@ -15,7 +15,6 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/emersion/go-message"
|
||||
"github.com/emersion/go-message/mail"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/mattn/go-runewidth"
|
||||
|
@ -99,10 +98,10 @@ func NewComposer(aerc *Aerc, conf *config.AercConfig,
|
|||
completer: cmpl,
|
||||
}
|
||||
|
||||
c.AddSignature()
|
||||
if err := c.AddTemplate(template, templateData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.AddSignature()
|
||||
|
||||
c.updateGrid()
|
||||
c.ShowTerminal()
|
||||
|
@ -172,16 +171,6 @@ func (c *Composer) SetContents(reader io.Reader) *Composer {
|
|||
return c
|
||||
}
|
||||
|
||||
func (c *Composer) PrependContents(reader io.Reader) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
c.email.Seek(0, io.SeekStart)
|
||||
io.Copy(buf, c.email)
|
||||
c.email.Seek(0, io.SeekStart)
|
||||
io.Copy(c.email, reader)
|
||||
io.Copy(c.email, buf)
|
||||
c.email.Sync()
|
||||
}
|
||||
|
||||
func (c *Composer) AppendContents(reader io.Reader) {
|
||||
c.email.Seek(0, io.SeekEnd)
|
||||
io.Copy(c.email, reader)
|
||||
|
@ -198,67 +187,29 @@ func (c *Composer) AddTemplate(template string, data interface{}) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.addTemplate(templateText)
|
||||
}
|
||||
|
||||
func (c *Composer) AddTemplateFromString(template string, data interface{}) error {
|
||||
if template == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
templateText, err := templates.ParseTemplate(template, data)
|
||||
mr, err := mail.CreateReader(templateText)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("Template loading failed: %v", err)
|
||||
}
|
||||
return c.addTemplate(templateText)
|
||||
}
|
||||
|
||||
func (c *Composer) addTemplate(templateText []byte) error {
|
||||
reader, err := mail.CreateReader(bytes.NewReader(templateText))
|
||||
if err != nil {
|
||||
// encountering an error when reading the template probably
|
||||
// means the template didn't evaluate to a properly formatted
|
||||
// mail file.
|
||||
// This is fine, we still want to support simple body templates
|
||||
// that don't include headers.
|
||||
//
|
||||
// Just prepend the rendered template in that case. This
|
||||
// basically equals the previous behavior.
|
||||
c.PrependContents(bytes.NewReader(templateText))
|
||||
return nil
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
// populate header editors
|
||||
header := reader.Header
|
||||
mhdr := (*message.Header)(&header.Header)
|
||||
for _, editor := range c.editors {
|
||||
if mhdr.Has(editor.name) {
|
||||
editor.input.Set(mhdr.Get(editor.name))
|
||||
// remove header fields that have editors
|
||||
mhdr.Del(editor.name)
|
||||
// add the headers contained in the template to the default headers
|
||||
hf := mr.Header.Fields()
|
||||
for hf.Next() {
|
||||
var val string
|
||||
var err error
|
||||
if val, err = hf.Text(); err != nil {
|
||||
val = hf.Value()
|
||||
}
|
||||
c.defaults[hf.Key()] = val
|
||||
}
|
||||
|
||||
part, err := reader.NextPart()
|
||||
part, err := mr.NextPart()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "reader.NextPart")
|
||||
}
|
||||
c.PrependContents(part.Body)
|
||||
|
||||
var (
|
||||
headers string
|
||||
fds = mhdr.Fields()
|
||||
)
|
||||
for fds.Next() {
|
||||
headers += fmt.Sprintf("%s: %s\n", fds.Key(), fds.Value())
|
||||
}
|
||||
if headers != "" {
|
||||
headers += "\n"
|
||||
return fmt.Errorf("Could not get body of template: %v", err)
|
||||
}
|
||||
|
||||
// prepend header fields without editors to message body
|
||||
c.PrependContents(bytes.NewReader([]byte(headers)))
|
||||
c.AppendContents(part.Body)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -411,114 +362,58 @@ func (c *Composer) Worker() *types.Worker {
|
|||
}
|
||||
|
||||
func (c *Composer) PrepareHeader() (*mail.Header, []string, error) {
|
||||
// Extract headers from the email, if present
|
||||
if err := c.reloadEmail(); err != nil {
|
||||
return nil, nil, err
|
||||
header := &mail.Header{}
|
||||
for h, val := range c.defaults {
|
||||
if val == "" {
|
||||
continue
|
||||
}
|
||||
header.SetText(h, val)
|
||||
}
|
||||
var (
|
||||
rcpts []string
|
||||
header mail.Header
|
||||
)
|
||||
reader, err := mail.CreateReader(c.email)
|
||||
if err == nil {
|
||||
header = reader.Header
|
||||
defer reader.Close()
|
||||
} else {
|
||||
c.email.Seek(0, io.SeekStart)
|
||||
}
|
||||
// Update headers
|
||||
mhdr := (*message.Header)(&header.Header)
|
||||
mhdr.SetText("Message-Id", c.msgId)
|
||||
header.SetText("Message-Id", c.msgId)
|
||||
header.SetDate(c.date)
|
||||
|
||||
headerKeys := make([]string, 0, len(c.editors))
|
||||
for key := range c.editors {
|
||||
headerKeys = append(headerKeys, key)
|
||||
}
|
||||
// Ensure headers which require special processing are included.
|
||||
for _, key := range []string{"To", "From", "Cc", "Bcc", "Subject", "Date"} {
|
||||
if _, ok := c.editors[key]; !ok {
|
||||
headerKeys = append(headerKeys, key)
|
||||
}
|
||||
}
|
||||
|
||||
for _, h := range headerKeys {
|
||||
val := ""
|
||||
editor, ok := c.editors[h]
|
||||
if ok {
|
||||
val = editor.input.String()
|
||||
} else {
|
||||
val, _ = mhdr.Text(h)
|
||||
var rcpts []string
|
||||
for h, editor := range c.editors {
|
||||
val := editor.input.String()
|
||||
if val == "" {
|
||||
continue
|
||||
}
|
||||
switch h {
|
||||
case "Subject":
|
||||
if subject, _ := header.Subject(); subject == "" {
|
||||
header.SetSubject(val)
|
||||
}
|
||||
case "Date":
|
||||
if date, err := header.Date(); err != nil || date == (time.Time{}) {
|
||||
header.SetDate(c.date)
|
||||
}
|
||||
case "From", "To", "Cc", "Bcc": // Address headers
|
||||
if val != "" {
|
||||
hdrRcpts, err := gomail.ParseAddressList(val)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrapf(err, "ParseAddressList(%s)", val)
|
||||
}
|
||||
edRcpts := make([]*mail.Address, len(hdrRcpts))
|
||||
for i, addr := range hdrRcpts {
|
||||
edRcpts[i] = (*mail.Address)(addr)
|
||||
}
|
||||
header.SetAddressList(h, edRcpts)
|
||||
if h != "From" {
|
||||
for _, addr := range edRcpts {
|
||||
rcpts = append(rcpts, addr.Address)
|
||||
}
|
||||
hdrRcpts, err := gomail.ParseAddressList(val)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrapf(err, "ParseAddressList(%s)", val)
|
||||
}
|
||||
edRcpts := make([]*mail.Address, len(hdrRcpts))
|
||||
for i, addr := range hdrRcpts {
|
||||
edRcpts[i] = (*mail.Address)(addr)
|
||||
}
|
||||
header.SetAddressList(h, edRcpts)
|
||||
if h != "From" {
|
||||
for _, addr := range edRcpts {
|
||||
rcpts = append(rcpts, addr.Address)
|
||||
}
|
||||
}
|
||||
default:
|
||||
// Handle user configured header editors.
|
||||
if ok && !mhdr.Header.Has(h) {
|
||||
if val := editor.input.String(); val != "" {
|
||||
mhdr.SetText(h, val)
|
||||
}
|
||||
}
|
||||
header.SetText(h, val)
|
||||
}
|
||||
}
|
||||
|
||||
// Merge in additional headers
|
||||
txthdr := mhdr.Header
|
||||
for key, value := range c.defaults {
|
||||
if !txthdr.Has(key) && value != "" {
|
||||
mhdr.SetText(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
return &header, rcpts, nil
|
||||
return header, rcpts, nil
|
||||
}
|
||||
|
||||
func (c *Composer) WriteMessage(header *mail.Header, writer io.Writer) error {
|
||||
if err := c.reloadEmail(); err != nil {
|
||||
return err
|
||||
}
|
||||
var body io.Reader
|
||||
reader, err := mail.CreateReader(c.email)
|
||||
if err == nil {
|
||||
// TODO: Do we want to let users write a full blown multipart email
|
||||
// into the editor? If so this needs to change
|
||||
part, err := reader.NextPart()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "reader.NextPart")
|
||||
}
|
||||
body = part.Body
|
||||
defer reader.Close()
|
||||
} else {
|
||||
c.email.Seek(0, io.SeekStart)
|
||||
body = c.email
|
||||
}
|
||||
|
||||
if len(c.attachments) == 0 {
|
||||
// don't create a multipart email if we only have text
|
||||
return writeInlineBody(header, body, writer)
|
||||
return writeInlineBody(header, c.email, writer)
|
||||
}
|
||||
|
||||
// otherwise create a multipart email,
|
||||
|
@ -529,7 +424,7 @@ func (c *Composer) WriteMessage(header *mail.Header, writer io.Writer) error {
|
|||
}
|
||||
defer w.Close()
|
||||
|
||||
if err := writeMultipartBody(body, w); err != nil {
|
||||
if err := writeMultipartBody(c.email, w); err != nil {
|
||||
return errors.Wrap(err, "writeMultipartBody")
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue