2019-06-07 21:35:23 +02:00
|
|
|
package lib
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
"unicode"
|
|
|
|
|
|
|
|
"github.com/emersion/go-imap"
|
|
|
|
|
|
|
|
"git.sr.ht/~sircmpwn/aerc/config"
|
2019-07-08 04:43:56 +02:00
|
|
|
"git.sr.ht/~sircmpwn/aerc/models"
|
2019-06-07 21:35:23 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
func ParseIndexFormat(conf *config.AercConfig, number int,
|
2019-07-08 04:43:56 +02:00
|
|
|
msg *models.MessageInfo) (string, []interface{}, error) {
|
2019-06-07 21:35:23 +02:00
|
|
|
|
|
|
|
format := conf.Ui.IndexFormat
|
|
|
|
retval := make([]byte, 0, len(format))
|
|
|
|
var args []interface{}
|
|
|
|
|
|
|
|
var c rune
|
|
|
|
for i, ni := 0, 0; i < len(format); {
|
|
|
|
ni = strings.IndexByte(format[i:], '%')
|
|
|
|
if ni < 0 {
|
|
|
|
ni = len(format)
|
|
|
|
retval = append(retval, []byte(format[i:ni])...)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
ni += i + 1
|
|
|
|
// Check for fmt flags
|
|
|
|
if ni == len(format) {
|
|
|
|
goto handle_end_error
|
|
|
|
}
|
|
|
|
c = rune(format[ni])
|
|
|
|
if c == '+' || c == '-' || c == '#' || c == ' ' || c == '0' {
|
|
|
|
ni++
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for precision and width
|
|
|
|
if ni == len(format) {
|
|
|
|
goto handle_end_error
|
|
|
|
}
|
|
|
|
c = rune(format[ni])
|
|
|
|
for unicode.IsDigit(c) {
|
|
|
|
ni++
|
|
|
|
c = rune(format[ni])
|
|
|
|
}
|
|
|
|
if c == '.' {
|
|
|
|
ni++
|
|
|
|
c = rune(format[ni])
|
|
|
|
for unicode.IsDigit(c) {
|
|
|
|
ni++
|
|
|
|
c = rune(format[ni])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
retval = append(retval, []byte(format[i:ni])...)
|
|
|
|
// Get final format verb
|
|
|
|
if ni == len(format) {
|
|
|
|
goto handle_end_error
|
|
|
|
}
|
|
|
|
c = rune(format[ni])
|
|
|
|
switch c {
|
|
|
|
case '%':
|
|
|
|
retval = append(retval, '%')
|
|
|
|
case 'a':
|
|
|
|
if len(msg.Envelope.From) == 0 {
|
|
|
|
return "", nil, errors.New("found no address for sender")
|
|
|
|
}
|
|
|
|
addr := msg.Envelope.From[0]
|
|
|
|
retval = append(retval, 's')
|
|
|
|
args = append(args, fmt.Sprintf("%s@%s", addr.MailboxName,
|
|
|
|
addr.HostName))
|
|
|
|
case 'A':
|
|
|
|
var addr *imap.Address
|
|
|
|
if len(msg.Envelope.ReplyTo) == 0 {
|
|
|
|
if len(msg.Envelope.From) == 0 {
|
|
|
|
return "", nil,
|
|
|
|
errors.New("found no address for sender or reply-to")
|
|
|
|
} else {
|
|
|
|
addr = msg.Envelope.From[0]
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
addr = msg.Envelope.ReplyTo[0]
|
|
|
|
}
|
|
|
|
retval = append(retval, 's')
|
|
|
|
args = append(args, fmt.Sprintf("%s@%s", addr.MailboxName,
|
|
|
|
addr.HostName))
|
|
|
|
case 'C':
|
|
|
|
retval = append(retval, 'd')
|
|
|
|
args = append(args, number)
|
|
|
|
case 'd':
|
|
|
|
retval = append(retval, 's')
|
|
|
|
args = append(args, msg.InternalDate.Format(conf.Ui.TimestampFormat))
|
|
|
|
case 'D':
|
|
|
|
retval = append(retval, 's')
|
|
|
|
args = append(args, msg.InternalDate.Local().Format(conf.Ui.TimestampFormat))
|
|
|
|
case 'f':
|
|
|
|
if len(msg.Envelope.From) == 0 {
|
|
|
|
return "", nil, errors.New("found no address for sender")
|
|
|
|
}
|
|
|
|
addr := FormatAddress(msg.Envelope.From[0])
|
|
|
|
retval = append(retval, 's')
|
|
|
|
args = append(args, addr)
|
|
|
|
case 'F':
|
|
|
|
if len(msg.Envelope.From) == 0 {
|
|
|
|
return "", nil, errors.New("found no address for sender")
|
|
|
|
}
|
|
|
|
addr := msg.Envelope.From[0]
|
|
|
|
// TODO: handle case when sender is current user. Then
|
|
|
|
// use recipient's name
|
|
|
|
var val string
|
|
|
|
if addr.PersonalName != "" {
|
|
|
|
val = addr.PersonalName
|
|
|
|
} else {
|
|
|
|
val = fmt.Sprintf("%s@%s",
|
|
|
|
addr.MailboxName, addr.HostName)
|
|
|
|
}
|
|
|
|
retval = append(retval, 's')
|
|
|
|
args = append(args, val)
|
|
|
|
|
|
|
|
case 'i':
|
|
|
|
retval = append(retval, 's')
|
|
|
|
args = append(args, msg.Envelope.MessageId)
|
|
|
|
case 'n':
|
|
|
|
if len(msg.Envelope.From) == 0 {
|
|
|
|
return "", nil, errors.New("found no address for sender")
|
|
|
|
}
|
|
|
|
addr := msg.Envelope.From[0]
|
|
|
|
var val string
|
|
|
|
if addr.PersonalName != "" {
|
|
|
|
val = addr.PersonalName
|
|
|
|
} else {
|
|
|
|
val = fmt.Sprintf("%s@%s",
|
|
|
|
addr.MailboxName, addr.HostName)
|
|
|
|
}
|
|
|
|
retval = append(retval, 's')
|
|
|
|
args = append(args, val)
|
|
|
|
case 'r':
|
|
|
|
addrs := FormatAddresses(msg.Envelope.To)
|
|
|
|
retval = append(retval, 's')
|
|
|
|
args = append(args, addrs)
|
|
|
|
case 'R':
|
|
|
|
addrs := FormatAddresses(msg.Envelope.Cc)
|
|
|
|
retval = append(retval, 's')
|
|
|
|
args = append(args, addrs)
|
|
|
|
case 's':
|
|
|
|
retval = append(retval, 's')
|
|
|
|
args = append(args, msg.Envelope.Subject)
|
|
|
|
case 'u':
|
|
|
|
if len(msg.Envelope.From) == 0 {
|
|
|
|
return "", nil, errors.New("found no address for sender")
|
|
|
|
}
|
|
|
|
addr := msg.Envelope.From[0]
|
|
|
|
retval = append(retval, 's')
|
|
|
|
args = append(args, addr.MailboxName)
|
|
|
|
case 'v':
|
|
|
|
if len(msg.Envelope.From) == 0 {
|
|
|
|
return "", nil, errors.New("found no address for sender")
|
|
|
|
}
|
|
|
|
addr := msg.Envelope.From[0]
|
|
|
|
// check if message is from current user
|
|
|
|
if addr.PersonalName != "" {
|
|
|
|
retval = append(retval, 's')
|
|
|
|
args = append(args, strings.Split(addr.PersonalName, " ")[0])
|
|
|
|
}
|
|
|
|
case 'Z':
|
|
|
|
// calculate all flags
|
|
|
|
var readFlag = ""
|
|
|
|
var delFlag = ""
|
|
|
|
var flaggedFlag = ""
|
|
|
|
for _, flag := range msg.Flags {
|
2019-06-08 15:10:14 +02:00
|
|
|
if flag == imap.SeenFlag {
|
2019-06-07 21:35:23 +02:00
|
|
|
readFlag = "O" // message is old
|
2019-06-08 15:10:14 +02:00
|
|
|
} else if flag == imap.RecentFlag {
|
2019-06-07 21:35:23 +02:00
|
|
|
readFlag = "N" // message is new
|
2019-06-08 15:10:14 +02:00
|
|
|
} else if flag == imap.AnsweredFlag {
|
2019-06-07 21:35:23 +02:00
|
|
|
readFlag = "r" // message has been replied to
|
2019-06-14 23:11:17 +02:00
|
|
|
}
|
|
|
|
if flag == imap.DeletedFlag {
|
2019-06-07 21:35:23 +02:00
|
|
|
delFlag = "D"
|
|
|
|
// TODO: check if attachments
|
2019-06-14 23:11:17 +02:00
|
|
|
}
|
|
|
|
if flag == imap.FlaggedFlag {
|
2019-06-07 21:35:23 +02:00
|
|
|
flaggedFlag = "!"
|
|
|
|
}
|
|
|
|
// TODO: check gpg stuff
|
|
|
|
}
|
|
|
|
retval = append(retval, '3', 's')
|
|
|
|
args = append(args, readFlag+delFlag+flaggedFlag)
|
|
|
|
|
|
|
|
// Move the below cases to proper alphabetical positions once
|
|
|
|
// implemented
|
|
|
|
case 'l':
|
|
|
|
// TODO: number of lines in the message
|
|
|
|
retval = append(retval, 'd')
|
|
|
|
args = append(args, msg.Size)
|
|
|
|
case 'e':
|
|
|
|
// TODO: current message number in thread
|
|
|
|
fallthrough
|
|
|
|
case 'E':
|
|
|
|
// TODO: number of messages in current thread
|
|
|
|
fallthrough
|
|
|
|
case 'H':
|
|
|
|
// TODO: spam attribute(s) of this message
|
|
|
|
fallthrough
|
|
|
|
case 'L':
|
|
|
|
// TODO:
|
|
|
|
fallthrough
|
|
|
|
case 'X':
|
|
|
|
// TODO: number of attachments
|
|
|
|
fallthrough
|
|
|
|
case 'y':
|
|
|
|
// TODO: X-Label field
|
|
|
|
fallthrough
|
|
|
|
case 'Y':
|
|
|
|
// TODO: X-Label field and some other constraints
|
|
|
|
fallthrough
|
|
|
|
default:
|
|
|
|
// Just ignore it and print as is
|
|
|
|
// so %k in index format becomes %%k to Printf
|
|
|
|
retval = append(retval, '%')
|
|
|
|
retval = append(retval, byte(c))
|
|
|
|
}
|
|
|
|
i = ni + 1
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(retval), args, nil
|
|
|
|
|
|
|
|
handle_end_error:
|
|
|
|
return "", nil, errors.New("reached end of string while parsing index format")
|
|
|
|
}
|