aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--commands/msgview/toggle-headers.go25
-rw-r--r--config/aerc.conf.in7
-rw-r--r--config/config.go1
-rw-r--r--widgets/msgviewer.go144
-rw-r--r--worker/imap/fetch.go21
-rw-r--r--worker/types/messages.go2
6 files changed, 146 insertions, 54 deletions
diff --git a/commands/msgview/toggle-headers.go b/commands/msgview/toggle-headers.go
new file mode 100644
index 00000000..fc29042d
--- /dev/null
+++ b/commands/msgview/toggle-headers.go
@@ -0,0 +1,25 @@
+package msgview
+
+import (
+ "errors"
+ "fmt"
+
+ "git.sr.ht/~sircmpwn/aerc/widgets"
+)
+
+func init() {
+ register("toggle-headers", ToggleHeaders)
+}
+
+func toggleHeadersUsage(cmd string) error {
+ return errors.New(fmt.Sprintf("Usage: %s", cmd))
+}
+
+func ToggleHeaders(aerc *widgets.Aerc, args []string) error {
+ if len(args) > 1 {
+ return toggleHeadersUsage(args[0])
+ }
+ mv, _ := aerc.SelectedTab().(*widgets.MessageViewer)
+ mv.ToggleHeaders()
+ return nil
+}
diff --git a/config/aerc.conf.in b/config/aerc.conf.in
index 090e624d..0bf114f4 100644
--- a/config/aerc.conf.in
+++ b/config/aerc.conf.in
@@ -44,6 +44,13 @@ pager=less -R
# Default: text/plain,text/html
alternatives=text/plain,text/html
+#
+# Default setting to determine whether to show full headers or only parsed
+# ones in message viewer.
+#
+# Default: false
+show-headers=false
+
[compose]
#
# Specifies the command to run the editor with. It will be shown in an embedded
diff --git a/config/config.go b/config/config.go
index 889a63d2..3b7edbbf 100644
--- a/config/config.go
+++ b/config/config.go
@@ -72,6 +72,7 @@ type FilterConfig struct {
type ViewerConfig struct {
Pager string
Alternatives []string
+ ShowHeaders bool `ini:"show-headers"`
}
type AercConfig struct {
diff --git a/widgets/msgviewer.go b/widgets/msgviewer.go
index 45a5ed06..b82fa8d0 100644
--- a/widgets/msgviewer.go
+++ b/widgets/msgviewer.go
@@ -35,8 +35,9 @@ type MessageViewer struct {
type PartSwitcher struct {
ui.Invalidatable
- parts []*PartViewer
- selected int
+ parts []*PartViewer
+ selected int
+ showHeaders bool
}
func formatAddresses(addrs []*imap.Address) string {
@@ -98,33 +99,10 @@ func NewMessageViewer(acct *AccountView, conf *config.AercConfig,
}).At(2, 0).Span(1, 2)
headers.AddChild(ui.NewFill(' ')).At(3, 0).Span(1, 2)
- var err error
switcher := &PartSwitcher{}
- if len(msg.BodyStructure.Parts) == 0 {
- pv, err := NewPartViewer(conf, store, msg, msg.BodyStructure, []int{1})
- if err != nil {
- goto handle_error
- }
- switcher.parts = []*PartViewer{pv}
- pv.OnInvalidate(func(_ ui.Drawable) {
- switcher.Invalidate()
- })
- } else {
- switcher.parts, err = enumerateParts(conf, store,
- msg, msg.BodyStructure, []int{})
- if err != nil {
- goto handle_error
- }
- switcher.selected = -1
- for i, pv := range switcher.parts {
- pv.OnInvalidate(func(_ ui.Drawable) {
- switcher.Invalidate()
- })
- // TODO: switch to user's preferred mimetype, if configured
- if switcher.selected == -1 && pv.part.MIMEType != "multipart" {
- switcher.selected = i
- }
- }
+ err := createSwitcher(switcher, conf, store, msg, conf.Viewer.ShowHeaders)
+ if err != nil {
+ goto handle_error
}
grid.AddChild(headers).At(0, 0)
@@ -132,6 +110,7 @@ func NewMessageViewer(acct *AccountView, conf *config.AercConfig,
return &MessageViewer{
acct: acct,
+ conf: conf,
grid: grid,
msg: msg,
store: store,
@@ -148,7 +127,7 @@ handle_error:
func enumerateParts(conf *config.AercConfig, store *lib.MessageStore,
msg *types.MessageInfo, body *imap.BodyStructure,
- index []int) ([]*PartViewer, error) {
+ showHeaders bool, index []int) ([]*PartViewer, error) {
var parts []*PartViewer
for i, part := range body.Parts {
@@ -158,14 +137,14 @@ func enumerateParts(conf *config.AercConfig, store *lib.MessageStore,
pv := &PartViewer{part: part}
parts = append(parts, pv)
subParts, err := enumerateParts(
- conf, store, msg, part, curindex)
+ conf, store, msg, part, showHeaders, curindex)
if err != nil {
return nil, err
}
parts = append(parts, subParts...)
continue
}
- pv, err := NewPartViewer(conf, store, msg, part, curindex)
+ pv, err := NewPartViewer(conf, store, msg, part, showHeaders, curindex)
if err != nil {
return nil, err
}
@@ -174,6 +153,44 @@ func enumerateParts(conf *config.AercConfig, store *lib.MessageStore,
return parts, nil
}
+func createSwitcher(switcher *PartSwitcher, conf *config.AercConfig,
+ store *lib.MessageStore, msg *types.MessageInfo, showHeaders bool) error {
+ var err error
+ switcher.showHeaders = showHeaders
+
+ if showHeaders {
+ }
+
+ if len(msg.BodyStructure.Parts) == 0 {
+ pv, err := NewPartViewer(conf, store, msg, msg.BodyStructure,
+ showHeaders, []int{1})
+ if err != nil {
+ return err
+ }
+ switcher.parts = []*PartViewer{pv}
+ pv.OnInvalidate(func(_ ui.Drawable) {
+ switcher.Invalidate()
+ })
+ } else {
+ switcher.parts, err = enumerateParts(conf, store,
+ msg, msg.BodyStructure, showHeaders, []int{})
+ if err != nil {
+ return err
+ }
+ switcher.selected = -1
+ for i, pv := range switcher.parts {
+ pv.OnInvalidate(func(_ ui.Drawable) {
+ switcher.Invalidate()
+ })
+ // TODO: switch to user's preferred mimetype, if configured
+ if switcher.selected == -1 && pv.part.MIMEType != "multipart" {
+ switcher.selected = i
+ }
+ }
+ }
+ return nil
+}
+
func (mv *MessageViewer) Draw(ctx *ui.Context) {
if mv.err != nil {
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', tcell.StyleDefault)
@@ -205,6 +222,15 @@ func (mv *MessageViewer) SelectedMessage() *types.MessageInfo {
return mv.msg
}
+func (mv *MessageViewer) ToggleHeaders() {
+ switcher := mv.switcher
+ err := createSwitcher(switcher, mv.conf, mv.store, mv.msg, !switcher.showHeaders)
+ if err != nil {
+ mv.acct.Logger().Printf("warning: error during create switcher - %v", err)
+ }
+ switcher.Invalidate()
+}
+
func (mv *MessageViewer) CurrentPart() *PartInfo {
switcher := mv.switcher
part := switcher.parts[switcher.selected]
@@ -295,18 +321,19 @@ func (mv *MessageViewer) Focus(focus bool) {
type PartViewer struct {
ui.Invalidatable
- err error
- fetched bool
- filter *exec.Cmd
- index []int
- msg *types.MessageInfo
- pager *exec.Cmd
- pagerin io.WriteCloser
- part *imap.BodyStructure
- sink io.WriteCloser
- source io.Reader
- store *lib.MessageStore
- term *Terminal
+ err error
+ fetched bool
+ filter *exec.Cmd
+ index []int
+ msg *types.MessageInfo
+ pager *exec.Cmd
+ pagerin io.WriteCloser
+ part *imap.BodyStructure
+ showHeaders bool
+ sink io.WriteCloser
+ source io.Reader
+ store *lib.MessageStore
+ term *Terminal
}
type PartInfo struct {
@@ -318,7 +345,8 @@ type PartInfo struct {
func NewPartViewer(conf *config.AercConfig,
store *lib.MessageStore, msg *types.MessageInfo,
- part *imap.BodyStructure, index []int) (*PartViewer, error) {
+ part *imap.BodyStructure, showHeaders bool,
+ index []int) (*PartViewer, error) {
var (
filter *exec.Cmd
@@ -375,15 +403,16 @@ func NewPartViewer(conf *config.AercConfig,
}
pv := &PartViewer{
- filter: filter,
- index: index,
- msg: msg,
- pager: pager,
- pagerin: pagerin,
- part: part,
- sink: pipe,
- store: store,
- term: term,
+ filter: filter,
+ index: index,
+ msg: msg,
+ pager: pager,
+ pagerin: pagerin,
+ part: part,
+ showHeaders: showHeaders,
+ sink: pipe,
+ store: store,
+ term: term,
}
if term != nil {
@@ -439,6 +468,15 @@ func (pv *PartViewer) attemptCopy() {
}()
}
go func() {
+ if pv.showHeaders && pv.msg.RFC822Headers != nil {
+ fields := pv.msg.RFC822Headers.Fields()
+ for fields.Next() {
+ field := fmt.Sprintf("%s: %s\n", fields.Key(), fields.Value())
+ pv.sink.Write([]byte(field))
+ }
+ pv.sink.Write([]byte{'\n'})
+ }
+
entity, err := message.New(header, pv.source)
if err != nil {
pv.err = err
diff --git a/worker/imap/fetch.go b/worker/imap/fetch.go
index ac9d0096..49c9ac53 100644
--- a/worker/imap/fetch.go
+++ b/worker/imap/fetch.go
@@ -1,7 +1,12 @@
package imap
import (
+ "bufio"
+
"github.com/emersion/go-imap"
+ "github.com/emersion/go-message"
+ "github.com/emersion/go-message/mail"
+ "github.com/emersion/go-message/textproto"
"git.sr.ht/~sircmpwn/aerc/worker/types"
)
@@ -10,15 +15,22 @@ func (imapw *IMAPWorker) handleFetchMessageHeaders(
msg *types.FetchMessageHeaders) {
imapw.worker.Logger.Printf("Fetching message headers")
+ section := &imap.BodySectionName{
+ BodyPartName: imap.BodyPartName{
+ Specifier: imap.HeaderSpecifier,
+ },
+ }
+
items := []imap.FetchItem{
imap.FetchBodyStructure,
imap.FetchEnvelope,
imap.FetchInternalDate,
imap.FetchFlags,
imap.FetchUid,
+ section.FetchItem(),
}
- imapw.handleFetchMessages(msg, &msg.Uids, items, nil)
+ imapw.handleFetchMessages(msg, &msg.Uids, items, section)
}
func (imapw *IMAPWorker) handleFetchMessageBodyPart(
@@ -54,12 +66,19 @@ func (imapw *IMAPWorker) handleFetchMessages(
imapw.seqMap[_msg.SeqNum-1] = _msg.Uid
switch msg.(type) {
case *types.FetchMessageHeaders:
+ reader := _msg.GetBody(section)
+ textprotoHeader, err := textproto.ReadHeader(bufio.NewReader(reader))
+ var header *mail.Header
+ if err == nil {
+ header = &mail.Header{message.Header{textprotoHeader}}
+ }
imapw.worker.PostMessage(&types.MessageInfo{
Message: types.RespondTo(msg),
BodyStructure: _msg.BodyStructure,
Envelope: _msg.Envelope,
Flags: _msg.Flags,
InternalDate: _msg.InternalDate,
+ RFC822Headers: header,
Uid: _msg.Uid,
}, nil)
case *types.FetchFullMessages:
diff --git a/worker/types/messages.go b/worker/types/messages.go
index 4e46cbf4..29d3d9f7 100644
--- a/worker/types/messages.go
+++ b/worker/types/messages.go
@@ -5,6 +5,7 @@ import (
"time"
"github.com/emersion/go-imap"
+ "github.com/emersion/go-message/mail"
"git.sr.ht/~sircmpwn/aerc/config"
)
@@ -145,6 +146,7 @@ type MessageInfo struct {
Envelope *imap.Envelope
Flags []string
InternalDate time.Time
+ RFC822Headers *mail.Header
Size uint32
Uid uint32
}