163 lines
3.2 KiB
Go
163 lines
3.2 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)
|
|
FindMessagesInRangeBySeqNum(m Maildir, start, end uint64) ([]Message, error)
|
|
CountMessages(m Maildir) uint64
|
|
}
|
|
|
|
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 (m *Maildir) UIDValidity() uint64 {
|
|
return m.uids.UIDValidity()
|
|
}
|
|
|
|
func (m *Maildir) NextUID() uint64 {
|
|
return m.uids.NextUID()
|
|
}
|
|
|
|
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) FindMessagesInRangeBySeqNum(m Maildir, start, end uint64) ([]Message, error) {
|
|
var out []Message
|
|
entries := m.uids.EntriesInRangeBySeqNum(start, end)
|
|
|
|
for _, entry := range entries {
|
|
msg := Message{
|
|
UID: entry.UID,
|
|
SeqNum: entry.SeqNum,
|
|
Maildir: m,
|
|
Key: entry.Key(),
|
|
}
|
|
|
|
out = append(out, msg)
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|
|
func (c *concrete) CountMessages(m Maildir) uint64 {
|
|
return m.uids.Count()
|
|
}
|