maildir: track the recent flag correctly

In the maildir worker we manually need to track the Recent flag in order for the
notification command etc to work.

Push that responsibility to the container, we must make sure to manually add the
flag though if one grabs the message info.
This commit is contained in:
Reto Brunner 2020-09-13 10:42:40 +02:00 committed by Robin Jarry
parent 8d4c6e1adf
commit bc48628839
2 changed files with 55 additions and 31 deletions

View File

@ -15,9 +15,10 @@ import (
// A Container is a directory which contains other directories which adhere to // A Container is a directory which contains other directories which adhere to
// the Maildir spec // the Maildir spec
type Container struct { type Container struct {
dir string dir string
log *log.Logger log *log.Logger
uids *uidstore.Store uids *uidstore.Store
recentUIDS map[uint32]struct{} // used to set the recent flag
} }
// NewContainer creates a new container at the specified directory // NewContainer creates a new container at the specified directory
@ -34,7 +35,8 @@ func NewContainer(dir string, l *log.Logger) (*Container, error) {
if !s.IsDir() { if !s.IsDir() {
return nil, fmt.Errorf("Given maildir '%s' not a directory", dir) return nil, fmt.Errorf("Given maildir '%s' not a directory", dir)
} }
return &Container{dir: dir, uids: uidstore.NewStore(), log: l}, nil return &Container{dir: dir, uids: uidstore.NewStore(), log: l,
recentUIDS: make(map[uint32]struct{})}, nil
} }
// ListFolders returns a list of maildir folders in the container // ListFolders returns a list of maildir folders in the container
@ -72,17 +74,26 @@ func (c *Container) ListFolders() ([]string, error) {
return folders, err return folders, err
} }
// SyncNewMail adds emails from new to cur, tracking them
func (c *Container) SyncNewMail(dir maildir.Dir) error {
keys, err := dir.Unseen()
if err != nil {
return err
}
for _, key := range keys {
uid := c.uids.GetOrInsert(key)
c.recentUIDS[uid] = struct{}{}
}
return nil
}
// OpenDirectory opens an existing maildir in the container by name, moves new // OpenDirectory opens an existing maildir in the container by name, moves new
// messages into cur, and registers the new keys in the UIDStore. // messages into cur, and registers the new keys in the UIDStore.
func (c *Container) OpenDirectory(name string) (maildir.Dir, error) { func (c *Container) OpenDirectory(name string) (maildir.Dir, error) {
dir := c.Dir(name) dir := c.Dir(name)
keys, err := dir.Unseen() if err := c.SyncNewMail(dir); err != nil {
if err != nil {
return dir, err return dir, err
} }
for _, key := range keys {
c.uids.GetOrInsert(key)
}
return dir, nil return dir, nil
} }
@ -91,6 +102,17 @@ func (c *Container) Dir(name string) maildir.Dir {
return maildir.Dir(filepath.Join(c.dir, name)) return maildir.Dir(filepath.Join(c.dir, name))
} }
// IsRecent returns if a uid has the Recent flag set
func (c *Container) IsRecent(uid uint32) bool {
_, ok := c.recentUIDS[uid]
return ok
}
// ClearRecentFlag removes the Recent flag from the message with the given uid
func (c *Container) ClearRecentFlag(uid uint32) {
delete(c.recentUIDS, uid)
}
// UIDs fetches the unique message identifiers for the maildir // UIDs fetches the unique message identifiers for the maildir
func (c *Container) UIDs(d maildir.Dir) ([]uint32, error) { func (c *Container) UIDs(d maildir.Dir) ([]uint32, error) {
keys, err := d.Keys() keys, err := d.Keys()

View File

@ -79,11 +79,12 @@ func (w *Worker) handleFSEvent(ev fsnotify.Event) {
if w.selected == nil { if w.selected == nil {
return return
} }
newUnseen, err := w.selected.Unseen() err := w.c.SyncNewMail(*w.selected)
if err != nil { if err != nil {
w.worker.Logger.Printf("could not move new to cur : %v", err) w.worker.Logger.Printf("could not move new to cur : %v", err)
return return
} }
uids, err := w.c.UIDs(*w.selected) uids, err := w.c.UIDs(*w.selected)
if err != nil { if err != nil {
w.worker.Logger.Printf("could not scan UIDs: %v", err) w.worker.Logger.Printf("could not scan UIDs: %v", err)
@ -98,7 +99,6 @@ func (w *Worker) handleFSEvent(ev fsnotify.Event) {
Uids: sortedUids, Uids: sortedUids,
}, nil) }, nil)
dirInfo := w.getDirectoryInfo(w.selectedName) dirInfo := w.getDirectoryInfo(w.selectedName)
dirInfo.Recent = len(newUnseen)
w.worker.PostMessage(&types.DirectoryInfo{ w.worker.PostMessage(&types.DirectoryInfo{
Info: dirInfo, Info: dirInfo,
}, nil) }, nil)
@ -138,12 +138,6 @@ func (w *Worker) getDirectoryInfo(name string) *models.DirectoryInfo {
return dirInfo return dirInfo
} }
recent, err := dir.UnseenCount()
if err != nil {
w.worker.Logger.Printf("could not get unseen count: %v", err)
}
dirInfo.Recent = recent
for _, uid := range uids { for _, uid := range uids {
message, err := w.c.Message(dir, uid) message, err := w.c.Message(dir, uid)
if err != nil { if err != nil {
@ -164,9 +158,12 @@ func (w *Worker) getDirectoryInfo(name string) *models.DirectoryInfo {
if !seen { if !seen {
dirInfo.Unseen++ dirInfo.Unseen++
} }
if w.c.IsRecent(uid) {
dirInfo.Recent++
}
} }
dirInfo.Unseen += dirInfo.Recent dirInfo.Unseen += dirInfo.Recent
dirInfo.Exists = len(uids) + recent dirInfo.Exists = len(uids) + dirInfo.Recent
return dirInfo return dirInfo
} }
@ -332,12 +329,7 @@ func (w *Worker) sort(uids []uint32, criteria []*types.SortCriterion) ([]uint32,
} }
var msgInfos []*models.MessageInfo var msgInfos []*models.MessageInfo
for _, uid := range uids { for _, uid := range uids {
m, err := w.c.Message(*w.selected, uid) info, err := w.msgInfoFromUid(uid)
if err != nil {
w.worker.Logger.Printf("could not get message: %v", err)
continue
}
info, err := m.MessageInfo()
if err != nil { if err != nil {
w.worker.Logger.Printf("could not get message info: %v", err) w.worker.Logger.Printf("could not get message info: %v", err)
continue continue
@ -375,13 +367,7 @@ func (w *Worker) handleRemoveDirectory(msg *types.RemoveDirectory) error {
func (w *Worker) handleFetchMessageHeaders( func (w *Worker) handleFetchMessageHeaders(
msg *types.FetchMessageHeaders) error { msg *types.FetchMessageHeaders) error {
for _, uid := range msg.Uids { for _, uid := range msg.Uids {
m, err := w.c.Message(*w.selected, uid) info, err := w.msgInfoFromUid(uid)
if err != nil {
w.worker.Logger.Printf("could not get message: %v", err)
w.err(msg, err)
continue
}
info, err := m.MessageInfo()
if err != nil { if err != nil {
w.worker.Logger.Printf("could not get message info: %v", err) w.worker.Logger.Printf("could not get message info: %v", err)
w.err(msg, err) w.err(msg, err)
@ -391,6 +377,7 @@ func (w *Worker) handleFetchMessageHeaders(
Message: types.RespondTo(msg), Message: types.RespondTo(msg),
Info: info, Info: info,
}, nil) }, nil)
w.c.ClearRecentFlag(uid)
} }
return nil return nil
} }
@ -588,3 +575,18 @@ func (w *Worker) handleSearchDirectory(msg *types.SearchDirectory) error {
}, nil) }, nil)
return nil return nil
} }
func (w *Worker) msgInfoFromUid(uid uint32) (*models.MessageInfo, error) {
m, err := w.c.Message(*w.selected, uid)
if err != nil {
return nil, err
}
info, err := m.MessageInfo()
if err != nil {
return nil, err
}
if w.c.IsRecent(uid) {
info.Flags = append(info.Flags, models.RecentFlag)
}
return info, nil
}