From 6f5c6e148f08266e92875e813105997317bc212b Mon Sep 17 00:00:00 2001 From: Koni Marti Date: Tue, 24 May 2022 07:36:06 +0200 Subject: compose: append text parts Append text parts to emails in the composer as multipart/alternative. Display the mime-type of the parts in the review window. Signed-off-by: Koni Marti Acked-by: Robin Jarry --- widgets/compose.go | 71 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 58 insertions(+), 13 deletions(-) (limited to 'widgets') diff --git a/widgets/compose.go b/widgets/compose.go index 6a342d41..49a8eff2 100644 --- a/widgets/compose.go +++ b/widgets/compose.go @@ -30,6 +30,12 @@ import ( "git.sr.ht/~rjarry/aerc/worker/types" ) +type Part struct { + MimeType string + Params map[string]string + Body io.Reader +} + type Composer struct { editors map[string]*headerEditor // indexes in lower case (from / cc / bcc) header *mail.Header @@ -61,6 +67,8 @@ type Composer struct { onClose []func(ti *Composer) width int + + textParts []*Part } func NewComposer(aerc *Aerc, acct *AccountView, conf *config.AercConfig, @@ -297,6 +305,14 @@ func (c *Composer) AppendContents(reader io.Reader) { c.email.Sync() } +func (c *Composer) AppendPart(mimetype string, params map[string]string, body io.Reader) error { + if !strings.HasPrefix(mimetype, "text") { + return fmt.Errorf("can only append text mimetypes") + } + c.textParts = append(c.textParts, &Part{MimeType: mimetype, Params: params, Body: body}) + return nil +} + func (c *Composer) AddTemplate(template string, data interface{}) error { if template == "" { return nil @@ -592,7 +608,7 @@ func (c *Composer) WriteMessage(header *mail.Header, writer io.Writer) error { } func writeMsgImpl(c *Composer, header *mail.Header, writer io.Writer) error { - if len(c.attachments) == 0 && !c.attachKey { + if len(c.attachments) == 0 && !c.attachKey && len(c.textParts) == 0 { // no attachements return writeInlineBody(header, c.email, writer) } else { @@ -601,7 +617,14 @@ func writeMsgImpl(c *Composer, header *mail.Header, writer io.Writer) error { if err != nil { return errors.Wrap(err, "CreateWriter") } - if err := writeMultipartBody(c.email, w); err != nil { + parts := []*Part{ + &Part{ + MimeType: "text/plain", + Params: map[string]string{"Charset": "UTF-8"}, + Body: c.email, + }, + } + if err := writeMultipartBody(append(parts, c.textParts...), w); err != nil { return errors.Wrap(err, "writeMultipartBody") } for _, a := range c.attachments { @@ -634,24 +657,26 @@ func writeInlineBody(header *mail.Header, body io.Reader, writer io.Writer) erro } // write the message body to the multipart message -func writeMultipartBody(body io.Reader, w *mail.Writer) error { - bh := mail.InlineHeader{} - bh.SetContentType("text/plain", map[string]string{"charset": "UTF-8"}) - +func writeMultipartBody(parts []*Part, w *mail.Writer) error { bi, err := w.CreateInline() if err != nil { return errors.Wrap(err, "CreateInline") } defer bi.Close() - bw, err := bi.CreatePart(bh) - if err != nil { - return errors.Wrap(err, "CreatePart") - } - defer bw.Close() - if _, err := io.Copy(bw, body); err != nil { - return errors.Wrap(err, "io.Copy") + for _, part := range parts { + bh := mail.InlineHeader{} + bh.SetContentType(part.MimeType, part.Params) + bw, err := bi.CreatePart(bh) + if err != nil { + return errors.Wrap(err, "CreatePart") + } + defer bw.Close() + if _, err := io.Copy(bw, part.Body); err != nil { + return errors.Wrap(err, "io.Copy") + } } + return nil } @@ -1084,6 +1109,13 @@ func newReviewMessage(composer *Composer, err error) *reviewMessage { if composer.attachKey { spec = append(spec, ui.GridSpec{Strategy: ui.SIZE_EXACT, Size: ui.Const(1)}) } + if len(composer.textParts) > 0 { + spec = append(spec, ui.GridSpec{Strategy: ui.SIZE_EXACT, Size: ui.Const(1)}) + spec = append(spec, ui.GridSpec{Strategy: ui.SIZE_EXACT, Size: ui.Const(1)}) + for i := 0; i < len(composer.textParts); i++ { + spec = append(spec, ui.GridSpec{Strategy: ui.SIZE_EXACT, Size: ui.Const(1)}) + } + } // make the last element fill remaining space spec = append(spec, ui.GridSpec{Strategy: ui.SIZE_WEIGHT, Size: ui.Const(1)}) @@ -1117,6 +1149,7 @@ func newReviewMessage(composer *Composer, err error) *reviewMessage { if len(composer.attachments) == 0 && !composer.attachKey { grid.AddChild(ui.NewText("(none)", uiConfig.GetStyle(config.STYLE_DEFAULT))).At(i, 0) + i += 1 } else { for _, a := range composer.attachments { grid.AddChild(ui.NewText(a, uiConfig.GetStyle(config.STYLE_DEFAULT))). @@ -1124,6 +1157,18 @@ func newReviewMessage(composer *Composer, err error) *reviewMessage { i += 1 } } + if len(composer.textParts) > 0 { + grid.AddChild(ui.NewText("Parts:", + uiConfig.GetStyle(config.STYLE_TITLE))).At(i, 0) + i += 1 + grid.AddChild(ui.NewText("text/plain", uiConfig.GetStyle(config.STYLE_DEFAULT))).At(i, 0) + i += 1 + for _, p := range composer.textParts { + grid.AddChild(ui.NewText(p.MimeType, uiConfig.GetStyle(config.STYLE_DEFAULT))).At(i, 0) + i += 1 + } + + } } return &reviewMessage{ -- cgit