aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDrew DeVault <sir@cmpwn.com>2019-05-16 12:15:34 -0400
committerDrew DeVault <sir@cmpwn.com>2019-05-16 12:15:34 -0400
commit475b697bdfd7a5821282174f14f8d904e47aff4d (patch)
tree7febf2e25cede5809f1d40c934d379315e06bd64
parent2b3e123cb86f9b4c5853e31d9e76c2b0d083f90a (diff)
Implement (basic form) of :reply
-rw-r--r--commands/account/reply.go83
-rw-r--r--go.mod1
-rw-r--r--go.sum2
-rw-r--r--lib/msgid.go34
-rw-r--r--widgets/account.go12
-rw-r--r--widgets/compose.go14
6 files changed, 139 insertions, 7 deletions
diff --git a/commands/account/reply.go b/commands/account/reply.go
new file mode 100644
index 0000000..d0d65a5
--- /dev/null
+++ b/commands/account/reply.go
@@ -0,0 +1,83 @@
+package account
+
+import (
+ "errors"
+ "fmt"
+ "strings"
+
+ "github.com/emersion/go-imap"
+
+ "git.sr.ht/~sircmpwn/aerc2/widgets"
+)
+
+func init() {
+ register("reply", Reply)
+}
+
+func Reply(aerc *widgets.Aerc, args []string) error {
+ if len(args) != 1 {
+ return errors.New("Usage: reply [-aq]")
+ }
+ // TODO: Reply all (w/ getopt)
+
+ acct := aerc.SelectedAccount()
+ msg := acct.Messages().Selected()
+ acct.Logger().Println("Replying to email " + msg.Envelope.MessageId)
+
+ var (
+ to []string
+ cc []string
+ toList []*imap.Address
+ )
+ if len(msg.Envelope.ReplyTo) != 0 {
+ toList = msg.Envelope.ReplyTo
+ } else {
+ toList = msg.Envelope.From
+ }
+ for _, addr := range toList {
+ if addr.PersonalName != "" {
+ to = append(to, fmt.Sprintf("%s <%s@%s>",
+ addr.PersonalName, addr.MailboxName, addr.HostName))
+ } else {
+ to = append(to, fmt.Sprintf("<%s@%s>",
+ addr.MailboxName, addr.HostName))
+ }
+ }
+ // TODO: Only if reply all
+ for _, addr := range msg.Envelope.Cc {
+ if addr.PersonalName != "" {
+ cc = append(cc, fmt.Sprintf("%s <%s@%s>",
+ addr.PersonalName, addr.MailboxName, addr.HostName))
+ } else {
+ cc = append(cc, fmt.Sprintf("<%s@%s>",
+ addr.MailboxName, addr.HostName))
+ }
+ }
+
+ subject := "Re: " + msg.Envelope.Subject
+
+ composer := widgets.NewComposer(
+ aerc.Config(), acct.AccountConfig(), acct.Worker()).
+ Defaults(map[string]string{
+ "To": strings.Join(to, ","),
+ "Cc": strings.Join(cc, ","),
+ "Subject": subject,
+ "In-Reply-To": msg.Envelope.MessageId,
+ }).
+ FocusTerminal()
+
+ tab := aerc.NewTab(composer, subject)
+
+ composer.OnSubjectChange(func(subject string) {
+ if subject == "" {
+ tab.Name = "New email"
+ } else {
+ tab.Name = subject
+ }
+ tab.Content.Invalidate()
+ })
+
+ return nil
+}
+
+
diff --git a/go.mod b/go.mod
index 0ddbcc2..f81ae62 100644
--- a/go.mod
+++ b/go.mod
@@ -15,6 +15,7 @@ require (
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf
github.com/kyoh86/xdg v0.0.0-20171127140545-8db68a8ea76a
github.com/lucasb-eyer/go-colorful v0.0.0-20180531031333-d9cec903b20c // indirect
+ github.com/martinlindhe/base36 v0.0.0-20190418230009-7c6542dfbb41
github.com/mattn/go-isatty v0.0.3
github.com/mattn/go-runewidth v0.0.2
github.com/miolini/datacounter v0.0.0-20171104152933-fd4e42a1d5e0
diff --git a/go.sum b/go.sum
index 30c58cc..8f497fe 100644
--- a/go.sum
+++ b/go.sum
@@ -40,6 +40,8 @@ github.com/kyoh86/xdg v0.0.0-20171127140545-8db68a8ea76a h1:vLFQnHOnCnmlySdpHAKF
github.com/kyoh86/xdg v0.0.0-20171127140545-8db68a8ea76a/go.mod h1:Z5mDqe0fxyxn3W2yTxsBAOQqIrXADQIh02wrTnaRM38=
github.com/lucasb-eyer/go-colorful v0.0.0-20180531031333-d9cec903b20c h1:b11Y3yxg40v2/9KUz76a4mSC1DMlgnPGAt+4pJSgmyU=
github.com/lucasb-eyer/go-colorful v0.0.0-20180531031333-d9cec903b20c/go.mod h1:NXg0ArsFk0Y01623LgUqoqcouGDB+PwCCQlrwrG6xJ4=
+github.com/martinlindhe/base36 v0.0.0-20190418230009-7c6542dfbb41 h1:CVsnY46BCLkX9XOhALJ/S7yb9ayc4eqjXSXO3tyB66A=
+github.com/martinlindhe/base36 v0.0.0-20190418230009-7c6542dfbb41/go.mod h1:+AtEs8xrBpCeYgSLoY/aJ6Wf37jtBuR0s35750M27+8=
github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-pointer v0.0.0-20180825124634-49522c3f3791 h1:PfHMsLQJwoc0ccjK0sam6J0wQo4s8mOuAo2yQGw+T2U=
diff --git a/lib/msgid.go b/lib/msgid.go
new file mode 100644
index 0000000..8282d1d
--- /dev/null
+++ b/lib/msgid.go
@@ -0,0 +1,34 @@
+package lib
+
+// TODO: Remove this pending merge into github.com/emersion/go-message
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "math/rand"
+ "os"
+ "time"
+
+ "github.com/martinlindhe/base36"
+)
+
+// Generates an RFC 2822-complaint Message-Id based on the informational draft
+// "Recommendations for generating Message IDs", for lack of a better
+// authoritative source.
+func GenerateMessageId() string {
+ var (
+ now bytes.Buffer
+ nonce bytes.Buffer
+ )
+ binary.Write(&now, binary.BigEndian, time.Now().UnixNano())
+ binary.Write(&nonce, binary.BigEndian, rand.Uint64())
+ hostname, err := os.Hostname()
+ if err != nil {
+ hostname = "localhost"
+ }
+ return fmt.Sprintf("<%s.%s@%s>",
+ base36.EncodeBytes(now.Bytes()),
+ base36.EncodeBytes(nonce.Bytes()),
+ hostname)
+}
diff --git a/widgets/account.go b/widgets/account.go
index c252e38..ab32f5d 100644
--- a/widgets/account.go
+++ b/widgets/account.go
@@ -84,6 +84,14 @@ func (acct *AccountView) AccountConfig() *config.AccountConfig {
return acct.acct
}
+func (acct *AccountView) Worker() *types.Worker {
+ return acct.worker
+}
+
+func (acct *AccountView) Logger() *log.Logger {
+ return acct.logger
+}
+
func (acct *AccountView) Name() string {
return acct.acct.Name
}
@@ -110,10 +118,6 @@ func (acct *AccountView) Focus(focus bool) {
// TODO: Unfocus children I guess
}
-func (acct *AccountView) Worker() *types.Worker {
- return acct.worker
-}
-
func (acct *AccountView) connected(msg types.WorkerMessage) {
switch msg := msg.(type) {
case *types.Done:
diff --git a/widgets/compose.go b/widgets/compose.go
index 2359cad..02a9d0c 100644
--- a/widgets/compose.go
+++ b/widgets/compose.go
@@ -14,6 +14,7 @@ import (
"github.com/mattn/go-runewidth"
"git.sr.ht/~sircmpwn/aerc2/config"
+ "git.sr.ht/~sircmpwn/aerc2/lib"
"git.sr.ht/~sircmpwn/aerc2/lib/ui"
"git.sr.ht/~sircmpwn/aerc2/worker/types"
)
@@ -123,6 +124,13 @@ func (c *Composer) Defaults(defaults map[string]string) *Composer {
return c
}
+func (c *Composer) FocusTerminal() *Composer {
+ c.focusable[c.focused].Focus(false)
+ c.focused = 3
+ c.focusable[c.focused].Focus(true)
+ return c
+}
+
func (c *Composer) OnSubjectChange(fn func(subject string)) {
c.headers.subject.OnChange(func() {
fn(c.headers.subject.input.String())
@@ -197,9 +205,9 @@ func (c *Composer) PrepareHeader() (*mail.Header, []string, error) {
c.email.Seek(0, os.SEEK_SET)
}
// Update headers
- // TODO: Custom header fields
mhdr := (*message.Header)(&header.Header)
mhdr.SetContentType("text/plain", map[string]string{"charset": "UTF-8"})
+ mhdr.SetText("Message-Id", lib.GenerateMessageId())
if subject, _ := header.Subject(); subject == "" {
header.SetSubject(c.headers.subject.input.String())
}
@@ -228,14 +236,14 @@ func (c *Composer) PrepareHeader() (*mail.Header, []string, error) {
rcpts = append(rcpts, addr.Address)
}
}
+ // TODO: Add cc, bcc to rcpts
// Merge in additional headers
txthdr := mhdr.Header
for key, value := range c.defaults {
- if !txthdr.Has(key) {
+ if !txthdr.Has(key) && value != "" {
mhdr.SetText(key, value)
}
}
- // TODO: Add cc, bcc to rcpts
return &header, rcpts, nil
}