aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Burwell <ben@benburwell.com>2019-07-04 22:34:52 -0400
committerBen Burwell <ben@benburwell.com>2019-07-04 22:37:29 -0400
commitb46b497f99d7f4dcba8936ce0ebfe8cf982cec1f (patch)
treeb9212d4b196706d3c0e2d93b8dfd8cc39cbacf3b
parent6574dedd8a4afdaedd3677283955a866214bd99a (diff)
Factor UI models out of the worker message packagemdps1
Before, the information needed to display different parts of the UI was tightly coupled to the specific messages being sent back and forth to the backend worker. Separating out a models package allows us to be more specific about exactly what a backend is able to and required to provide for the UI.
-rw-r--r--commands/msg/reply.go28
-rw-r--r--lib/address.go40
-rw-r--r--lib/indexformat.go46
-rw-r--r--models/models.go88
-rw-r--r--widgets/msglist.go3
-rw-r--r--widgets/msgviewer.go19
-rw-r--r--worker/imap/fetch.go10
-rw-r--r--worker/imap/imap.go75
-rw-r--r--worker/imap/worker.go6
9 files changed, 210 insertions, 105 deletions
diff --git a/commands/msg/reply.go b/commands/msg/reply.go
index 7a64d21..68c2089 100644
--- a/commands/msg/reply.go
+++ b/commands/msg/reply.go
@@ -9,12 +9,11 @@ import (
"strings"
"git.sr.ht/~sircmpwn/getopt"
- "github.com/emersion/go-imap"
"github.com/emersion/go-message"
_ "github.com/emersion/go-message/charset"
"github.com/emersion/go-message/mail"
- "git.sr.ht/~sircmpwn/aerc/lib"
+ "git.sr.ht/~sircmpwn/aerc/models"
"git.sr.ht/~sircmpwn/aerc/widgets"
)
@@ -67,7 +66,7 @@ func (_ reply) Execute(aerc *widgets.Aerc, args []string) error {
var (
to []string
cc []string
- toList []*imap.Address
+ toList []*models.Address
)
if args[0] == "reply" {
if len(msg.Envelope.ReplyTo) != 0 {
@@ -76,24 +75,23 @@ func (_ reply) Execute(aerc *widgets.Aerc, args []string) error {
toList = msg.Envelope.From
}
for _, addr := range toList {
- if addr.PersonalName != "" {
+ if addr.Name != "" {
to = append(to, fmt.Sprintf("%s <%s@%s>",
- addr.PersonalName, addr.MailboxName, addr.HostName))
+ addr.Name, addr.Mailbox, addr.Host))
} else {
- to = append(to, fmt.Sprintf("<%s@%s>",
- addr.MailboxName, addr.HostName))
+ to = append(to, fmt.Sprintf("<%s@%s>", addr.Mailbox, addr.Host))
}
}
if replyAll {
for _, addr := range msg.Envelope.Cc {
- cc = append(cc, lib.FormatAddress(addr))
+ cc = append(cc, addr.Format())
}
for _, addr := range msg.Envelope.To {
- address := fmt.Sprintf("%s@%s", addr.MailboxName, addr.HostName)
+ address := fmt.Sprintf("%s@%s", addr.Mailbox, addr.Host)
if address == us.Address {
continue
}
- to = append(to, lib.FormatAddress(addr))
+ to = append(to, addr.Format())
}
}
}
@@ -163,7 +161,7 @@ func (_ reply) Execute(aerc *widgets.Aerc, args []string) error {
go composer.SetContents(pipeout)
// TODO: Let user customize the date format used here
io.WriteString(pipein, fmt.Sprintf("Forwarded message from %s on %s:\n\n",
- msg.Envelope.From[0].PersonalName,
+ msg.Envelope.From[0].Name,
msg.Envelope.Date.Format("Mon Jan 2, 2006 at 3:04 PM")))
for scanner.Scan() {
io.WriteString(pipein, fmt.Sprintf("%s\n", scanner.Text()))
@@ -176,7 +174,7 @@ func (_ reply) Execute(aerc *widgets.Aerc, args []string) error {
if quote {
var (
path []int
- part *imap.BodyStructure
+ part *models.BodyStructure
)
if len(msg.BodyStructure.Parts) != 0 {
part, path = findPlaintext(msg.BodyStructure, path)
@@ -212,7 +210,7 @@ func (_ reply) Execute(aerc *widgets.Aerc, args []string) error {
// TODO: Let user customize the date format used here
io.WriteString(pipein, fmt.Sprintf("On %s %s wrote:\n",
msg.Envelope.Date.Format("Mon Jan 2, 2006 at 3:04 PM"),
- msg.Envelope.From[0].PersonalName))
+ msg.Envelope.From[0].Name))
for scanner.Scan() {
io.WriteString(pipein, fmt.Sprintf("> %s\n", scanner.Text()))
}
@@ -228,8 +226,8 @@ func (_ reply) Execute(aerc *widgets.Aerc, args []string) error {
return nil
}
-func findPlaintext(bs *imap.BodyStructure,
- path []int) (*imap.BodyStructure, []int) {
+func findPlaintext(bs *models.BodyStructure,
+ path []int) (*models.BodyStructure, []int) {
for i, part := range bs.Parts {
cur := append(path, i+1)
diff --git a/lib/address.go b/lib/address.go
deleted file mode 100644
index b557195..0000000
--- a/lib/address.go
+++ /dev/null
@@ -1,40 +0,0 @@
-package lib
-
-import (
- "bytes"
- "fmt"
- "regexp"
- "strings"
-
- "github.com/emersion/go-imap"
-)
-
-var (
- atom *regexp.Regexp = regexp.MustCompile("^[a-z0-9!#$%7'*+-/=?^_`{}|~ ]+$")
-)
-
-func FormatAddresses(addrs []*imap.Address) string {
- val := bytes.Buffer{}
- for i, addr := range addrs {
- val.WriteString(FormatAddress(addr))
- if i != len(addrs)-1 {
- val.WriteString(", ")
- }
- }
- return val.String()
-}
-
-func FormatAddress(addr *imap.Address) string {
- if addr.PersonalName != "" {
- if atom.MatchString(addr.PersonalName) {
- return fmt.Sprintf("%s <%s@%s>",
- addr.PersonalName, addr.MailboxName, addr.HostName)
- } else {
- return fmt.Sprintf("\"%s\" <%s@%s>",
- strings.ReplaceAll(addr.PersonalName, "\"", "'"),
- addr.MailboxName, addr.HostName)
- }
- } else {
- return fmt.Sprintf("<%s@%s>", addr.MailboxName, addr.HostName)
- }
-}
diff --git a/lib/indexformat.go b/lib/indexformat.go
index 43d2ef8..fa39909 100644
--- a/lib/indexformat.go
+++ b/lib/indexformat.go
@@ -6,8 +6,6 @@ import (
"strings"
"unicode"
- "github.com/emersion/go-imap"
-
"git.sr.ht/~sircmpwn/aerc/config"
"git.sr.ht/~sircmpwn/aerc/models"
)
@@ -70,10 +68,9 @@ func ParseIndexFormat(conf *config.AercConfig, number int,
}
addr := msg.Envelope.From[0]
retval = append(retval, 's')
- args = append(args, fmt.Sprintf("%s@%s", addr.MailboxName,
- addr.HostName))
+ args = append(args, fmt.Sprintf("%s@%s", addr.Mailbox, addr.Host))
case 'A':
- var addr *imap.Address
+ var addr *models.Address
if len(msg.Envelope.ReplyTo) == 0 {
if len(msg.Envelope.From) == 0 {
return "", nil,
@@ -85,8 +82,7 @@ func ParseIndexFormat(conf *config.AercConfig, number int,
addr = msg.Envelope.ReplyTo[0]
}
retval = append(retval, 's')
- args = append(args, fmt.Sprintf("%s@%s", addr.MailboxName,
- addr.HostName))
+ args = append(args, fmt.Sprintf("%s@%s", addr.Mailbox, addr.Host))
case 'C':
retval = append(retval, 'd')
args = append(args, number)
@@ -100,7 +96,7 @@ func ParseIndexFormat(conf *config.AercConfig, number int,
if len(msg.Envelope.From) == 0 {
return "", nil, errors.New("found no address for sender")
}
- addr := FormatAddress(msg.Envelope.From[0])
+ addr := msg.Envelope.From[0].Format()
retval = append(retval, 's')
args = append(args, addr)
case 'F':
@@ -111,11 +107,10 @@ func ParseIndexFormat(conf *config.AercConfig, number int,
// TODO: handle case when sender is current user. Then
// use recipient's name
var val string
- if addr.PersonalName != "" {
- val = addr.PersonalName
+ if addr.Name != "" {
+ val = addr.Name
} else {
- val = fmt.Sprintf("%s@%s",
- addr.MailboxName, addr.HostName)
+ val = fmt.Sprintf("%s@%s", addr.Mailbox, addr.Host)
}
retval = append(retval, 's')
args = append(args, val)
@@ -129,20 +124,19 @@ func ParseIndexFormat(conf *config.AercConfig, number int,
}
addr := msg.Envelope.From[0]
var val string
- if addr.PersonalName != "" {
- val = addr.PersonalName
+ if addr.Name != "" {
+ val = addr.Name
} else {
- val = fmt.Sprintf("%s@%s",
- addr.MailboxName, addr.HostName)
+ val = fmt.Sprintf("%s@%s", addr.Mailbox, addr.Host)
}
retval = append(retval, 's')
args = append(args, val)
case 'r':
- addrs := FormatAddresses(msg.Envelope.To)
+ addrs := models.FormatAddresses(msg.Envelope.To)
retval = append(retval, 's')
args = append(args, addrs)
case 'R':
- addrs := FormatAddresses(msg.Envelope.Cc)
+ addrs := models.FormatAddresses(msg.Envelope.Cc)
retval = append(retval, 's')
args = append(args, addrs)
case 's':
@@ -154,16 +148,16 @@ func ParseIndexFormat(conf *config.AercConfig, number int,
}
addr := msg.Envelope.From[0]
retval = append(retval, 's')
- args = append(args, addr.MailboxName)
+ args = append(args, addr.Mailbox)
case 'v':
if len(msg.Envelope.From) == 0 {
return "", nil, errors.New("found no address for sender")
}
addr := msg.Envelope.From[0]
// check if message is from current user
- if addr.PersonalName != "" {
+ if addr.Name != "" {
retval = append(retval, 's')
- args = append(args, strings.Split(addr.PersonalName, " ")[0])
+ args = append(args, strings.Split(addr.Name, " ")[0])
}
case 'Z':
// calculate all flags
@@ -171,18 +165,18 @@ func ParseIndexFormat(conf *config.AercConfig, number int,
var delFlag = ""
var flaggedFlag = ""
for _, flag := range msg.Flags {
- if flag == imap.SeenFlag {
+ if flag == models.SeenFlag {
readFlag = "O" // message is old
- } else if flag == imap.RecentFlag {
+ } else if flag == models.RecentFlag {
readFlag = "N" // message is new
- } else if flag == imap.AnsweredFlag {
+ } else if flag == models.AnsweredFlag {
readFlag = "r" // message has been replied to
}
- if flag == imap.DeletedFlag {
+ if flag == models.DeletedFlag {
delFlag = "D"
// TODO: check if attachments
}
- if flag == imap.FlaggedFlag {
+ if flag == models.FlaggedFlag {
flaggedFlag = "!"
}
// TODO: check gpg stuff
diff --git a/models/models.go b/models/models.go
index 00297e9..1f6636f 100644
--- a/models/models.go
+++ b/models/models.go
@@ -1,18 +1,42 @@
package models
import (
+ "bytes"
+ "fmt"
"io"
+ "regexp"
+ "strings"
"time"
- "github.com/emersion/go-imap"
"github.com/emersion/go-message/mail"
)
+// Flag is an abstraction around the different flags which can be present in
+// different email backends and represents a flag that we use in the UI.
+type Flag int
+
+const (
+ // SeenFlag marks a message as having been seen previously
+ SeenFlag Flag = iota
+
+ // RecentFlag marks a message as being recent
+ RecentFlag
+
+ // AnsweredFlag marks a message as having been replied to
+ AnsweredFlag
+
+ // DeletedFlag marks a message as having been deleted
+ DeletedFlag
+
+ // FlaggedFlag marks a message with a user flag
+ FlaggedFlag
+)
+
// A MessageInfo holds information about the structure of a message
type MessageInfo struct {
- BodyStructure *imap.BodyStructure
- Envelope *imap.Envelope
- Flags []string
+ BodyStructure *BodyStructure
+ Envelope *Envelope
+ Flags []Flag
InternalDate time.Time
RFC822Headers *mail.Header
Size uint32
@@ -30,3 +54,59 @@ type FullMessage struct {
Reader io.Reader
Uid uint32
}
+
+type BodyStructure struct {
+ MIMEType string
+ MIMESubType string
+ Params map[string]string
+ Description string
+ Encoding string
+ Parts []*BodyStructure
+ Disposition string
+ DispositionParams map[string]string
+}
+
+type Envelope struct {
+ Date time.Time
+ Subject string
+ From []*Address
+ ReplyTo []*Address
+ To []*Address
+ Cc []*Address
+ Bcc []*Address
+ MessageId string
+}
+
+type Address struct {
+ Name string
+ Mailbox string
+ Host string
+}
+
+var atom *regexp.Regexp = regexp.MustCompile("^[a-z0-9!#$%7'*+-/=?^_`{}|~ ]+$")
+
+func (a Address) Format() string {
+ if a.Name != "" {
+ if atom.MatchString(a.Name) {
+ return fmt.Sprintf("%s <%s@%s>", a.Name, a.Mailbox, a.Host)
+ } else {
+ return fmt.Sprintf("\"%s\" <%s@%s>",
+ strings.ReplaceAll(a.Name, "\"", "'"),
+ a.Mailbox, a.Host)
+ }
+ } else {
+ return fmt.Sprintf("<%s@%s>", a.Mailbox, a.Host)
+ }
+}
+
+// FormatAddresses formats a list of addresses, separating each by a comma
+func FormatAddresses(addrs []*Address) string {
+ val := bytes.Buffer{}
+ for i, addr := range addrs {
+ val.WriteString(addr.Format())
+ if i != len(addrs)-1 {
+ val.WriteString(", ")
+ }
+ }
+ return val.String()
+}
diff --git a/widgets/msglist.go b/widgets/msglist.go
index 7051478..8968653 100644
--- a/widgets/msglist.go
+++ b/widgets/msglist.go
@@ -4,7 +4,6 @@ import (
"fmt"
"log"
- "github.com/emersion/go-imap"
"github.com/gdamore/tcell"
"github.com/mattn/go-runewidth"
@@ -86,7 +85,7 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
// unread message
seen := false
for _, flag := range msg.Flags {
- if flag == imap.SeenFlag {
+ if flag == models.SeenFlag {
seen = true
}
}
diff --git a/widgets/msgviewer.go b/widgets/msgviewer.go
index 49b4dd4..47b9dff 100644
--- a/widgets/msgviewer.go
+++ b/widgets/msgviewer.go
@@ -9,7 +9,6 @@ import (
"strings"
"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"
@@ -66,12 +65,12 @@ func NewMessageViewer(acct *AccountView, conf *config.AercConfig,
headers.AddChild(
&HeaderView{
Name: "From",
- Value: lib.FormatAddresses(msg.Envelope.From),
+ Value: models.FormatAddresses(msg.Envelope.From),
}).At(0, 0)
headers.AddChild(
&HeaderView{
Name: "To",
- Value: lib.FormatAddresses(msg.Envelope.To),
+ Value: models.FormatAddresses(msg.Envelope.To),
}).At(0, 1)
headers.AddChild(
&HeaderView{
@@ -112,7 +111,7 @@ handle_error:
}
func enumerateParts(conf *config.AercConfig, store *lib.MessageStore,
- msg *models.MessageInfo, body *imap.BodyStructure,
+ msg *models.MessageInfo, body *models.BodyStructure,
showHeaders bool, index []int) ([]*PartViewer, error) {
var parts []*PartViewer
@@ -324,7 +323,7 @@ type PartViewer struct {
msg *models.MessageInfo
pager *exec.Cmd
pagerin io.WriteCloser
- part *imap.BodyStructure
+ part *models.BodyStructure
showHeaders bool
sink io.WriteCloser
source io.Reader
@@ -335,13 +334,13 @@ type PartViewer struct {
type PartInfo struct {
Index []int
Msg *models.MessageInfo
- Part *imap.BodyStructure
+ Part *models.BodyStructure
Store *lib.MessageStore
}
func NewPartViewer(conf *config.AercConfig,
store *lib.MessageStore, msg *models.MessageInfo,
- part *imap.BodyStructure, showHeaders bool,
+ part *models.BodyStructure, showHeaders bool,
index []int) (*PartViewer, error) {
var (
@@ -372,11 +371,11 @@ func NewPartViewer(conf *config.AercConfig,
case "subject":
header = msg.Envelope.Subject
case "from":
- header = lib.FormatAddresses(msg.Envelope.From)
+ header = models.FormatAddresses(msg.Envelope.From)
case "to":
- header = lib.FormatAddresses(msg.Envelope.To)
+ header = models.FormatAddresses(msg.Envelope.To)
case "cc":
- header = lib.FormatAddresses(msg.Envelope.Cc)
+ header = models.FormatAddresses(msg.Envelope.Cc)
}
if f.Regex.Match([]byte(header)) {
filter = exec.Command("sh", "-c", f.Command)
diff --git a/worker/imap/fetch.go b/worker/imap/fetch.go
index fe25977..1745ead 100644
--- a/worker/imap/fetch.go
+++ b/worker/imap/fetch.go
@@ -82,9 +82,9 @@ func (imapw *IMAPWorker) handleFetchMessages(
imapw.worker.PostMessage(&types.MessageInfo{
Message: types.RespondTo(msg),
Info: &models.MessageInfo{
- BodyStructure: _msg.BodyStructure,
- Envelope: _msg.Envelope,
- Flags: _msg.Flags,
+ BodyStructure: translateBodyStructure(_msg.BodyStructure),
+ Envelope: translateEnvelope(_msg.Envelope),
+ Flags: translateFlags(_msg.Flags),
InternalDate: _msg.InternalDate,
RFC822Headers: header,
Uid: _msg.Uid,
@@ -103,7 +103,7 @@ func (imapw *IMAPWorker) handleFetchMessages(
imapw.worker.PostMessage(&types.MessageInfo{
Message: types.RespondTo(msg),
Info: &models.MessageInfo{
- Flags: _msg.Flags,
+ Flags: translateFlags(_msg.Flags),
Uid: _msg.Uid,
},
}, nil)
@@ -120,7 +120,7 @@ func (imapw *IMAPWorker) handleFetchMessages(
imapw.worker.PostMessage(&types.MessageInfo{
Message: types.RespondTo(msg),
Info: &models.MessageInfo{
- Flags: _msg.Flags,
+ Flags: translateFlags(_msg.Flags),
Uid: _msg.Uid,
},
}, nil)
diff --git a/worker/imap/imap.go b/worker/imap/imap.go
index 28bac93..06bcd00 100644
--- a/worker/imap/imap.go
+++ b/worker/imap/imap.go
@@ -2,6 +2,8 @@ package imap
import (
"github.com/emersion/go-imap"
+
+ "git.sr.ht/~sircmpwn/aerc/models"
)
func toSeqSet(uids []uint32) *imap.SeqSet {
@@ -11,3 +13,76 @@ func toSeqSet(uids []uint32) *imap.SeqSet {
}
return &set
}
+
+func translateBodyStructure(bs *imap.BodyStructure) *models.BodyStructure {
+ if bs == nil {
+ return nil
+ }
+ var parts []*models.BodyStructure
+ for _, part := range bs.Parts {
+ parts = append(parts, translateBodyStructure(part))
+ }
+ return &models.BodyStructure{
+ MIMEType: bs.MIMEType,
+ MIMESubType: bs.MIMESubType,
+ Params: bs.Params,
+ Description: bs.Description,
+ Encoding: bs.Encoding,
+ Parts: parts,
+ Disposition: bs.Disposition,
+ DispositionParams: bs.DispositionParams,
+ }
+}
+
+func translateEnvelope(e *imap.Envelope) *models.Envelope {
+ if e == nil {
+ return nil
+ }
+ return &models.Envelope{
+ Date: e.Date,
+ Subject: e.Subject,
+ From: translateAddresses(e.From),
+ ReplyTo: translateAddresses(e.ReplyTo),
+ To: translateAddresses(e.To),
+ Cc: translateAddresses(e.Cc),
+ Bcc: translateAddresses(e.Bcc),
+ MessageId: e.MessageId,
+ }
+}
+
+func translateAddress(a *imap.Address) *models.Address {
+ if a == nil {
+ return nil
+ }
+ return &models.Address{
+ Name: a.PersonalName,
+ Mailbox: a.MailboxName,
+ Host: a.HostName,
+ }
+}
+
+func translateAddresses(addrs []*imap.Address) []*models.Address {
+ var converted []*models.Address
+ for _, addr := range addrs {
+ converted = append(converted, translateAddress(addr))
+ }
+ return converted
+}
+
+var flagMap = map[string]models.Flag{
+ imap.SeenFlag: models.SeenFlag,
+ imap.RecentFlag: models.RecentFlag,
+ imap.AnsweredFlag: models.AnsweredFlag,
+ imap.DeletedFlag: models.DeletedFlag,
+ imap.FlaggedFlag: models.FlaggedFlag,
+}
+
+func translateFlags(imapFlags []string) []models.Flag {
+ var flags []models.Flag
+ for _, imapFlag := range imapFlags {
+ if flag, ok := flagMap[imapFlag]; ok {
+ flags = append(flags, flag)
+ }
+ }
+ return flags
+}
diff --git a/worker/imap/worker.go b/worker/imap/worker.go
index 78e5537..8d1a5bb 100644
--- a/worker/imap/worker.go
+++ b/worker/imap/worker.go
@@ -179,9 +179,9 @@ func (w *IMAPWorker) handleImapUpdate(update client.Update) {
}
w.worker.PostMessage(&types.MessageInfo{
Info: &models.MessageInfo{
- BodyStructure: msg.BodyStructure,
- Envelope: msg.Envelope,
- Flags: msg.Flags,
+ BodyStructure: translateBodyStructure(msg.BodyStructure),
+ Envelope: translateEnvelope(msg.Envelope),
+ Flags: translateFlags(msg.Flags),
InternalDate: msg.InternalDate,
Uid: msg.Uid,
},