worker/maildir: implement Maildir++ support
See https://www.courier-mta.org/maildir.html#maildircontents Signed-off-by: Adnan Maolood <me@adnano.co> Acked-by: Koni Marti <koni.marti@gmail.com>
This commit is contained in:
parent
4d3156ddf1
commit
c5daf43460
3 changed files with 49 additions and 4 deletions
|
@ -44,6 +44,13 @@ The following maildir-specific options are available:
|
||||||
|
|
||||||
source = maildir://~/mail
|
source = maildir://~/mail
|
||||||
|
|
||||||
|
If your maildir is using the Maildir++ directory layout, you can use the
|
||||||
|
_maildirpp://_ scheme instead:
|
||||||
|
|
||||||
|
source = maildirpp:///home/me/mail
|
||||||
|
|
||||||
|
source = maildirpp://~/mail
|
||||||
|
|
||||||
# SEE ALSO
|
# SEE ALSO
|
||||||
|
|
||||||
*aerc*(1) *aerc-config*(5) *aerc-smtp*(5) *aerc-notmuch*(5)
|
*aerc*(1) *aerc-config*(5) *aerc-smtp*(5) *aerc-notmuch*(5)
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/emersion/go-maildir"
|
"github.com/emersion/go-maildir"
|
||||||
|
|
||||||
|
@ -19,10 +20,11 @@ type Container struct {
|
||||||
log *log.Logger
|
log *log.Logger
|
||||||
uids *uidstore.Store
|
uids *uidstore.Store
|
||||||
recentUIDS map[uint32]struct{} // used to set the recent flag
|
recentUIDS map[uint32]struct{} // used to set the recent flag
|
||||||
|
maildirpp bool // whether to use Maildir++ directory layout
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewContainer creates a new container at the specified directory
|
// NewContainer creates a new container at the specified directory
|
||||||
func NewContainer(dir string, l *log.Logger) (*Container, error) {
|
func NewContainer(dir string, l *log.Logger, maildirpp bool) (*Container, error) {
|
||||||
f, err := os.Open(dir)
|
f, err := os.Open(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -36,16 +38,19 @@ func NewContainer(dir string, l *log.Logger) (*Container, error) {
|
||||||
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,
|
return &Container{dir: dir, uids: uidstore.NewStore(), log: l,
|
||||||
recentUIDS: make(map[uint32]struct{})}, nil
|
recentUIDS: make(map[uint32]struct{}), maildirpp: maildirpp}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListFolders returns a list of maildir folders in the container
|
// ListFolders returns a list of maildir folders in the container
|
||||||
func (c *Container) ListFolders() ([]string, error) {
|
func (c *Container) ListFolders() ([]string, error) {
|
||||||
folders := []string{}
|
folders := []string{}
|
||||||
|
if c.maildirpp {
|
||||||
|
// In Maildir++ layout, INBOX is the root folder
|
||||||
|
folders = append(folders, "INBOX")
|
||||||
|
}
|
||||||
err := filepath.Walk(c.dir, func(path string, info os.FileInfo, err error) error {
|
err := filepath.Walk(c.dir, func(path string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Invalid path '%s': error: %v", path, err)
|
return fmt.Errorf("Invalid path '%s': error: %v", path, err)
|
||||||
|
|
||||||
}
|
}
|
||||||
if !info.IsDir() {
|
if !info.IsDir() {
|
||||||
return nil
|
return nil
|
||||||
|
@ -68,6 +73,21 @@ func (c *Container) ListFolders() ([]string, error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.maildirpp {
|
||||||
|
// In Maildir++ layout, mailboxes are stored in a single directory
|
||||||
|
// and prefixed with a dot, and subfolders are separated by dots.
|
||||||
|
if !strings.HasPrefix(dirPath, ".") {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
dirPath = strings.TrimPrefix(dirPath, ".")
|
||||||
|
dirPath = strings.Replace(dirPath, ".", "/", -1)
|
||||||
|
folders = append(folders, dirPath)
|
||||||
|
|
||||||
|
// Since all mailboxes are stored in a single directory, don't
|
||||||
|
// recurse into subdirectories
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
|
||||||
folders = append(folders, dirPath)
|
folders = append(folders, dirPath)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
@ -99,6 +119,13 @@ func (c *Container) OpenDirectory(name string) (maildir.Dir, error) {
|
||||||
|
|
||||||
// Dir returns a maildir.Dir with the specified name inside the container
|
// Dir returns a maildir.Dir with the specified name inside the container
|
||||||
func (c *Container) Dir(name string) maildir.Dir {
|
func (c *Container) Dir(name string) maildir.Dir {
|
||||||
|
if c.maildirpp {
|
||||||
|
// Use Maildir++ layout
|
||||||
|
if name == "INBOX" {
|
||||||
|
return maildir.Dir(c.dir)
|
||||||
|
}
|
||||||
|
return maildir.Dir(filepath.Join(c.dir, "."+strings.Replace(name, "/", ".", -1)))
|
||||||
|
}
|
||||||
return maildir.Dir(filepath.Join(c.dir, name))
|
return maildir.Dir(filepath.Join(c.dir, name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
handlers.RegisterWorkerFactory("maildir", NewWorker)
|
handlers.RegisterWorkerFactory("maildir", NewWorker)
|
||||||
|
handlers.RegisterWorkerFactory("maildirpp", NewMaildirppWorker)
|
||||||
}
|
}
|
||||||
|
|
||||||
var errUnsupported = fmt.Errorf("unsupported command")
|
var errUnsupported = fmt.Errorf("unsupported command")
|
||||||
|
@ -37,6 +38,7 @@ type Worker struct {
|
||||||
worker *types.Worker
|
worker *types.Worker
|
||||||
watcher *fsnotify.Watcher
|
watcher *fsnotify.Watcher
|
||||||
currentSortCriteria []*types.SortCriterion
|
currentSortCriteria []*types.SortCriterion
|
||||||
|
maildirpp bool // whether to use Maildir++ directory layout
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWorker creates a new maildir worker with the provided worker.
|
// NewWorker creates a new maildir worker with the provided worker.
|
||||||
|
@ -48,6 +50,15 @@ func NewWorker(worker *types.Worker) (types.Backend, error) {
|
||||||
return &Worker{worker: worker, watcher: watch}, nil
|
return &Worker{worker: worker, watcher: watch}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewMaildirppWorker creates a new Maildir++ worker with the provided worker.
|
||||||
|
func NewMaildirppWorker(worker *types.Worker) (types.Backend, error) {
|
||||||
|
watch, err := fsnotify.NewWatcher()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not create file system watcher: %v", err)
|
||||||
|
}
|
||||||
|
return &Worker{worker: worker, watcher: watch, maildirpp: true}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Run starts the worker's message handling loop.
|
// Run starts the worker's message handling loop.
|
||||||
func (w *Worker) Run() {
|
func (w *Worker) Run() {
|
||||||
for {
|
for {
|
||||||
|
@ -301,7 +312,7 @@ func (w *Worker) handleConfigure(msg *types.Configure) error {
|
||||||
if len(dir) == 0 {
|
if len(dir) == 0 {
|
||||||
return fmt.Errorf("could not resolve maildir from URL '%s'", msg.Config.Source)
|
return fmt.Errorf("could not resolve maildir from URL '%s'", msg.Config.Source)
|
||||||
}
|
}
|
||||||
c, err := NewContainer(dir, w.worker.Logger)
|
c, err := NewContainer(dir, w.worker.Logger, w.maildirpp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.worker.Logger.Printf("could not configure maildir: %s", dir)
|
w.worker.Logger.Printf("could not configure maildir: %s", dir)
|
||||||
return err
|
return err
|
||||||
|
|
Loading…
Reference in a new issue