diff --git a/cmd/inbucket/main.go b/cmd/inbucket/main.go index 0886a71..6e25e44 100644 --- a/cmd/inbucket/main.go +++ b/cmd/inbucket/main.go @@ -116,7 +116,7 @@ func main() { msgHub := msghub.New(rootCtx, config.GetWebConfig().MonitorHistory) // Grab our datastore - ds := filestore.DefaultFileDataStore() + ds := file.DefaultStore() // Start HTTP server web.Initialize(config.GetWebConfig(), shutdownChan, ds, msgHub) diff --git a/pkg/rest/apiv1_controller.go b/pkg/rest/apiv1_controller.go index abd9b0b..010bfac 100644 --- a/pkg/rest/apiv1_controller.go +++ b/pkg/rest/apiv1_controller.go @@ -65,7 +65,7 @@ func MailboxShowV1(w http.ResponseWriter, req *http.Request, ctx *web.Context) ( return fmt.Errorf("Failed to get mailbox for %q: %v", name, err) } msg, err := mb.GetMessage(id) - if err == datastore.ErrNotExist { + if err == storage.ErrNotExist { http.NotFound(w, req) return nil } @@ -150,7 +150,7 @@ func MailboxSourceV1(w http.ResponseWriter, req *http.Request, ctx *web.Context) return fmt.Errorf("Failed to get mailbox for %q: %v", name, err) } message, err := mb.GetMessage(id) - if err == datastore.ErrNotExist { + if err == storage.ErrNotExist { http.NotFound(w, req) return nil } @@ -184,7 +184,7 @@ func MailboxDeleteV1(w http.ResponseWriter, req *http.Request, ctx *web.Context) return fmt.Errorf("Failed to get mailbox for %q: %v", name, err) } message, err := mb.GetMessage(id) - if err == datastore.ErrNotExist { + if err == storage.ErrNotExist { http.NotFound(w, req) return nil } diff --git a/pkg/rest/apiv1_controller_test.go b/pkg/rest/apiv1_controller_test.go index 7f5aba7..9c4e789 100644 --- a/pkg/rest/apiv1_controller_test.go +++ b/pkg/rest/apiv1_controller_test.go @@ -31,7 +31,7 @@ const ( func TestRestMailboxList(t *testing.T) { // Setup - ds := &datastore.MockDataStore{} + ds := &storage.MockDataStore{} logbuf := setupWebServer(ds) // Test invalid mailbox name @@ -45,9 +45,9 @@ func TestRestMailboxList(t *testing.T) { } // Test empty mailbox - emptybox := &datastore.MockMailbox{} + emptybox := &storage.MockMailbox{} ds.On("MailboxFor", "empty").Return(emptybox, nil) - emptybox.On("GetMessages").Return([]datastore.Message{}, nil) + emptybox.On("GetMessages").Return([]storage.Message{}, nil) w, err = testRestGet(baseURL + "/mailbox/empty") expectCode = 200 @@ -59,7 +59,7 @@ func TestRestMailboxList(t *testing.T) { } // Test MailboxFor error - ds.On("MailboxFor", "error").Return(&datastore.MockMailbox{}, fmt.Errorf("Internal error")) + ds.On("MailboxFor", "error").Return(&storage.MockMailbox{}, fmt.Errorf("Internal error")) w, err = testRestGet(baseURL + "/mailbox/error") expectCode = 500 if err != nil { @@ -77,9 +77,9 @@ func TestRestMailboxList(t *testing.T) { } // Test MailboxFor error - error2box := &datastore.MockMailbox{} + error2box := &storage.MockMailbox{} ds.On("MailboxFor", "error2").Return(error2box, nil) - error2box.On("GetMessages").Return([]datastore.Message{}, fmt.Errorf("Internal error 2")) + error2box.On("GetMessages").Return([]storage.Message{}, fmt.Errorf("Internal error 2")) w, err = testRestGet(baseURL + "/mailbox/error2") expectCode = 500 @@ -107,11 +107,11 @@ func TestRestMailboxList(t *testing.T) { Subject: "subject 2", Date: time.Date(2012, 7, 1, 10, 11, 12, 253, time.FixedZone("PDT", -700)), } - goodbox := &datastore.MockMailbox{} + goodbox := &storage.MockMailbox{} ds.On("MailboxFor", "good").Return(goodbox, nil) msg1 := data1.MockMessage() msg2 := data2.MockMessage() - goodbox.On("GetMessages").Return([]datastore.Message{msg1, msg2}, nil) + goodbox.On("GetMessages").Return([]storage.Message{msg1, msg2}, nil) // Check return code w, err = testRestGet(baseURL + "/mailbox/good") @@ -155,7 +155,7 @@ func TestRestMailboxList(t *testing.T) { func TestRestMessage(t *testing.T) { // Setup - ds := &datastore.MockDataStore{} + ds := &storage.MockDataStore{} logbuf := setupWebServer(ds) // Test invalid mailbox name @@ -169,9 +169,9 @@ func TestRestMessage(t *testing.T) { } // Test requesting a message that does not exist - emptybox := &datastore.MockMailbox{} + emptybox := &storage.MockMailbox{} ds.On("MailboxFor", "empty").Return(emptybox, nil) - emptybox.On("GetMessage", "0001").Return(&datastore.MockMessage{}, datastore.ErrNotExist) + emptybox.On("GetMessage", "0001").Return(&storage.MockMessage{}, storage.ErrNotExist) w, err = testRestGet(baseURL + "/mailbox/empty/0001") expectCode = 404 @@ -183,7 +183,7 @@ func TestRestMessage(t *testing.T) { } // Test MailboxFor error - ds.On("MailboxFor", "error").Return(&datastore.MockMailbox{}, fmt.Errorf("Internal error")) + ds.On("MailboxFor", "error").Return(&storage.MockMailbox{}, fmt.Errorf("Internal error")) w, err = testRestGet(baseURL + "/mailbox/error/0001") expectCode = 500 if err != nil { @@ -201,9 +201,9 @@ func TestRestMessage(t *testing.T) { } // Test GetMessage error - error2box := &datastore.MockMailbox{} + error2box := &storage.MockMailbox{} ds.On("MailboxFor", "error2").Return(error2box, nil) - error2box.On("GetMessage", "0001").Return(&datastore.MockMessage{}, fmt.Errorf("Internal error 2")) + error2box.On("GetMessage", "0001").Return(&storage.MockMessage{}, fmt.Errorf("Internal error 2")) w, err = testRestGet(baseURL + "/mailbox/error2/0001") expectCode = 500 @@ -228,7 +228,7 @@ func TestRestMessage(t *testing.T) { Text: "This is some text", HTML: "This is some HTML", } - goodbox := &datastore.MockMailbox{} + goodbox := &storage.MockMailbox{} ds.On("MailboxFor", "good").Return(goodbox, nil) msg1 := data1.MockMessage() goodbox.On("GetMessage", "0001").Return(msg1, nil) diff --git a/pkg/rest/testutils_test.go b/pkg/rest/testutils_test.go index 7955828..ef294df 100644 --- a/pkg/rest/testutils_test.go +++ b/pkg/rest/testutils_test.go @@ -25,8 +25,8 @@ type InputMessageData struct { HTML, Text string } -func (d *InputMessageData) MockMessage() *datastore.MockMessage { - msg := &datastore.MockMessage{} +func (d *InputMessageData) MockMessage() *storage.MockMessage { + msg := &storage.MockMessage{} msg.On("ID").Return(d.ID) msg.On("From").Return(d.From) msg.On("To").Return(d.To) @@ -188,7 +188,7 @@ func testRestGet(url string) (*httptest.ResponseRecorder, error) { return w, nil } -func setupWebServer(ds datastore.DataStore) *bytes.Buffer { +func setupWebServer(ds storage.Store) *bytes.Buffer { // Capture log output buf := new(bytes.Buffer) log.SetOutput(buf) diff --git a/pkg/server/pop3/handler.go b/pkg/server/pop3/handler.go index 7922ca2..98cce65 100644 --- a/pkg/server/pop3/handler.go +++ b/pkg/server/pop3/handler.go @@ -57,18 +57,18 @@ var commands = map[string]bool{ // Session defines an active POP3 session type Session struct { - server *Server // Reference to the server we belong to - id int // Session ID number - conn net.Conn // Our network connection - remoteHost string // IP address of client - sendError error // Used to bail out of read loop on send error - state State // Current session state - reader *bufio.Reader // Buffered reader for our net conn - user string // Mailbox name - mailbox datastore.Mailbox // Mailbox instance - messages []datastore.Message // Slice of messages in mailbox - retain []bool // Messages to retain upon UPDATE (true=retain) - msgCount int // Number of undeleted messages + server *Server // Reference to the server we belong to + id int // Session ID number + conn net.Conn // Our network connection + remoteHost string // IP address of client + sendError error // Used to bail out of read loop on send error + state State // Current session state + reader *bufio.Reader // Buffered reader for our net conn + user string // Mailbox name + mailbox storage.Mailbox // Mailbox instance + messages []storage.Message // Slice of messages in mailbox + retain []bool // Messages to retain upon UPDATE (true=retain) + msgCount int // Number of undeleted messages } // NewSession creates a new POP3 session @@ -432,7 +432,7 @@ func (ses *Session) transactionHandler(cmd string, args []string) { } // Send the contents of the message to the client -func (ses *Session) sendMessage(msg datastore.Message) { +func (ses *Session) sendMessage(msg storage.Message) { reader, err := msg.RawReader() if err != nil { ses.logError("Failed to read message for RETR command") @@ -465,7 +465,7 @@ func (ses *Session) sendMessage(msg datastore.Message) { } // Send the headers plus the top N lines to the client -func (ses *Session) sendMessageTop(msg datastore.Message, lineCount int) { +func (ses *Session) sendMessageTop(msg storage.Message, lineCount int) { reader, err := msg.RawReader() if err != nil { ses.logError("Failed to read message for RETR command") diff --git a/pkg/server/pop3/listener.go b/pkg/server/pop3/listener.go index 42aaa56..c971854 100644 --- a/pkg/server/pop3/listener.go +++ b/pkg/server/pop3/listener.go @@ -17,14 +17,14 @@ type Server struct { host string domain string maxIdleSeconds int - dataStore datastore.DataStore + dataStore storage.Store listener net.Listener globalShutdown chan bool waitgroup *sync.WaitGroup } // New creates a new Server struct -func New(cfg config.POP3Config, shutdownChan chan bool, ds datastore.DataStore) *Server { +func New(cfg config.POP3Config, shutdownChan chan bool, ds storage.Store) *Server { return &Server{ host: fmt.Sprintf("%v:%v", cfg.IP4address, cfg.IP4port), domain: cfg.Domain, diff --git a/pkg/server/smtp/handler.go b/pkg/server/smtp/handler.go index 96646b1..a678d3a 100644 --- a/pkg/server/smtp/handler.go +++ b/pkg/server/smtp/handler.go @@ -73,7 +73,7 @@ var commands = map[string]bool{ // recipientDetails for message delivery type recipientDetails struct { address, localPart, domainPart string - mailbox datastore.Mailbox + mailbox storage.Mailbox } // Session holds the state of an SMTP session diff --git a/pkg/server/smtp/handler_test.go b/pkg/server/smtp/handler_test.go index c1e6168..b53b651 100644 --- a/pkg/server/smtp/handler_test.go +++ b/pkg/server/smtp/handler_test.go @@ -26,7 +26,7 @@ type scriptStep struct { // Test commands in GREET state func TestGreetState(t *testing.T) { // Setup mock objects - mds := &datastore.MockDataStore{} + mds := &storage.MockDataStore{} server, logbuf, teardown := setupSMTPServer(mds) defer teardown() @@ -83,7 +83,7 @@ func TestGreetState(t *testing.T) { // Test commands in READY state func TestReadyState(t *testing.T) { // Setup mock objects - mds := &datastore.MockDataStore{} + mds := &storage.MockDataStore{} server, logbuf, teardown := setupSMTPServer(mds) defer teardown() @@ -144,9 +144,9 @@ func TestReadyState(t *testing.T) { // Test commands in MAIL state func TestMailState(t *testing.T) { // Setup mock objects - mds := &datastore.MockDataStore{} - mb1 := &datastore.MockMailbox{} - msg1 := &datastore.MockMessage{} + mds := &storage.MockDataStore{} + mb1 := &storage.MockMailbox{} + msg1 := &storage.MockMessage{} mds.On("MailboxFor", "u1").Return(mb1, nil) mb1.On("NewMessage").Return(msg1, nil) mb1.On("Name").Return("u1") @@ -259,9 +259,9 @@ func TestMailState(t *testing.T) { // Test commands in DATA state func TestDataState(t *testing.T) { // Setup mock objects - mds := &datastore.MockDataStore{} - mb1 := &datastore.MockMailbox{} - msg1 := &datastore.MockMessage{} + mds := &storage.MockDataStore{} + mb1 := &storage.MockMailbox{} + msg1 := &storage.MockMessage{} mds.On("MailboxFor", "u1").Return(mb1, nil) mb1.On("NewMessage").Return(msg1, nil) mb1.On("Name").Return("u1") @@ -367,7 +367,7 @@ func (m *mockConn) SetDeadline(t time.Time) error { return nil } func (m *mockConn) SetReadDeadline(t time.Time) error { return nil } func (m *mockConn) SetWriteDeadline(t time.Time) error { return nil } -func setupSMTPServer(ds datastore.DataStore) (s *Server, buf *bytes.Buffer, teardown func()) { +func setupSMTPServer(ds storage.Store) (s *Server, buf *bytes.Buffer, teardown func()) { // Test Server Config cfg := config.SMTPConfig{ IP4address: net.IPv4(127, 0, 0, 1), diff --git a/pkg/server/smtp/listener.go b/pkg/server/smtp/listener.go index 986d112..4586174 100644 --- a/pkg/server/smtp/listener.go +++ b/pkg/server/smtp/listener.go @@ -48,10 +48,10 @@ type Server struct { storeMessages bool // Dependencies - dataStore datastore.DataStore // Mailbox/message store - globalShutdown chan bool // Shuts down Inbucket - msgHub *msghub.Hub // Pub/sub for message info - retentionScanner *datastore.RetentionScanner // Deletes expired messages + dataStore storage.Store // Mailbox/message store + globalShutdown chan bool // Shuts down Inbucket + msgHub *msghub.Hub // Pub/sub for message info + retentionScanner *storage.RetentionScanner // Deletes expired messages // State listener net.Listener // Incoming network connections @@ -83,7 +83,7 @@ var ( func NewServer( cfg config.SMTPConfig, globalShutdown chan bool, - ds datastore.DataStore, + ds storage.Store, msgHub *msghub.Hub) *Server { return &Server{ host: fmt.Sprintf("%v:%v", cfg.IP4address, cfg.IP4port), @@ -96,7 +96,7 @@ func NewServer( globalShutdown: globalShutdown, dataStore: ds, msgHub: msgHub, - retentionScanner: datastore.NewRetentionScanner(ds, globalShutdown), + retentionScanner: storage.NewRetentionScanner(ds, globalShutdown), waitgroup: new(sync.WaitGroup), } } diff --git a/pkg/server/web/context.go b/pkg/server/web/context.go index 01d6c85..422fdb5 100644 --- a/pkg/server/web/context.go +++ b/pkg/server/web/context.go @@ -15,7 +15,7 @@ import ( type Context struct { Vars map[string]string Session *sessions.Session - DataStore datastore.DataStore + DataStore storage.Store MsgHub *msghub.Hub WebConfig config.WebConfig IsJSON bool diff --git a/pkg/server/web/server.go b/pkg/server/web/server.go index a9cd22a..5c82430 100644 --- a/pkg/server/web/server.go +++ b/pkg/server/web/server.go @@ -23,7 +23,7 @@ type Handler func(http.ResponseWriter, *http.Request, *Context) error var ( // DataStore is where all the mailboxes and messages live - DataStore datastore.DataStore + DataStore storage.Store // msgHub holds a reference to the message pub/sub system msgHub *msghub.Hub @@ -51,7 +51,7 @@ func init() { func Initialize( cfg config.WebConfig, shutdownChan chan bool, - ds datastore.DataStore, + ds storage.Store, mh *msghub.Hub) { webConfig = cfg diff --git a/pkg/storage/file/fmessage.go b/pkg/storage/file/fmessage.go index 22de241..4e496f8 100644 --- a/pkg/storage/file/fmessage.go +++ b/pkg/storage/file/fmessage.go @@ -1,4 +1,4 @@ -package filestore +package file import ( "bufio" @@ -15,10 +15,10 @@ import ( "github.com/jhillyerd/inbucket/pkg/storage" ) -// FileMessage implements Message and contains a little bit of data about a +// Message implements Message and contains a little bit of data about a // particular email message, and methods to retrieve the rest of it from disk. -type FileMessage struct { - mailbox *FileMailbox +type Message struct { + mailbox *Mailbox // Stored in GOB Fid string Fdate time.Time @@ -34,7 +34,7 @@ type FileMessage struct { // NewMessage creates a new FileMessage object and sets the Date and Id fields. // It will also delete messages over messageCap if configured. -func (mb *FileMailbox) NewMessage() (datastore.Message, error) { +func (mb *Mailbox) NewMessage() (storage.Message, error) { // Load index if !mb.indexLoaded { if err := mb.readIndex(); err != nil { @@ -54,50 +54,50 @@ func (mb *FileMailbox) NewMessage() (datastore.Message, error) { date := time.Now() id := generateID(date) - return &FileMessage{mailbox: mb, Fid: id, Fdate: date, writable: true}, nil + return &Message{mailbox: mb, Fid: id, Fdate: date, writable: true}, nil } // ID gets the ID of the Message -func (m *FileMessage) ID() string { +func (m *Message) ID() string { return m.Fid } // Date returns the date/time this Message was received by Inbucket -func (m *FileMessage) Date() time.Time { +func (m *Message) Date() time.Time { return m.Fdate } // From returns the value of the Message From header -func (m *FileMessage) From() string { +func (m *Message) From() string { return m.Ffrom } // To returns the value of the Message To header -func (m *FileMessage) To() []string { +func (m *Message) To() []string { return m.Fto } // Subject returns the value of the Message Subject header -func (m *FileMessage) Subject() string { +func (m *Message) Subject() string { return m.Fsubject } // String returns a string in the form: "Subject()" from From() -func (m *FileMessage) String() string { +func (m *Message) String() string { return fmt.Sprintf("\"%v\" from %v", m.Fsubject, m.Ffrom) } // Size returns the size of the Message on disk in bytes -func (m *FileMessage) Size() int64 { +func (m *Message) Size() int64 { return m.Fsize } -func (m *FileMessage) rawPath() string { +func (m *Message) rawPath() string { return filepath.Join(m.mailbox.path, m.Fid+".raw") } // ReadHeader opens the .raw portion of a Message and returns a standard Go mail.Message object -func (m *FileMessage) ReadHeader() (msg *mail.Message, err error) { +func (m *Message) ReadHeader() (msg *mail.Message, err error) { file, err := os.Open(m.rawPath()) if err != nil { return nil, err @@ -113,7 +113,7 @@ func (m *FileMessage) ReadHeader() (msg *mail.Message, err error) { } // ReadBody opens the .raw portion of a Message and returns a MIMEBody object -func (m *FileMessage) ReadBody() (body *enmime.Envelope, err error) { +func (m *Message) ReadBody() (body *enmime.Envelope, err error) { file, err := os.Open(m.rawPath()) if err != nil { return nil, err @@ -133,7 +133,7 @@ func (m *FileMessage) ReadBody() (body *enmime.Envelope, err error) { } // RawReader opens the .raw portion of a Message as an io.ReadCloser -func (m *FileMessage) RawReader() (reader io.ReadCloser, err error) { +func (m *Message) RawReader() (reader io.ReadCloser, err error) { file, err := os.Open(m.rawPath()) if err != nil { return nil, err @@ -142,7 +142,7 @@ func (m *FileMessage) RawReader() (reader io.ReadCloser, err error) { } // ReadRaw opens the .raw portion of a Message and returns it as a string -func (m *FileMessage) ReadRaw() (raw *string, err error) { +func (m *Message) ReadRaw() (raw *string, err error) { reader, err := m.RawReader() if err != nil { return nil, err @@ -163,10 +163,10 @@ func (m *FileMessage) ReadRaw() (raw *string, err error) { // Append data to a newly opened Message, this will fail on a pre-existing Message and // after Close() is called. -func (m *FileMessage) Append(data []byte) error { +func (m *Message) Append(data []byte) error { // Prevent Appending to a pre-existing Message if !m.writable { - return datastore.ErrNotWritable + return storage.ErrNotWritable } // Open file for writing if we haven't yet if m.writer == nil { @@ -190,7 +190,7 @@ func (m *FileMessage) Append(data []byte) error { // Close this Message for writing - no more data may be Appended. Close() will also // trigger the creation of the .gob file. -func (m *FileMessage) Close() error { +func (m *Message) Close() error { // nil out the writer fields so they can't be used writer := m.writer writerFile := m.writerFile @@ -245,7 +245,7 @@ func (m *FileMessage) Close() error { // Delete this Message from disk by removing it from the index and deleting the // raw files. -func (m *FileMessage) Delete() error { +func (m *Message) Delete() error { messages := m.mailbox.messages for i, mm := range messages { if m == mm { diff --git a/pkg/storage/file/fstore.go b/pkg/storage/file/fstore.go index 7d7dd35..5588cbb 100644 --- a/pkg/storage/file/fstore.go +++ b/pkg/storage/file/fstore.go @@ -1,4 +1,4 @@ -package filestore +package file import ( "bufio" @@ -48,17 +48,17 @@ func countGenerator(c chan int) { } } -// FileDataStore implements DataStore aand is the root of the mail storage +// Store implements DataStore aand is the root of the mail storage // hiearchy. It provides access to Mailbox objects -type FileDataStore struct { - hashLock datastore.HashLock +type Store struct { + hashLock storage.HashLock path string mailPath string messageCap int } -// NewFileDataStore creates a new DataStore object using the specified path -func NewFileDataStore(cfg config.DataStoreConfig) datastore.DataStore { +// New creates a new DataStore object using the specified path +func New(cfg config.DataStoreConfig) storage.Store { path := cfg.Path if path == "" { log.Errorf("No value configured for datastore path") @@ -71,19 +71,19 @@ func NewFileDataStore(cfg config.DataStoreConfig) datastore.DataStore { log.Errorf("Error creating dir %q: %v", mailPath, err) } } - return &FileDataStore{path: path, mailPath: mailPath, messageCap: cfg.MailboxMsgCap} + return &Store{path: path, mailPath: mailPath, messageCap: cfg.MailboxMsgCap} } -// DefaultFileDataStore creates a new DataStore object. It uses the inbucket.Config object to +// DefaultStore creates a new DataStore object. It uses the inbucket.Config object to // construct it's path. -func DefaultFileDataStore() datastore.DataStore { +func DefaultStore() storage.Store { cfg := config.GetDataStoreConfig() - return NewFileDataStore(cfg) + return New(cfg) } // MailboxFor retrieves the Mailbox object for a specified email address, if the mailbox // does not exist, it will attempt to create it. -func (ds *FileDataStore) MailboxFor(emailAddress string) (datastore.Mailbox, error) { +func (ds *Store) MailboxFor(emailAddress string) (storage.Mailbox, error) { name, err := stringutil.ParseMailboxName(emailAddress) if err != nil { return nil, err @@ -94,13 +94,13 @@ func (ds *FileDataStore) MailboxFor(emailAddress string) (datastore.Mailbox, err path := filepath.Join(ds.mailPath, s1, s2, dir) indexPath := filepath.Join(path, indexFileName) - return &FileMailbox{store: ds, name: name, dirName: dir, path: path, + return &Mailbox{store: ds, name: name, dirName: dir, path: path, indexPath: indexPath}, nil } // AllMailboxes returns a slice with all Mailboxes -func (ds *FileDataStore) AllMailboxes() ([]datastore.Mailbox, error) { - mailboxes := make([]datastore.Mailbox, 0, 100) +func (ds *Store) AllMailboxes() ([]storage.Mailbox, error) { + mailboxes := make([]storage.Mailbox, 0, 100) infos1, err := ioutil.ReadDir(ds.mailPath) if err != nil { return nil, err @@ -127,7 +127,7 @@ func (ds *FileDataStore) AllMailboxes() ([]datastore.Mailbox, error) { mbdir := inf3.Name() mbpath := filepath.Join(ds.mailPath, l1, l2, mbdir) idx := filepath.Join(mbpath, indexFileName) - mb := &FileMailbox{store: ds, dirName: mbdir, path: mbpath, + mb := &Mailbox{store: ds, dirName: mbdir, path: mbpath, indexPath: idx} mailboxes = append(mailboxes, mb) } @@ -141,7 +141,7 @@ func (ds *FileDataStore) AllMailboxes() ([]datastore.Mailbox, error) { } // LockFor returns the RWMutex for this mailbox, or an error. -func (ds *FileDataStore) LockFor(emailAddress string) (*sync.RWMutex, error) { +func (ds *Store) LockFor(emailAddress string) (*sync.RWMutex, error) { name, err := stringutil.ParseMailboxName(emailAddress) if err != nil { return nil, err @@ -150,38 +150,38 @@ func (ds *FileDataStore) LockFor(emailAddress string) (*sync.RWMutex, error) { return ds.hashLock.Get(hash), nil } -// FileMailbox implements Mailbox, manages the mail for a specific user and +// Mailbox implements Mailbox, manages the mail for a specific user and // correlates to a particular directory on disk. -type FileMailbox struct { - store *FileDataStore +type Mailbox struct { + store *Store name string dirName string path string indexLoaded bool indexPath string - messages []*FileMessage + messages []*Message } // Name of the mailbox -func (mb *FileMailbox) Name() string { +func (mb *Mailbox) Name() string { return mb.name } // String renders the name and directory path of the mailbox -func (mb *FileMailbox) String() string { +func (mb *Mailbox) String() string { return mb.name + "[" + mb.dirName + "]" } // GetMessages scans the mailbox directory for .gob files and decodes them into // a slice of Message objects. -func (mb *FileMailbox) GetMessages() ([]datastore.Message, error) { +func (mb *Mailbox) GetMessages() ([]storage.Message, error) { if !mb.indexLoaded { if err := mb.readIndex(); err != nil { return nil, err } } - messages := make([]datastore.Message, len(mb.messages)) + messages := make([]storage.Message, len(mb.messages)) for i, m := range mb.messages { messages[i] = m } @@ -189,7 +189,7 @@ func (mb *FileMailbox) GetMessages() ([]datastore.Message, error) { } // GetMessage decodes a single message by Id and returns a Message object -func (mb *FileMailbox) GetMessage(id string) (datastore.Message, error) { +func (mb *Mailbox) GetMessage(id string) (storage.Message, error) { if !mb.indexLoaded { if err := mb.readIndex(); err != nil { return nil, err @@ -206,17 +206,17 @@ func (mb *FileMailbox) GetMessage(id string) (datastore.Message, error) { } } - return nil, datastore.ErrNotExist + return nil, storage.ErrNotExist } // Purge deletes all messages in this mailbox -func (mb *FileMailbox) Purge() error { +func (mb *Mailbox) Purge() error { mb.messages = mb.messages[:0] return mb.writeIndex() } // readIndex loads the mailbox index data from disk -func (mb *FileMailbox) readIndex() error { +func (mb *Mailbox) readIndex() error { // Clear message slice, open index mb.messages = mb.messages[:0] // Lock for reading @@ -242,7 +242,7 @@ func (mb *FileMailbox) readIndex() error { // Decode gob data dec := gob.NewDecoder(bufio.NewReader(file)) for { - msg := new(FileMessage) + msg := new(Message) if err = dec.Decode(msg); err != nil { if err == io.EOF { // It's OK to get an EOF here @@ -259,7 +259,7 @@ func (mb *FileMailbox) readIndex() error { } // writeIndex overwrites the index on disk with the current mailbox data -func (mb *FileMailbox) writeIndex() error { +func (mb *Mailbox) writeIndex() error { // Lock for writing indexMx.Lock() defer indexMx.Unlock() @@ -301,7 +301,7 @@ func (mb *FileMailbox) writeIndex() error { } // createDir checks for the presence of the path for this mailbox, creates it if needed -func (mb *FileMailbox) createDir() error { +func (mb *Mailbox) createDir() error { dirMx.Lock() defer dirMx.Unlock() if _, err := os.Stat(mb.path); err != nil { @@ -314,7 +314,7 @@ func (mb *FileMailbox) createDir() error { } // removeDir removes the mailbox, plus empty higher level directories -func (mb *FileMailbox) removeDir() error { +func (mb *Mailbox) removeDir() error { dirMx.Lock() defer dirMx.Unlock() // remove mailbox dir, including index file diff --git a/pkg/storage/file/fstore_test.go b/pkg/storage/file/fstore_test.go index b9fd41c..b02bdd1 100644 --- a/pkg/storage/file/fstore_test.go +++ b/pkg/storage/file/fstore_test.go @@ -1,4 +1,4 @@ -package filestore +package file import ( "bytes" @@ -359,7 +359,7 @@ func TestFSMissing(t *testing.T) { // Delete a message file without removing it from index msg, err := mb.GetMessage(sentIds[1]) assert.Nil(t, err) - fmsg := msg.(*FileMessage) + fmsg := msg.(*Message) _ = os.Remove(fmsg.rawPath()) msg, err = mb.GetMessage(sentIds[1]) assert.Nil(t, err) @@ -508,7 +508,7 @@ func TestGetLatestMessage(t *testing.T) { } // setupDataStore creates a new FileDataStore in a temporary directory -func setupDataStore(cfg config.DataStoreConfig) (*FileDataStore, *bytes.Buffer) { +func setupDataStore(cfg config.DataStoreConfig) (*Store, *bytes.Buffer) { path, err := ioutil.TempDir("", "inbucket") if err != nil { panic(err) @@ -519,12 +519,12 @@ func setupDataStore(cfg config.DataStoreConfig) (*FileDataStore, *bytes.Buffer) log.SetOutput(buf) cfg.Path = path - return NewFileDataStore(cfg).(*FileDataStore), buf + return New(cfg).(*Store), buf } // deliverMessage creates and delivers a message to the specific mailbox, returning // the size of the generated message. -func deliverMessage(ds *FileDataStore, mbName string, subject string, +func deliverMessage(ds *Store, mbName string, subject string, date time.Time) (id string, size int64) { // Build fake SMTP message for delivery testMsg := make([]byte, 0, 300) @@ -544,7 +544,7 @@ func deliverMessage(ds *FileDataStore, mbName string, subject string, if err != nil { panic(err) } - fmsg := msg.(*FileMessage) + fmsg := msg.(*Message) fmsg.Fdate = date fmsg.Fid = id if err = msg.Append(testMsg); err != nil { @@ -557,7 +557,7 @@ func deliverMessage(ds *FileDataStore, mbName string, subject string, return id, int64(len(testMsg)) } -func teardownDataStore(ds *FileDataStore) { +func teardownDataStore(ds *Store) { if err := os.RemoveAll(ds.path); err != nil { panic(err) } diff --git a/pkg/storage/lock.go b/pkg/storage/lock.go index 5247702..8612e80 100644 --- a/pkg/storage/lock.go +++ b/pkg/storage/lock.go @@ -1,4 +1,4 @@ -package datastore +package storage import ( "strconv" diff --git a/pkg/storage/lock_test.go b/pkg/storage/lock_test.go index 203ebf1..ab87bba 100644 --- a/pkg/storage/lock_test.go +++ b/pkg/storage/lock_test.go @@ -1,4 +1,4 @@ -package datastore_test +package storage_test import ( "testing" @@ -7,7 +7,7 @@ import ( ) func TestHashLock(t *testing.T) { - hl := &datastore.HashLock{} + hl := &storage.HashLock{} // Invalid hashes testCases := []struct { diff --git a/pkg/storage/retention.go b/pkg/storage/retention.go index ef3154b..f067843 100644 --- a/pkg/storage/retention.go +++ b/pkg/storage/retention.go @@ -1,4 +1,4 @@ -package datastore +package storage import ( "container/list" @@ -47,14 +47,14 @@ func init() { type RetentionScanner struct { globalShutdown chan bool // Closes when Inbucket needs to shut down retentionShutdown chan bool // Closed after the scanner has shut down - ds DataStore + ds Store retentionPeriod time.Duration retentionSleep time.Duration } // NewRetentionScanner launches a go-routine that scans for expired // messages, following the configured interval -func NewRetentionScanner(ds DataStore, shutdownChannel chan bool) *RetentionScanner { +func NewRetentionScanner(ds Store, shutdownChannel chan bool) *RetentionScanner { cfg := config.GetDataStoreConfig() rs := &RetentionScanner{ globalShutdown: shutdownChannel, diff --git a/pkg/storage/retention_test.go b/pkg/storage/retention_test.go index c357f7e..ae221e9 100644 --- a/pkg/storage/retention_test.go +++ b/pkg/storage/retention_test.go @@ -1,4 +1,4 @@ -package datastore +package storage import ( "fmt" diff --git a/pkg/storage/datastore.go b/pkg/storage/storage.go similarity index 87% rename from pkg/storage/datastore.go rename to pkg/storage/storage.go index a9bcb57..a137f9b 100644 --- a/pkg/storage/datastore.go +++ b/pkg/storage/storage.go @@ -1,5 +1,5 @@ -// Package datastore contains implementation independent datastore logic -package datastore +// Package storage contains implementation independent datastore logic +package storage import ( "errors" @@ -19,8 +19,8 @@ var ( ErrNotWritable = errors.New("Message not writable") ) -// DataStore is an interface to get Mailboxes stored in Inbucket -type DataStore interface { +// Store is an interface to get Mailboxes stored in Inbucket +type Store interface { MailboxFor(emailAddress string) (Mailbox, error) AllMailboxes() ([]Mailbox, error) // LockFor is a temporary hack to fix #77 until Datastore revamp diff --git a/pkg/storage/testing.go b/pkg/storage/testing.go index aa6c3de..fc8dbab 100644 --- a/pkg/storage/testing.go +++ b/pkg/storage/testing.go @@ -1,4 +1,4 @@ -package datastore +package storage import ( "io" diff --git a/pkg/webui/mailbox_controller.go b/pkg/webui/mailbox_controller.go index bc2ef77..f9684b9 100644 --- a/pkg/webui/mailbox_controller.go +++ b/pkg/webui/mailbox_controller.go @@ -105,7 +105,7 @@ func MailboxShow(w http.ResponseWriter, req *http.Request, ctx *web.Context) (er return fmt.Errorf("Failed to get mailbox for %q: %v", name, err) } msg, err := mb.GetMessage(id) - if err == datastore.ErrNotExist { + if err == storage.ErrNotExist { http.NotFound(w, req) return nil } @@ -154,7 +154,7 @@ func MailboxHTML(w http.ResponseWriter, req *http.Request, ctx *web.Context) (er return fmt.Errorf("Failed to get mailbox for %q: %v", name, err) } message, err := mb.GetMessage(id) - if err == datastore.ErrNotExist { + if err == storage.ErrNotExist { http.NotFound(w, req) return nil } @@ -191,7 +191,7 @@ func MailboxSource(w http.ResponseWriter, req *http.Request, ctx *web.Context) ( return fmt.Errorf("Failed to get mailbox for %q: %v", name, err) } message, err := mb.GetMessage(id) - if err == datastore.ErrNotExist { + if err == storage.ErrNotExist { http.NotFound(w, req) return nil } @@ -237,7 +237,7 @@ func MailboxDownloadAttach(w http.ResponseWriter, req *http.Request, ctx *web.Co return fmt.Errorf("Failed to get mailbox for %q: %v", name, err) } message, err := mb.GetMessage(id) - if err == datastore.ErrNotExist { + if err == storage.ErrNotExist { http.NotFound(w, req) return nil } @@ -290,7 +290,7 @@ func MailboxViewAttach(w http.ResponseWriter, req *http.Request, ctx *web.Contex return fmt.Errorf("Failed to get mailbox for %q: %v", name, err) } message, err := mb.GetMessage(id) - if err == datastore.ErrNotExist { + if err == storage.ErrNotExist { http.NotFound(w, req) return nil }