implements ability to view headers in message view

This commit is contained in:
Yash Srivastav 2019-06-07 13:56:14 +05:30 committed by Drew DeVault
parent 2279ac3ab3
commit b83e7c9fa6
6 changed files with 146 additions and 54 deletions

View File

@ -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
}

View File

@ -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

View File

@ -72,6 +72,7 @@ type FilterConfig struct {
type ViewerConfig struct {
Pager string
Alternatives []string
ShowHeaders bool `ini:"show-headers"`
}
type AercConfig struct {

View File

@ -37,6 +37,7 @@ type PartSwitcher struct {
ui.Invalidatable
parts []*PartViewer
selected int
showHeaders bool
}
func formatAddresses(addrs []*imap.Address) string {
@ -98,40 +99,18 @@ 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})
err := createSwitcher(switcher, conf, store, msg, conf.Viewer.ShowHeaders)
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
}
}
}
grid.AddChild(headers).At(0, 0)
grid.AddChild(switcher).At(1, 0)
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]
@ -303,6 +329,7 @@ type PartViewer struct {
pager *exec.Cmd
pagerin io.WriteCloser
part *imap.BodyStructure
showHeaders bool
sink io.WriteCloser
source io.Reader
store *lib.MessageStore
@ -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
@ -381,6 +409,7 @@ func NewPartViewer(conf *config.AercConfig,
pager: pager,
pagerin: pagerin,
part: part,
showHeaders: showHeaders,
sink: pipe,
store: store,
term: term,
@ -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

View File

@ -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:

View File

@ -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
}