func generatePrefix(date time.Time) string {
	return date.Format("20060102T150405")
}
generatePrefix converts a Time object into the ISO style format we use
as a prefix for message files.  Note:  It is used directly by unit
tests.
package file
import (
	"bufio"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sync"
	"time"
	"github.com/inbucket/inbucket/pkg/config"
	"github.com/inbucket/inbucket/pkg/storage"
	"github.com/inbucket/inbucket/pkg/stringutil"
	"github.com/rs/zerolog/log"
)
const indexFileName = "index.gob"
var (
	countChannel = make(chan int, 10)
)
func init() {
	go countGenerator(countChannel)
}
func countGenerator(c chan int) {
	for i := 0; true; i = (i + 1) % 10000 {
		c <- i
	}
}
type Store struct {
	hashLock      storage.HashLock
	path          string
	mailPath      string
	messageCap    int
	bufReaderPool sync.Pool
}
func New(cfg config.Storage) (storage.Store, error) {
	path := cfg.Params["path"]
	if path == "" {
		return nil, fmt.Errorf("'path' parameter not specified")
	}
	mailPath := filepath.Join(path, "mail")
	if _, err := os.Stat(mailPath); err != nil {
		if err = os.MkdirAll(mailPath, 0770); err != nil {
			log.Error().Str("module", "storage").Str("path", mailPath).Err(err).
				Msg("Error creating dir")
		}
	}
	return &Store{
		path:       path,
		mailPath:   mailPath,
		messageCap: cfg.MailboxMsgCap,
		bufReaderPool: sync.Pool{
			New: func() interface{} {
				return bufio.NewReader(nil)
			},
		},
	}, nil
}
func (fs *Store) AddMessage(m storage.Message) (id string, err error) {
	mb := fs.mbox(m.Mailbox())
	mb.Lock()
	defer mb.Unlock()
	r, err := m.Source()
	if err != nil {
		return "", err
	}
	fm, err := mb.newMessage()
	if err != nil {
		return "", err
	}
	if err := mb.createDir(); err != nil {
		return "", err
	}
	file, err := os.Create(fm.rawPath())
	if err != nil {
		return "", err
	}
	w := bufio.NewWriter(file)
	size, err := io.Copy(w, r)
	if err != nil {
		_ = file.Close()
		_ = os.Remove(fm.rawPath())
		return "", err
	}
	_ = r.Close()
	if err := w.Flush(); err != nil {
		_ = file.Close()
		_ = os.Remove(fm.rawPath())
		return "", err
	}
	if err := file.Close(); err != nil {
		_ = os.Remove(fm.rawPath())
		return "", err
	}
	fm.Fdate = m.Date()
	fm.Ffrom = m.From()
	fm.Fto = m.To()
	fm.Fsize = size
	fm.Fsubject = m.Subject()
	mb.messages = append(mb.messages, fm)
	if err := mb.writeIndex(); err != nil {
		_ = os.Remove(fm.rawPath())
		return "", err
	}
	return fm.Fid, nil
}
func (fs *Store) GetMessage(mailbox, id string) (storage.Message, error) {
	mb := fs.mbox(mailbox)
	mb.RLock()
	defer mb.RUnlock()
	return mb.getMessage(id)
}
func (fs *Store) GetMessages(mailbox string) ([]storage.Message, error) {
	mb := fs.mbox(mailbox)
	mb.RLock()
	defer mb.RUnlock()
	return mb.getMessages()
}
func (fs *Store) MarkSeen(mailbox, id string) error {
	mb := fs.mbox(mailbox)
	mb.Lock()
	defer mb.Unlock()
	if !mb.indexLoaded {
		if err := mb.readIndex(); err != nil {
			return err
		}
	}
	for _, m := range mb.messages {
		if m.Fid == id {
			if m.Fseen {
				return nil
			}
			m.Fseen = true
			break
		}
	}
	return mb.writeIndex()
}
func (fs *Store) RemoveMessage(mailbox, id string) error {
	mb := fs.mbox(mailbox)
	mb.Lock()
	defer mb.Unlock()
	return mb.removeMessage(id)
}
func (fs *Store) PurgeMessages(mailbox string) error {
	mb := fs.mbox(mailbox)
	mb.Lock()
	defer mb.Unlock()
	return mb.purge()
}
func (fs *Store) VisitMailboxes(f func([]storage.Message) (cont bool)) error {
	names1, err := readDirNames(fs.mailPath)
	if err != nil {
		return err
	}
	for _, name1 := range names1 {
		names2, err := readDirNames(fs.mailPath, name1)
		if err != nil {
			return err
		}
		for _, name2 := range names2 {
			names3, err := readDirNames(fs.mailPath, name1, name2)
			if err != nil {
				return err
			}
			for _, name3 := range names3 {
				mb := fs.mboxFromHash(name3)
				mb.RLock()
				msgs, err := mb.getMessages()
				mb.RUnlock()
				if err != nil {
					return err
				}
				if !f(msgs) {
					return nil
				}
			}
		}
	}
	return nil
}
func (fs *Store) mbox(mailbox string) *mbox {
	hash := stringutil.HashMailboxName(mailbox)
	s1 := hash[0:3]
	s2 := hash[0:6]
	path := filepath.Join(fs.mailPath, s1, s2, hash)
	indexPath := filepath.Join(path, indexFileName)
	return &mbox{
		RWMutex:   fs.hashLock.Get(hash),
		store:     fs,
		name:      mailbox,
		dirName:   hash,
		path:      path,
		indexPath: indexPath,
	}
}
func (fs *Store) mboxFromHash(hash string) *mbox {
	s1 := hash[0:3]
	s2 := hash[0:6]
	path := filepath.Join(fs.mailPath, s1, s2, hash)
	indexPath := filepath.Join(path, indexFileName)
	return &mbox{
		RWMutex:   fs.hashLock.Get(hash),
		store:     fs,
		dirName:   hash,
		path:      path,
		indexPath: indexPath,
	}
}
func (fs *Store) getPooledReader(r io.Reader) *bufio.Reader {
	br := fs.bufReaderPool.Get().(*bufio.Reader)
	br.Reset(r)
	return br
}
func (fs *Store) putPooledReader(br *bufio.Reader) {
	fs.bufReaderPool.Put(br)
}