2018-03-09 03:20:22 +00:00
|
|
|
package store
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2018-06-26 03:08:51 +01:00
|
|
|
"net/mail"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
|
|
|
|
"github.com/luksen/maildir"
|
|
|
|
|
|
|
|
"ur.gs/crockery/internal/imap/uidlist"
|
2018-03-09 03:20:22 +00:00
|
|
|
)
|
|
|
|
|
2018-06-26 03:08:51 +01:00
|
|
|
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)
|
2018-03-09 03:20:22 +00:00
|
|
|
}
|
|
|
|
|
2018-06-26 03:08:51 +01:00
|
|
|
type Maildir struct {
|
|
|
|
Account Account
|
|
|
|
Name string
|
|
|
|
|
|
|
|
directory string
|
|
|
|
|
|
|
|
filesystem maildir.Dir
|
|
|
|
uids *uidlist.List
|
2018-03-09 03:20:22 +00:00
|
|
|
}
|
|
|
|
|
2018-06-26 03:08:51 +01:00
|
|
|
func (m *Maildir) Rel() string {
|
|
|
|
return filepath.Join(m.Account.Username, m.Name)
|
2018-03-09 03:20:22 +00:00
|
|
|
}
|
|
|
|
|
2018-06-26 03:08:51 +01:00
|
|
|
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()
|
2018-03-09 03:20:22 +00:00
|
|
|
if err != nil {
|
2018-06-26 03:08:51 +01:00
|
|
|
return err
|
2018-03-09 03:20:22 +00:00
|
|
|
}
|
|
|
|
|
2018-06-26 03:08:51 +01:00
|
|
|
if m.uids == nil {
|
|
|
|
uids, err := uidlist.Create(m.directory)
|
|
|
|
if err != nil {
|
|
|
|
// FIXME: this leaves a dangling Maildir
|
|
|
|
return err
|
2018-03-09 03:20:22 +00:00
|
|
|
}
|
2018-06-26 03:08:51 +01:00
|
|
|
|
|
|
|
m.uids = uids
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
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),
|
2018-03-09 03:20:22 +00:00
|
|
|
}
|
2018-06-26 03:08:51 +01:00
|
|
|
m.filesystem = maildir.Dir(m.directory)
|
2018-03-09 03:20:22 +00:00
|
|
|
|
2018-06-26 03:08:51 +01:00
|
|
|
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
|
2018-03-09 03:20:22 +00:00
|
|
|
}
|
|
|
|
|
2018-06-26 03:08:51 +01:00
|
|
|
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)
|
|
|
|
}
|
2018-03-09 03:20:22 +00:00
|
|
|
|
2018-06-26 03:08:51 +01:00
|
|
|
return out, nil
|
2018-03-09 03:20:22 +00:00
|
|
|
}
|