From 19e275025538a92bae92b152c4e39bdd22ad7e7f Mon Sep 17 00:00:00 2001 From: Koni Marti Date: Thu, 20 Oct 2022 23:55:28 +0200 Subject: [PATCH] envelope: display message envelope info Display entire message envelope in a user-friendly dialog popup with the :envelope command. All header fields can be displayed with the -h flag. Fixes: https://todo.sr.ht/~rjarry/aerc/85 Signed-off-by: Koni Marti Acked-by: Robin Jarry --- CHANGELOG.md | 4 ++ commands/msg/envelope.go | 152 +++++++++++++++++++++++++++++++++++++++ doc/aerc.1.scd | 9 +++ 3 files changed, 165 insertions(+) create mode 100644 commands/msg/envelope.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 9eb86e6..30ed0d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased](https://git.sr.ht/~rjarry/aerc/log/master) +### Added + +- View common email envelope headers with `:envelope`. + ### Fixed - `:pipe -m git am -3` on patch series when `Message-Id` headers have not been diff --git a/commands/msg/envelope.go b/commands/msg/envelope.go new file mode 100644 index 0000000..616798d --- /dev/null +++ b/commands/msg/envelope.go @@ -0,0 +1,152 @@ +package msg + +import ( + "errors" + "fmt" + "strings" + + "git.sr.ht/~rjarry/aerc/lib/format" + "git.sr.ht/~rjarry/aerc/logging" + "git.sr.ht/~rjarry/aerc/models" + "git.sr.ht/~rjarry/aerc/widgets" + "git.sr.ht/~sircmpwn/getopt" + "github.com/emersion/go-message/mail" +) + +type Envelope struct{} + +func init() { + register(Envelope{}) +} + +func (Envelope) Aliases() []string { + return []string{"envelope"} +} + +func (Envelope) Complete(aerc *widgets.Aerc, args []string) []string { + return nil +} + +func (Envelope) Execute(aerc *widgets.Aerc, args []string) error { + header := false + fmtStr := "%-20.20s: %s" + opts, _, err := getopt.Getopts(args, "hs:") + if err != nil { + return err + } + for _, opt := range opts { + switch opt.Option { + case 's': + fmtStr = opt.Value + case 'h': + header = true + } + } + + acct := aerc.SelectedAccount() + if acct == nil { + return errors.New("No account selected") + } + + var list []string + if msg, err := acct.SelectedMessage(); err != nil { + return err + } else { + if msg != nil { + if header { + list = parseHeader(msg, fmtStr) + } else { + list = parseEnvelope(msg, fmtStr, + acct.UiConfig().TimestampFormat) + } + } else { + return fmt.Errorf("Selected message is empty.") + } + } + + n := len(list) + aerc.AddDialog(widgets.NewDialog( + widgets.NewListBox( + "Message Envelope. Press or to close. "+ + "Start typing to filter.", + list, + aerc.SelectedAccountUiConfig(), + func(_ string) { + aerc.CloseDialog() + }, + ), + // start pos on screen + func(h int) int { + if n < h/8*6 { + return h/2 - n/2 - 1 + } + return h / 8 + }, + // dialog height + func(h int) int { + if n < h/8*6 { + return n + 2 + } + return h / 8 * 6 + }, + )) + + return nil +} + +func parseEnvelope(msg *models.MessageInfo, fmtStr, fmtTime string, +) (result []string) { + if envlp := msg.Envelope; envlp != nil { + addStr := func(key, text string) { + result = append(result, fmt.Sprintf(fmtStr, key, text)) + } + addAddr := func(key string, ls []*mail.Address) { + for _, l := range ls { + result = append(result, + fmt.Sprintf(fmtStr, key, + format.AddressForHumans(l))) + } + } + + addStr("Date", envlp.Date.Format(fmtTime)) + addStr("Subject", envlp.Subject) + addStr("Message-Id", envlp.MessageId) + + addAddr("From", envlp.From) + addAddr("To", envlp.To) + addAddr("ReplyTo", envlp.ReplyTo) + addAddr("Cc", envlp.Cc) + addAddr("Bcc", envlp.Bcc) + } + return +} + +func parseHeader(msg *models.MessageInfo, fmtStr string) (result []string) { + if h := msg.RFC822Headers; h != nil { + hf := h.Fields() + for hf.Next() { + text, err := hf.Text() + if err != nil { + logging.Errorf(err.Error()) + text = hf.Value() + } + result = append(result, + headerExpand(fmtStr, hf.Key(), text)...) + } + } + return +} + +func headerExpand(fmtStr, key, text string) []string { + var result []string + switch strings.ToLower(key) { + case "to", "from", "bcc", "cc": + for _, item := range strings.Split(text, ",") { + result = append(result, fmt.Sprintf(fmtStr, key, + strings.TrimSpace(item))) + } + default: + result = append(result, fmt.Sprintf(fmtStr, key, text)) + } + return result +} diff --git a/doc/aerc.1.scd b/doc/aerc.1.scd index ea4be17..f14d467 100644 --- a/doc/aerc.1.scd +++ b/doc/aerc.1.scd @@ -162,6 +162,15 @@ message list, the message in the message viewer, etc). *delete-message* Deletes the selected message. +*envelope [-h] [-s ]* + Opens the message envelope in a dialog popup. + + *-h*: Show all header fields + + *-s* + User-defined format specifier requiring two %s for the key and + value strings. Default format: '%-20.20s: %s' + *recall* [-f] Opens the selected message for re-editing. Messages can only be recalled from the postpone directory. The original message is deleted.