Convert from BoltDB to Maildir storage for emails

This commit is contained in:
2018-06-26 03:08:51 +01:00
parent d25ed6c1bd
commit a3c2508160
19 changed files with 869 additions and 175 deletions

View File

@@ -2,42 +2,123 @@ package store
import (
"errors"
"net/mail"
"os"
"path/filepath"
"github.com/luksen/maildir"
"ur.gs/crockery/internal/imap/uidlist"
)
type MailboxInterface interface {
CreateMailbox(*Mailbox) error
FindMailbox(account Account, name string) (Mailbox, error)
FindMailboxes(account Account) ([]Mailbox, error)
type MaildirInterface interface {
CreateMaildir(*Maildir) error
FindMaildir(account Account, name string) (Maildir, error)
FindMaildirs(account Account) ([]Maildir, error)
FindMessagesInRange(m Maildir, start, end uint64) ([]Message, error)
}
type Mailbox struct {
ID uint64 `storm:"id,increment"`
Account string `storm:"index"` // FK accounts.username
Name string `storm:"index"`
type Maildir struct {
Account Account
Name string
directory string
filesystem maildir.Dir
uids *uidlist.List
}
func (c *concrete) CreateMailbox(mailbox *Mailbox) error {
return c.storm.Save(mailbox)
func (m *Maildir) Rel() string {
return filepath.Join(m.Account.Username, m.Name)
}
func (c *concrete) FindMailbox(account Account, name string) (Mailbox, error) {
// FIXME I don't know how to storm
candidates, err := c.FindMailboxes(account)
func (m *Maildir) Directory() string {
return m.directory
}
func (m *Maildir) Header(key string) (mail.Header, error) {
return m.filesystem.Header(key)
}
func (c *concrete) CreateMaildir(m *Maildir) error {
if m.directory == "" {
m.directory = c.buildMaildirPath(m.Account, m.Name)
}
if string(m.filesystem) == "" {
m.filesystem = maildir.Dir(m.directory)
}
err := m.filesystem.Create()
if err != nil {
return Mailbox{}, err
return err
}
for _, mbox := range candidates {
if mbox.Name == name {
return mbox, nil
if m.uids == nil {
uids, err := uidlist.Create(m.directory)
if err != nil {
// FIXME: this leaves a dangling Maildir
return err
}
m.uids = uids
}
return Mailbox{}, errors.New("not found")
return nil
}
func (c *concrete) FindMailboxes(account Account) ([]Mailbox, error) {
var out []Mailbox
func (c *concrete) FindMaildir(account Account, name string) (Maildir, error) {
// FIXME: multiple IMAP sessions get different Maildir instances, so breaking
// any hope at locking (sigh)
m := Maildir{
Account: account,
Name: name,
directory: c.buildMaildirPath(account, name),
}
m.filesystem = maildir.Dir(m.directory)
return out, c.storm.Find("Account", account.Username, &out)
if _, err := os.Stat(m.Directory()); os.IsNotExist(err) {
return Maildir{}, errors.New("not found")
}
uids, err := uidlist.Open(m.directory)
if err != nil {
return Maildir{}, err
}
m.uids = uids
return m, nil
}
func (c *concrete) FindMaildirs(account Account) ([]Maildir, error) {
// TODO: iterate through the maildir looking for submaildirs
defaultMaildir, err := c.FindMaildir(account, "")
if err != nil {
return nil, err
}
return []Maildir{defaultMaildir}, nil
}
func (c *concrete) buildMaildirPath(account Account, parts ...string) string {
bits := append([]string{BuildMailboxesPath(c.home), account.Username}, parts...)
return filepath.Join(bits...)
}
func (c *concrete) FindMessagesInRange(m Maildir, start, end uint64) ([]Message, error) {
var out []Message
entries := m.uids.EntriesInRange(start, end)
for _, entry := range entries {
msg := Message{
UID: entry.UID,
Maildir: m,
Key: entry.Key(),
}
out = append(out, msg)
}
return out, nil
}