about summary refs log tree commit diff stats
path: root/worker/maildir/container.go
diff options
context:
space:
mode:
authorBen Burwell <ben@benburwell.com>2019-07-11 09:44:51 -0400
committerDrew DeVault <sir@cmpwn.com>2019-07-12 11:09:50 -0400
commit1b8b6e218c7a70cb61c5449a204e38738b7bd945 (patch)
tree505495696c83b407b4a9f3379b1d8d1050e970fa /worker/maildir/container.go
parentd7cd35e72b81644774e5f1ab44ff8645e31aa510 (diff)
downloadaerc-1b8b6e218c7a70cb61c5449a204e38738b7bd945.tar.gz
Add maildir backend worker
Add the initial implementation of a backend for Maildir accounts. Much
of the functionality required is implemented in the go-message and
go-maildir libraries, so we use them as much as possible.

The maildir worker hooks into a new maildir:// URL scheme in the
accounts.conf file which points to a container of several maildir
directories. From there, the OpenDirectory, FetchDirectoryContents, etc
messages work on subdirectories. This is implemented as a Container
struct which handles mapping between the symbolic email folder names and
UIDs to the concrete directories and file names.
Diffstat (limited to 'worker/maildir/container.go')
-rw-r--r--worker/maildir/container.go105
1 files changed, 105 insertions, 0 deletions
diff --git a/worker/maildir/container.go b/worker/maildir/container.go
new file mode 100644
index 0000000..351afed
--- /dev/null
+++ b/worker/maildir/container.go
@@ -0,0 +1,105 @@
+package maildir
+
+import (
+	"fmt"
+	"io/ioutil"
+	"log"
+	"path/filepath"
+	"sort"
+
+	"github.com/emersion/go-maildir"
+
+	"git.sr.ht/~sircmpwn/aerc/lib/uidstore"
+)
+
+// A Container is a directory which contains other directories which adhere to
+// the Maildir spec
+type Container struct {
+	dir  string
+	log  *log.Logger
+	uids *uidstore.Store
+}
+
+// NewContainer creates a new container at the specified directory
+// TODO: return an error if the provided directory is not accessible
+func NewContainer(dir string, l *log.Logger) *Container {
+	return &Container{dir: dir, uids: uidstore.NewStore(), log: l}
+}
+
+// ListFolders returns a list of maildir folders in the container
+func (c *Container) ListFolders() ([]string, error) {
+	files, err := ioutil.ReadDir(c.dir)
+	if err != nil {
+		return nil, fmt.Errorf("error reading folders: %v", err)
+	}
+	dirnames := []string{}
+	for _, f := range files {
+		if f.IsDir() {
+			dirnames = append(dirnames, f.Name())
+		}
+	}
+	return dirnames, nil
+}
+
+// OpenDirectory opens an existing maildir in the container by name, moves new
+// messages into cur, and registers the new keys in the UIDStore.
+func (c *Container) OpenDirectory(name string) (maildir.Dir, error) {
+	dir := c.Dir(name)
+	keys, err := dir.Unseen()
+	if err != nil {
+		return dir, err
+	}
+	for _, key := range keys {
+		c.uids.GetOrInsert(key)
+	}
+	return dir, nil
+}
+
+// Dir returns a maildir.Dir with the specified name inside the container
+func (c *Container) Dir(name string) maildir.Dir {
+	return maildir.Dir(filepath.Join(c.dir, name))
+}
+
+// UIDs fetches the unique message identifiers for the maildir
+func (c *Container) UIDs(d maildir.Dir) ([]uint32, error) {
+	keys, err := d.Keys()
+	if err != nil {
+		return nil, fmt.Errorf("could not get keys for %s: %v", d, err)
+	}
+	sort.Strings(keys)
+	var uids []uint32
+	for _, key := range keys {
+		uids = append(uids, c.uids.GetOrInsert(key))
+	}
+	return uids, nil
+}
+
+// Message returns a Message struct for the given UID and maildir
+func (c *Container) Message(d maildir.Dir, uid uint32) (*Message, error) {
+	if key, ok := c.uids.GetKey(uid); ok {
+		return &Message{
+			dir: d,
+			uid: uid,
+			key: key,
+		}, nil
+	}
+	return nil, fmt.Errorf("could not find message with uid %d in maildir %s",
+		uid, d)
+}
+
+// DeleteAll deletes a set of messages by UID and returns the subset of UIDs
+// which were successfully deleted, stopping upon the first error.
+func (c *Container) DeleteAll(d maildir.Dir, uids []uint32) ([]uint32, error) {
+	var success []uint32
+	for _, uid := range uids {
+		msg, err := c.Message(d, uid)
+		if err != nil {
+			return success, err
+		}
+		if err := msg.Remove(); err != nil {
+			return success, err
+		}
+		success = append(success, uid)
+	}
+	return success, nil
+}