summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorYash Srivastav <yash111998@gmail.com>2019-06-07 13:56:14 +0530
committerDrew DeVault <sir@cmpwn.com>2019-06-07 09:20:24 -0400
commitb83e7c9fa6a0d187a0f20d98d522cff792053cdd (patch)
tree4c3765b41d216e26406d21d3b045f340edabbadc
parent2279ac3ab3d19cd233600907bc8e3e8b4e57b350 (diff)
downloadaerc-b83e7c9fa6a0d187a0f20d98d522cff792053cdd.tar.gz
implements ability to view headers in message view
-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 0000000..fc29042
--- /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 090e624..0bf114f 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 889a63d..3b7edbb 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 45a5ed0..b82fa8d 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 ac9d009..49c9ac5 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 4e46cbf..29d3d9f 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
 }