package msg import ( "errors" "fmt" "strings" "git.sr.ht/~rjarry/aerc/app" "git.sr.ht/~rjarry/aerc/commands" "git.sr.ht/~rjarry/aerc/lib/format" "git.sr.ht/~rjarry/aerc/lib/log" "git.sr.ht/~rjarry/aerc/models" "github.com/emersion/go-message/mail" ) type Envelope struct { Header bool `opt:"-h" desc:"Show all header fields."` Format string `opt:"-s" default:"%-20.20s: %s" desc:"Format specifier."` } func init() { commands.Register(Envelope{}) } func (Envelope) Description() string { return "Open the message envelope in a dialog popup." } func (Envelope) Context() commands.CommandContext { return commands.MESSAGE_LIST | commands.MESSAGE_VIEWER } func (Envelope) Aliases() []string { return []string{"envelope"} } func (e Envelope) Execute(args []string) error { provider, ok := app.SelectedTabContent().(app.ProvidesMessages) if !ok { return fmt.Errorf("current tab does not implement app.ProvidesMessage interface") } acct := provider.SelectedAccount() if acct == nil { return errors.New("No account selected") } var list []string if msg, err := provider.SelectedMessage(); err != nil { return err } else { if msg != nil { if e.Header { list = parseHeader(msg, e.Format) } else { list = parseEnvelope(msg, e.Format, acct.UiConfig().TimestampFormat) } } else { return fmt.Errorf("Selected message is empty.") } } app.AddDialog(app.DefaultDialog( app.NewListBox( "Message Envelope. Press or to close. "+ "Start typing to filter.", list, app.SelectedAccountUiConfig(), func(_ string) { app.CloseDialog() }, ), )) 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 { log.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 }