imap: refcount clients

Closes: https://github.com/emersion/hydroxide/issues/59
This commit is contained in:
Simon Ser 2020-01-27 19:35:40 +01:00
parent 7d69179f76
commit 83311a0302
No known key found for this signature in database
GPG Key ID: 0FDE7BE0E88F5E48
2 changed files with 49 additions and 12 deletions

View File

@ -16,6 +16,7 @@ type backend struct {
sessions *auth.Manager sessions *auth.Manager
eventsManager *events.Manager eventsManager *events.Manager
updates chan imapbackend.Update updates chan imapbackend.Update
users map[string]*user
} }
func (be *backend) Login(info *imap.ConnInfo, username, password string) (imapbackend.User, error) { func (be *backend) Login(info *imap.ConnInfo, username, password string) (imapbackend.User, error) {
@ -24,17 +25,7 @@ func (be *backend) Login(info *imap.ConnInfo, username, password string) (imapba
return nil, err return nil, err
} }
u, err := c.GetCurrentUser() return getUser(be, username, c, privateKeys)
if err != nil {
return nil, err
}
addrs, err := c.ListAddresses()
if err != nil {
return nil, err
}
return newUser(be, c, u, privateKeys, addrs)
} }
func (be *backend) Updates() <-chan imapbackend.Update { func (be *backend) Updates() <-chan imapbackend.Update {
@ -42,5 +33,10 @@ func (be *backend) Updates() <-chan imapbackend.Update {
} }
func New(sessions *auth.Manager, eventsManager *events.Manager) imapbackend.Backend { func New(sessions *auth.Manager, eventsManager *events.Manager) imapbackend.Backend {
return &backend{sessions, eventsManager, make(chan imapbackend.Update, 50)} return &backend{
sessions: sessions,
eventsManager: eventsManager,
updates: make(chan imapbackend.Update, 50),
users: make(map[string]*user),
}
} }

View File

@ -30,10 +30,12 @@ var systemMailboxes = []struct {
} }
type user struct { type user struct {
backend *backend
c *protonmail.Client c *protonmail.Client
u *protonmail.User u *protonmail.User
privateKeys openpgp.EntityList privateKeys openpgp.EntityList
addrs []*protonmail.Address addrs []*protonmail.Address
numClients int
db *database.User db *database.User
eventsReceiver *events.Receiver eventsReceiver *events.Receiver
@ -45,13 +47,40 @@ type user struct {
eventSent chan struct{} eventSent chan struct{}
} }
func getUser(be *backend, username string, c *protonmail.Client, privateKeys openpgp.EntityList) (*user, error) {
if u, ok := be.users[username]; ok {
u.numClients++
return u, nil
} else {
pu, err := c.GetCurrentUser()
if err != nil {
return nil, err
}
addrs, err := c.ListAddresses()
if err != nil {
return nil, err
}
u, err := newUser(be, c, pu, privateKeys, addrs)
if err != nil {
return nil, err
}
be.users[username] = u
return u, nil
}
}
func newUser(be *backend, c *protonmail.Client, u *protonmail.User, privateKeys openpgp.EntityList, addrs []*protonmail.Address) (*user, error) { func newUser(be *backend, c *protonmail.Client, u *protonmail.User, privateKeys openpgp.EntityList, addrs []*protonmail.Address) (*user, error) {
uu := &user{ uu := &user{
backend: be,
c: c, c: c,
u: u, u: u,
privateKeys: privateKeys, privateKeys: privateKeys,
addrs: addrs, addrs: addrs,
eventSent: make(chan struct{}), eventSent: make(chan struct{}),
numClients: 1,
} }
db, err := database.Open(u.Name + ".db") db, err := database.Open(u.Name + ".db")
@ -70,6 +99,7 @@ func newUser(be *backend, c *protonmail.Client, u *protonmail.User, privateKeys
go uu.receiveEvents(be.updates, ch) go uu.receiveEvents(be.updates, ch)
uu.eventsReceiver = be.eventsManager.Register(c, u.Name, ch, done) uu.eventsReceiver = be.eventsManager.Register(c, u.Name, ch, done)
log.Printf("User %q logged in via IMAP", u.Name)
return uu, nil return uu, nil
} }
@ -164,12 +194,23 @@ func (u *user) RenameMailbox(existingName, newName string) error {
} }
func (u *user) Logout() error { func (u *user) Logout() error {
if u.numClients <= 0 {
panic("unreachable")
}
u.numClients--
if u.numClients > 0 {
return nil
}
delete(u.backend.users, u.u.Name)
close(u.done) close(u.done)
if err := u.db.Close(); err != nil { if err := u.db.Close(); err != nil {
return err return err
} }
log.Printf("User %q logged out via IMAP", u.u.Name)
u.c = nil u.c = nil
u.u = nil u.u = nil
u.privateKeys = nil u.privateKeys = nil