Files
crockery/internal/store/maildir.go

149 lines
2.9 KiB
Go

package store
import (
"errors"
"net/mail"
"os"
"path/filepath"
"sync"
"github.com/luksen/maildir"
"ur.gs/crockery/internal/imap/uidlist"
)
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 Maildir struct {
Account Account
Name string
directory string
filesystem maildir.Dir
uids *uidlist.List
}
func (m *Maildir) Rel() string {
return filepath.Join(m.Account.Username, m.Name)
}
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 err
}
if m.uids == nil {
uidsLock.Lock()
defer uidsLock.Unlock()
if uidsCache[m.Rel()] == nil {
uids, err := uidlist.Create(m.directory)
if err != nil {
// FIXME: this leaves a dangling Maildir
return err
}
uidsCache[m.Rel()] = uids
}
m.uids = uidsCache[m.Rel()]
}
return nil
}
// Ugh
// FIXME: multiple sessions get different Maildir instances, so breaking any
// hope at locking (sigh)
var (
uidsLock sync.Mutex
uidsCache map[string]*uidlist.List
)
func init() {
uidsCache = make(map[string]*uidlist.List)
}
func (c *concrete) FindMaildir(account Account, name string) (Maildir, error) {
m := Maildir{
Account: account,
Name: name,
directory: c.buildMaildirPath(account, name),
}
m.filesystem = maildir.Dir(m.directory)
if _, err := os.Stat(m.Directory()); os.IsNotExist(err) {
return Maildir{}, errors.New("not found")
}
uidsLock.Lock()
defer uidsLock.Unlock()
if uidsCache[m.Rel()] == nil {
uids, err := uidlist.Open(m.directory)
if err != nil {
return Maildir{}, err
}
uidsCache[m.Rel()] = uids
}
m.uids = uidsCache[m.Rel()]
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
}