aerc

Working clone of aerc-mail.org
git clone git://git.danielmoch.com/aerc.git
Log | Files | Refs | README | LICENSE

commit 6c36e04c1f7f7e222c71c5c8e7e7337744fe9c34
parent d5e82ecfe05065a84f1e13bee44e309b8e1559bd
Author: Drew DeVault <sir@cmpwn.com>
Date:   Tue, 14 May 2019 13:07:48 -0400

Add :send-message, prepares & writes email to /tmp

Diffstat:
Acommands/compose/send-message.go | 29+++++++++++++++++++++++++++++
Mgo.mod | 3+--
Mgo.sum | 5+++++
Mwidgets/compose.go | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mwidgets/msgviewer.go | 11+++++++----
5 files changed, 121 insertions(+), 6 deletions(-)

diff --git a/commands/compose/send-message.go b/commands/compose/send-message.go @@ -0,0 +1,29 @@ +package compose + +import ( + "errors" + "os" + + "git.sr.ht/~sircmpwn/aerc2/widgets" +) + +func init() { + register("send-message", SendMessage) +} + +func SendMessage(aerc *widgets.Aerc, args []string) error { + if len(args) > 1 { + return errors.New("Usage: send-message") + } + composer, _ := aerc.SelectedTab().(*widgets.Composer) + //config := composer.Config() + f, err := os.Create("/tmp/test.eml") + if err != nil { + panic(err) + } + _, err = composer.Message(f) + if err != nil { + panic(err) + } + return nil +} diff --git a/go.mod b/go.mod @@ -6,7 +6,7 @@ require ( github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 github.com/emersion/go-imap v1.0.0-beta.4 github.com/emersion/go-imap-idle v0.0.0-20180114101550-2af93776db6b - github.com/emersion/go-message v0.9.2 + github.com/emersion/go-message v0.10.0 github.com/gdamore/encoding v0.0.0-20151215212835-b23993cbb635 // indirect github.com/gdamore/tcell v1.0.0 github.com/go-ini/ini v1.42.0 @@ -20,6 +20,5 @@ require ( github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a // indirect github.com/stretchr/testify v1.3.0 golang.org/x/sys v0.0.0-20190426135247-a129542de9ae // indirect - golang.org/x/text v0.3.2 // indirect gopkg.in/ini.v1 v1.42.0 // indirect ) diff --git a/go.sum b/go.sum @@ -10,9 +10,14 @@ github.com/emersion/go-imap v1.0.0-beta.4 h1:QglkDofK1RhU471SqcHxzRlSuPsCL6YpFc+ github.com/emersion/go-imap v1.0.0-beta.4/go.mod h1:mOPegfAgLVXbhRm1bh2JTX08z2Y3HYmKYpbrKDeAzsQ= github.com/emersion/go-imap-idle v0.0.0-20180114101550-2af93776db6b h1:q4qkNe/W10qFGD3RWd4meQTkD0+Zrz0L4ekMvlptg60= github.com/emersion/go-imap-idle v0.0.0-20180114101550-2af93776db6b/go.mod h1:o14zPKCmEH5WC1vU5SdPoZGgNvQx7zzKSnxPQlobo78= +github.com/emersion/go-message v0.9.1 h1:s6HoJ6t+1wHWEs0G/QVR1r5bb6nvx2/b6DuQfknit14= github.com/emersion/go-message v0.9.1/go.mod h1:m3cK90skCWxm5sIMs1sXxly4Tn9Plvcf6eayHZJ1NzM= github.com/emersion/go-message v0.9.2 h1:rJmtGZO1Z71PJDQXbC31EwzlJCsA/8kya6GnebSGp6I= github.com/emersion/go-message v0.9.2/go.mod h1:m3cK90skCWxm5sIMs1sXxly4Tn9Plvcf6eayHZJ1NzM= +github.com/emersion/go-message v0.9.3-0.20190413201152-1e345aac1fa8 h1:yFBYnt6PWNC+PQO70xOI3oayW6alYPRmCN1y0oOfgmQ= +github.com/emersion/go-message v0.9.3-0.20190413201152-1e345aac1fa8/go.mod h1:XQBEXJJ+6gGcjLsa3rYvGwTC7IJAIWQuxiTIcUKcIbc= +github.com/emersion/go-message v0.10.0 h1:V8hwhZPNIuAIGNLcMZiCzzavUIiODG3COYLsQMBLvG4= +github.com/emersion/go-message v0.10.0/go.mod h1:7d2eJfhjiJSnlaKcUPq7sEC7ekWELG6F5Lw2BxOGj6Y= github.com/emersion/go-sasl v0.0.0-20161116183048-7e096a0a6197 h1:rDJPbyliyym8ZL/Wt71kdolp6yaD4fLIQz638E6JEt0= github.com/emersion/go-sasl v0.0.0-20161116183048-7e096a0a6197/go.mod h1:G/dpzLu16WtQpBfQ/z3LYiYJn3ZhKSGWn83fyoyQe/k= github.com/emersion/go-textwrapper v0.0.0-20160606182133-d0e65e56babe h1:40SWqY0zE3qCi6ZrtTf5OUdNm5lDnGnjRSq9GgmeTrg= diff --git a/widgets/compose.go b/widgets/compose.go @@ -1,10 +1,15 @@ package widgets import ( + "io" "io/ioutil" + gomail "net/mail" "os" "os/exec" + "time" + "github.com/emersion/go-message" + "github.com/emersion/go-message/mail" "github.com/gdamore/tcell" "github.com/mattn/go-runewidth" @@ -79,6 +84,9 @@ func NewComposer(conf *config.AccountConfig) *Composer { focused: 1, focusable: []ui.DrawableInteractive{from, to, subject, term}, } + c.headers.to = to + c.headers.from = from + c.headers.subject = subject term.OnClose = c.termClosed @@ -107,6 +115,77 @@ func (c *Composer) Focus(focus bool) { c.focusable[c.focused].Focus(focus) } +func (c *Composer) Config() *config.AccountConfig { + return c.config +} + +// Writes the email to the given writer, and returns a list of recipients +func (c *Composer) Message(writeto io.Writer) ([]string, error) { + // Extract headers from the email, if present + c.email.Seek(0, os.SEEK_SET) + var ( + rcpts []string + header mail.Header + body io.Reader + ) + reader, err := mail.CreateReader(c.email) + if err == nil { + header = reader.Header + // 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 nil, err + } + body = part.Body + defer reader.Close() + } else { + c.email.Seek(0, os.SEEK_SET) + body = c.email + } + // Update headers + // TODO: Custom header fields + mhdr := (*message.Header)(&header.Header) + mhdr.SetContentType("text/plain", map[string]string{"charset": "UTF-8"}) + if subject, _ := header.Subject(); subject == "" { + header.SetSubject(c.headers.subject.input.String()) + } + if date, err := header.Date(); err != nil && date != (time.Time{}) { + header.SetDate(time.Now()) + } + if from, _ := mhdr.Text("From"); from == "" { + mhdr.SetText("From", c.headers.from.input.String()) + } + if to := c.headers.to.input.String(); to != "" { + // Dammit Simon, this branch is 3x as long as it ought to be because + // your types aren't compatible enough with each other + to_rcpts, err := gomail.ParseAddressList(to) + if err != nil { + return nil, err + } + ed_rcpts, err := header.AddressList("To") + if err != nil { + return nil, err + } + for _, addr := range to_rcpts { + ed_rcpts = append(ed_rcpts, (*mail.Address)(addr)) + } + header.SetAddressList("To", ed_rcpts) + for _, addr := range ed_rcpts { + rcpts = append(rcpts, addr.Address) + } + } + // TODO: Add cc, bcc to rcpts + // TODO: attachments + writer, err := mail.CreateSingleInlineWriter(writeto, header) + if err != nil { + return nil, err + } + defer writer.Close() + io.Copy(writer, body) + return rcpts, nil +} + func (c *Composer) termClosed(err error) { // TODO: do we care about that error (note: yes, we do) c.grid.RemoveChild(c.editor) diff --git a/widgets/msgviewer.go b/widgets/msgviewer.go @@ -10,6 +10,7 @@ import ( "github.com/danwakefield/fnmatch" "github.com/emersion/go-imap" "github.com/emersion/go-message" + _ "github.com/emersion/go-message/charset" "github.com/emersion/go-message/mail" "github.com/gdamore/tcell" "github.com/google/shlex" @@ -109,7 +110,8 @@ func NewMessageViewer(conf *config.AercConfig, store *lib.MessageStore, pager = exec.Command(cmd[0], cmd[1:]...) for _, f := range conf.Filters { - mime := strings.ToLower(msg.BodyStructure.MIMEType) + "/" + strings.ToLower(msg.BodyStructure.MIMESubType) + mime := strings.ToLower(msg.BodyStructure.MIMEType) + + "/" + strings.ToLower(msg.BodyStructure.MIMESubType) switch f.FilterType { case config.FILTER_MIMETYPE: if fnmatch.Match(f.Filter, mime, 0) { @@ -181,11 +183,12 @@ handle_error: func (mv *MessageViewer) attemptCopy() { if mv.source != nil && mv.pager.Process != nil { - header := make(message.Header) - header.Set("Content-Transfer-Encoding", mv.msg.BodyStructure.Encoding) + header := message.Header{} + header.SetText("Content-Transfer-Encoding", + mv.msg.BodyStructure.Encoding) header.SetContentType( mv.msg.BodyStructure.MIMEType, mv.msg.BodyStructure.Params) - header.SetContentDescription(mv.msg.BodyStructure.Description) + header.SetText("Content-Description", mv.msg.BodyStructure.Description) if mv.filter != nil { stdout, _ := mv.filter.StdoutPipe() mv.filter.Start()