package state import ( "strings" "time" "git.sr.ht/~rjarry/aerc/config" "git.sr.ht/~rjarry/aerc/models" sortthread "github.com/emersion/go-imap-sortthread" "github.com/emersion/go-message/mail" ) type TemplateData struct { // only available when composing/replying/forwarding headers *mail.Header // only available when replying with a quote parent *models.OriginalMail // only available for the message list info *models.MessageInfo marked bool msgNum int // message list threading ThreadSameSubject bool ThreadPrefix string // selected account account *config.AccountConfig myAddresses map[string]bool folder string // selected folder name folders []string getRUEcount func(string) (int, int, int) } // only used for compose/reply/forward func (d *TemplateData) SetHeaders(h *mail.Header, o *models.OriginalMail) { d.headers = h d.parent = o } // only used for message list templates func (d *TemplateData) SetInfo(info *models.MessageInfo, num int, marked bool) { d.info = info d.msgNum = num d.marked = marked } func (d *TemplateData) SetAccount(acct *config.AccountConfig) { d.account = acct d.myAddresses = make(map[string]bool) if acct != nil { d.myAddresses[acct.From.Address] = true for _, addr := range acct.Aliases { d.myAddresses[addr.Address] = true } } } func (d *TemplateData) SetFolder(folder string) { d.folder = folder } func (d *TemplateData) SetRUE(folders []string, cb func(string) (int, int, int)) { d.folders = folders d.getRUEcount = cb } func (d *TemplateData) Account() string { if d.account != nil { return d.account.Name } return "" } func (d *TemplateData) Folder() string { return d.folder } func (d *TemplateData) ui() *config.UIConfig { return config.Ui.ForAccount(d.Account()).ForFolder(d.folder) } func (d *TemplateData) To() []*mail.Address { var to []*mail.Address switch { case d.info != nil && d.info.Envelope != nil: to = d.info.Envelope.To case d.headers != nil: to, _ = d.headers.AddressList("to") } return to } func (d *TemplateData) Cc() []*mail.Address { var cc []*mail.Address switch { case d.info != nil && d.info.Envelope != nil: cc = d.info.Envelope.Cc case d.headers != nil: cc, _ = d.headers.AddressList("cc") } return cc } func (d *TemplateData) Bcc() []*mail.Address { var bcc []*mail.Address switch { case d.info != nil && d.info.Envelope != nil: bcc = d.info.Envelope.Bcc case d.headers != nil: bcc, _ = d.headers.AddressList("bcc") } return bcc } func (d *TemplateData) From() []*mail.Address { var from []*mail.Address switch { case d.info != nil && d.info.Envelope != nil: from = d.info.Envelope.From case d.headers != nil: from, _ = d.headers.AddressList("from") } return from } func (d *TemplateData) Peer() []*mail.Address { var from, to []*mail.Address switch { case d.info != nil && d.info.Envelope != nil: from = d.info.Envelope.From to = d.info.Envelope.To case d.headers != nil: from, _ = d.headers.AddressList("from") to, _ = d.headers.AddressList("to") } for _, addr := range from { if d.myAddresses[addr.Address] { return to } } return from } func (d *TemplateData) ReplyTo() []*mail.Address { var replyTo []*mail.Address switch { case d.info != nil && d.info.Envelope != nil: replyTo = d.info.Envelope.ReplyTo case d.headers != nil: replyTo, _ = d.headers.AddressList("reply-to") } return replyTo } func (d *TemplateData) Date() time.Time { var date time.Time switch { case d.info != nil && d.info.Envelope != nil: date = d.info.Envelope.Date case d.info != nil: date = d.info.InternalDate default: date = time.Now() } return date } func (d *TemplateData) DateAutoFormat(date time.Time) string { if date.IsZero() { return "" } ui := d.ui() year := date.Year() day := date.YearDay() now := time.Now() thisYear := now.Year() thisDay := now.YearDay() fmt := ui.TimestampFormat if year == thisYear { switch { case day == thisDay && ui.ThisDayTimeFormat != "": fmt = ui.ThisDayTimeFormat case day > thisDay-7 && ui.ThisWeekTimeFormat != "": fmt = ui.ThisWeekTimeFormat case ui.ThisYearTimeFormat != "": fmt = ui.ThisYearTimeFormat } } return date.Format(fmt) } func (d *TemplateData) Header(name string) string { var h *mail.Header switch { case d.headers != nil: h = d.headers case d.info != nil && d.info.RFC822Headers != nil: h = d.info.RFC822Headers default: return "" } text, err := h.Text(name) if err != nil { text = h.Get(name) } return text } func (d *TemplateData) Subject() string { var subject string switch { case d.info != nil && d.info.Envelope != nil: subject = d.info.Envelope.Subject case d.headers != nil: subject = d.Header("subject") } if d.ThreadSameSubject { subject = "" } return d.ThreadPrefix + subject } func (d *TemplateData) SubjectBase() string { base, _ := sortthread.GetBaseSubject(d.Subject()) return base } func (d *TemplateData) Number() int { return d.msgNum } func (d *TemplateData) Labels() []string { if d.info == nil { return nil } return d.info.Labels } func (d *TemplateData) Flags() []string { var flags []string if d.info == nil { return flags } switch { case d.info.Flags.Has(models.SeenFlag | models.AnsweredFlag): flags = append(flags, "r") // message has been replied to case d.info.Flags.Has(models.SeenFlag): break case d.info.Flags.Has(models.RecentFlag): flags = append(flags, "N") // message is new default: flags = append(flags, "O") // message is old } if d.info.Flags.Has(models.DeletedFlag) { flags = append(flags, "D") } if d.info.BodyStructure != nil { for _, bS := range d.info.BodyStructure.Parts { if strings.ToLower(bS.Disposition) == "attachment" { flags = append(flags, d.ui().IconAttachment) break } } } if d.info.Flags.Has(models.FlaggedFlag) { flags = append(flags, "!") } if d.marked { flags = append(flags, "*") } return flags } func (d *TemplateData) MessageId() string { if d.info == nil || d.info.Envelope == nil { return "" } return d.info.Envelope.MessageId } func (d *TemplateData) Size() int { if d.info == nil || d.info.Envelope == nil { return 0 } return int(d.info.Size) } func (d *TemplateData) OriginalText() string { if d.parent == nil { return "" } return d.parent.Text } func (d *TemplateData) OriginalDate() time.Time { if d.parent == nil { return time.Time{} } return d.parent.Date } func (d *TemplateData) OriginalFrom() []*mail.Address { if d.parent == nil || d.parent.RFC822Headers == nil { return nil } from, _ := d.parent.RFC822Headers.AddressList("from") return from } func (d *TemplateData) OriginalMIMEType() string { if d.parent == nil { return "" } return d.parent.MIMEType } func (d *TemplateData) OriginalHeader(name string) string { if d.parent == nil || d.parent.RFC822Headers == nil { return "" } text, err := d.parent.RFC822Headers.Text(name) if err != nil { text = d.parent.RFC822Headers.Get(name) } return text } func (d *TemplateData) rue(folders ...string) (int, int, int) { var recent, unread, exists int if d.getRUEcount != nil { if len(folders) == 0 { folders = d.folders } for _, dir := range folders { r, u, e := d.getRUEcount(dir) recent += r unread += u exists += e } } return recent, unread, exists } func (d *TemplateData) Recent(folders ...string) int { r, _, _ := d.rue(folders...) return r } func (d *TemplateData) Unread(folders ...string) int { _, u, _ := d.rue(folders...) return u } func (d *TemplateData) Exists(folders ...string) int { _, _, e := d.rue(folders...) return e }