Skip to content
This repository has been archived by the owner on Feb 13, 2022. It is now read-only.

Commit

Permalink
Refactoring backends, fixes #6
Browse files Browse the repository at this point in the history
  • Loading branch information
emersion committed Mar 25, 2016
1 parent e16324d commit 6c00ff1
Show file tree
Hide file tree
Showing 24 changed files with 316 additions and 300 deletions.
60 changes: 35 additions & 25 deletions backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,47 @@
package backend

// A backend takes care of storing all mailbox data.
type Backend interface {
type Backend struct {
ContactsBackend
LabelsBackend
ConversationsBackend
SendBackend
DomainsBackend
EventsBackend
UsersBackend
KeysBackend
}

// TODO: move all these methods to sub-backends

// Check if a username is available.
IsUsernameAvailable(username string) (bool, error)
// Get a user.
GetUser(id string) (*User, error)
// Check if the provided username and password are correct
Auth(username, password string) (*User, error)
// Insert a new user. Returns the newly created user.
InsertUser(user *User, password string) (*User, error)
// Update an existing user.
UpdateUser(update *UserUpdate) error
// Update a user's password.
UpdateUserPassword(id, current, new string) error
// Update a user's private key.
UpdateKeypair(id, password string, keypair *Keypair) error
// Delete a user.
//DeleteUser(id string) error

// Get a public key for a user.
GetPublicKey(email string) (string, error)
// Set one or some of this backend's components.
func (b *Backend) Set(backends ...interface{}) {
for _, bkd := range backends {
if contacts, ok := bkd.(ContactsBackend); ok {
b.ContactsBackend = contacts
}
if labels, ok := bkd.(LabelsBackend); ok {
b.LabelsBackend = labels
}
if conversations, ok := bkd.(ConversationsBackend); ok {
b.ConversationsBackend = conversations
}
if send, ok := bkd.(SendBackend); ok {
b.SendBackend = send
}
if domains, ok := bkd.(DomainsBackend); ok {
b.DomainsBackend = domains
}
if events, ok := bkd.(EventsBackend); ok {
b.EventsBackend = events
}
if users, ok := bkd.(UsersBackend); ok {
b.UsersBackend = users
}
if keys, ok := bkd.(KeysBackend); ok {
b.KeysBackend = keys
}
}
}

// Set a backend.
// TODO: remove this
Set(item interface{}) error
func New() *Backend {
return &Backend{}
}
55 changes: 12 additions & 43 deletions backend/imap/backend.go
Original file line number Diff line number Diff line change
@@ -1,55 +1,24 @@
package imap

import (
"errors"

"github.com/emersion/neutron/backend"
"github.com/emersion/neutron/backend/memory"
"github.com/emersion/neutron/backend/util"
)

type Backend struct {
backend.DomainsBackend
backend.ContactsBackend
backend.LabelsBackend
backend.ConversationsBackend
backend.SendBackend
backend.EventsBackend

*connBackend

users map[string]*backend.User
passwords map[string]string
type Config struct {
Hostname string
Port int
Suffix string
}

func (b *Backend) Set(item interface{}) error {
switch val := item.(type) {
case backend.SendBackend:
b.SendBackend = val
default:
return errors.New("Unsupported backend")
}
return nil
}

func New(config *Config) backend.Backend {
bkd := &Backend{
connBackend: newConnBackend(config),

users: map[string]*backend.User{},
passwords: map[string]string{},
}

messages := newMessagesBackend(bkd.connBackend)
conversations := util.NewDummyConversationsBackend(messages)
func Use(config *Config, bkd *backend.Backend) *Users {
conns := newConns(config)
messages := newMessages(conns)
conversations := util.NewDummyConversations(messages)
users := newUsers(conns)

// TODO: do not use memory backends
bkd.EventsBackend = memory.NewEventsBackend()
bkd.DomainsBackend = memory.NewDomainsBackend()
bkd.ContactsBackend = util.NewEventedContactsBackend(memory.NewContactsBackend(), bkd.EventsBackend)
bkd.LabelsBackend = util.NewEventedLabelsBackend(memory.NewLabelsBackend(), bkd.EventsBackend)
bkd.ConversationsBackend = util.NewEventedConversationsBackend(conversations, bkd.EventsBackend)
bkd.SendBackend = util.NewNoopSendBackend()
bkd.Set(conversations, users)

return bkd
// TODO: do not return users backend
return users
}
26 changes: 10 additions & 16 deletions backend/imap/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,6 @@ import (
"github.com/mxk/go-imap/imap"
)

type Config struct {
Hostname string
Port int
Suffix string
}

func (c *Config) Host() string {
host := c.Hostname
if c.Port > 0 {
Expand All @@ -22,34 +16,34 @@ func (c *Config) Host() string {
return host
}

type connBackend struct {
type conns struct {
config *Config
conns map[string]*imap.Client
clients map[string]*imap.Client
locks map[string]sync.Locker
}

func (b *connBackend) insertConn(user string, conn *imap.Client) {
b.conns[user] = conn
func (b *conns) insertConn(user string, conn *imap.Client) {
b.clients[user] = conn
b.locks[user] = &sync.Mutex{}
}

func (b *connBackend) getConn(user string) (*imap.Client, func(), error) {
func (b *conns) getConn(user string) (*imap.Client, func(), error) {
lock, ok := b.locks[user]
if !ok {
return nil, nil, errors.New("No such user")
}

lock.Lock()

conn, ok := b.conns[user]
conn, ok := b.clients[user]
if !ok {
lock.Unlock()
return nil, nil, errors.New("No such user")
}

state := conn.State()
if state == imap.Logout || state == imap.Closed {
delete(b.conns, user)
delete(b.clients, user)
delete(b.locks, user)
lock.Unlock()
return nil, nil, errors.New("Connection to IMAP server closed")
Expand All @@ -58,11 +52,11 @@ func (b *connBackend) getConn(user string) (*imap.Client, func(), error) {
return conn, lock.Unlock, nil
}

func newConnBackend(config *Config) *connBackend {
return &connBackend{
func newConns(config *Config) *conns {
return &conns{
config: config,

conns: map[string]*imap.Client{},
clients: map[string]*imap.Client{},
locks: map[string]sync.Locker{},
}
}
38 changes: 19 additions & 19 deletions backend/imap/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import (
"github.com/emersion/neutron/backend/util/textproto"
)

type MessagesBackend struct {
*connBackend
type Messages struct {
*conns

mailboxes map[string][]*imap.MailboxInfo
}
Expand Down Expand Up @@ -119,7 +119,7 @@ func parseEnvelope(msg *backend.Message, envelope []imap.Field) {
// envelope[8] is Message-Id
}

func (b *MessagesBackend) getMailboxes(user string) ([]*imap.MailboxInfo, error) {
func (b *Messages) getMailboxes(user string) ([]*imap.MailboxInfo, error) {
// Mailboxes list already retrieved
if len(b.mailboxes[user]) > 0 {
return b.mailboxes[user], nil
Expand Down Expand Up @@ -157,7 +157,7 @@ func (b *MessagesBackend) getMailboxes(user string) ([]*imap.MailboxInfo, error)
return b.mailboxes[user], nil
}

func (b *MessagesBackend) getLabelMailbox(user, label string) (mailbox string, err error) {
func (b *Messages) getLabelMailbox(user, label string) (mailbox string, err error) {
mailboxes, err := b.getMailboxes(user)
if err != nil {
return
Expand All @@ -174,7 +174,7 @@ func (b *MessagesBackend) getLabelMailbox(user, label string) (mailbox string, e
return
}

func (b *MessagesBackend) selectMailbox(user, mailbox string) (err error) {
func (b *Messages) selectMailbox(user, mailbox string) (err error) {
c, unlock, err := b.getConn(user)
if err != nil {
return
Expand All @@ -191,7 +191,7 @@ func (b *MessagesBackend) selectMailbox(user, mailbox string) (err error) {
return
}

func (b *MessagesBackend) selectLabelMailbox(user, label string) (err error) {
func (b *Messages) selectLabelMailbox(user, label string) (err error) {
mailbox, err := b.getLabelMailbox(user, label)
if err != nil {
return
Expand All @@ -200,7 +200,7 @@ func (b *MessagesBackend) selectLabelMailbox(user, label string) (err error) {
return b.selectMailbox(user, mailbox)
}

func (b *MessagesBackend) GetMessage(user, id string) (msg *backend.Message, err error) {
func (b *Messages) GetMessage(user, id string) (msg *backend.Message, err error) {
mailbox, uid, err := parseMessageId(id)
if err != nil {
return
Expand Down Expand Up @@ -257,7 +257,7 @@ func reverseMessagesList(msgs []*backend.Message) {
}
}

func (b *MessagesBackend) ListMessages(user string, filter *backend.MessagesFilter) (msgs []*backend.Message, total int, err error) {
func (b *Messages) ListMessages(user string, filter *backend.MessagesFilter) (msgs []*backend.Message, total int, err error) {
if filter.Label == "" {
err = errors.New("Cannot list messages without specifying a label")
return
Expand Down Expand Up @@ -326,7 +326,7 @@ func (b *MessagesBackend) ListMessages(user string, filter *backend.MessagesFilt
return
}

func (b *MessagesBackend) CountMessages(user string) (counts []*backend.MessagesCount, err error) {
func (b *Messages) CountMessages(user string) (counts []*backend.MessagesCount, err error) {
mailboxes, err := b.getMailboxes(user)
if err != nil {
return
Expand Down Expand Up @@ -356,7 +356,7 @@ func (b *MessagesBackend) CountMessages(user string) (counts []*backend.Messages
return
}

func (b *MessagesBackend) InsertMessage(user string, msg *backend.Message) (inserted *backend.Message, err error) {
func (b *Messages) InsertMessage(user string, msg *backend.Message) (inserted *backend.Message, err error) {
mailbox, err := b.getLabelMailbox(user, backend.DraftLabel)
if err != nil {
return
Expand Down Expand Up @@ -387,7 +387,7 @@ func (b *MessagesBackend) InsertMessage(user string, msg *backend.Message) (inse
return
}

func (b *MessagesBackend) updateMessageFlags(user string, seqset *imap.SeqSet, flag string, value bool) error {
func (b *Messages) updateMessageFlags(user string, seqset *imap.SeqSet, flag string, value bool) error {
item := "+FLAGS"
if !value {
item = "-FLAGS"
Expand All @@ -409,7 +409,7 @@ func (b *MessagesBackend) updateMessageFlags(user string, seqset *imap.SeqSet, f
return nil
}

func (b *MessagesBackend) deleteMessages(user string, seqset *imap.SeqSet) (err error) {
func (b *Messages) deleteMessages(user string, seqset *imap.SeqSet) (err error) {
c, unlock, err := b.getConn(user)
if err != nil {
return
Expand All @@ -431,7 +431,7 @@ func (b *MessagesBackend) deleteMessages(user string, seqset *imap.SeqSet) (err
}

// TODO: only supports moving one single message
func (b *MessagesBackend) copyMessages(user string, seqset *imap.SeqSet, mbox string) (uid uint32, err error) {
func (b *Messages) copyMessages(user string, seqset *imap.SeqSet, mbox string) (uid uint32, err error) {
c, unlock, err := b.getConn(user)
if err != nil {
return
Expand All @@ -452,7 +452,7 @@ func (b *MessagesBackend) copyMessages(user string, seqset *imap.SeqSet, mbox st
return
}

func (b *MessagesBackend) moveMessages(user string, seqset *imap.SeqSet, mbox string) (uid uint32, err error) {
func (b *Messages) moveMessages(user string, seqset *imap.SeqSet, mbox string) (uid uint32, err error) {
uid, err = b.copyMessages(user, seqset, mbox)
if err != nil {
return
Expand All @@ -462,7 +462,7 @@ func (b *MessagesBackend) moveMessages(user string, seqset *imap.SeqSet, mbox st
return
}

func (b *MessagesBackend) UpdateMessage(user string, update *backend.MessageUpdate) (msg *backend.Message, err error) {
func (b *Messages) UpdateMessage(user string, update *backend.MessageUpdate) (msg *backend.Message, err error) {
mailbox, uid, err := parseMessageId(update.Message.ID)
if err != nil {
return
Expand Down Expand Up @@ -547,7 +547,7 @@ func (b *MessagesBackend) UpdateMessage(user string, update *backend.MessageUpda
return
}

func (b *MessagesBackend) DeleteMessage(user, id string) (err error) {
func (b *Messages) DeleteMessage(user, id string) (err error) {
mailbox, uid, err := parseMessageId(id)
if err != nil {
return
Expand All @@ -565,9 +565,9 @@ func (b *MessagesBackend) DeleteMessage(user, id string) (err error) {
return
}

func newMessagesBackend(conn *connBackend) backend.MessagesBackend {
return &MessagesBackend{
connBackend: conn,
func newMessages(conns *conns) backend.MessagesBackend {
return &Messages{
conns: conns,
mailboxes: map[string][]*imap.MailboxInfo{},
}
}
Loading

0 comments on commit 6c00ff1

Please sign in to comment.