imap: Fix a race when sending updates to the clients

create-reload-action
Lukasz Janyst 3 years ago
parent d9cc3801d5
commit 89164ae006
No known key found for this signature in database
GPG Key ID: 32DE641041F17A9A
  1. 4
      pkg/imap/backend.go
  2. 52
      pkg/imap/updates.go

@ -165,7 +165,7 @@ func (ib *imapBackend) Login(_ *imap.ConnInfo, username, password string) (goIMA
} }
if err := imapUser.user.CheckCredentials(slot, password); err != nil { if err := imapUser.user.CheckCredentials(slot, password); err != nil {
log.WithError(err).Error("Could not check bridge password") log.WithError(err).Errorf("Could not check bridge password: %s %s", username, slot)
if err := imapUser.Logout(); err != nil { if err := imapUser.Logout(); err != nil {
log.WithError(err).Warn("Could not logout user after unsuccessful login check") log.WithError(err).Warn("Could not logout user after unsuccessful login check")
} }
@ -188,7 +188,7 @@ func (ib *imapBackend) Login(_ *imap.ConnInfo, username, password string) (goIMA
// Updates returns a channel of updates for IMAP IDLE extension. // Updates returns a channel of updates for IMAP IDLE extension.
func (ib *imapBackend) Updates() <-chan goIMAPBackend.Update { func (ib *imapBackend) Updates() <-chan goIMAPBackend.Update {
return ib.updates.ch return ib.updates.chout
} }
func (ib *imapBackend) CreateMessageLimit() *uint32 { func (ib *imapBackend) CreateMessageLimit() *uint32 {

@ -22,11 +22,11 @@ import (
"sync" "sync"
"time" "time"
"github.com/ljanyst/peroxide/pkg/store"
"github.com/ljanyst/peroxide/pkg/message"
"github.com/ljanyst/peroxide/pkg/pmapi"
imap "github.com/emersion/go-imap" imap "github.com/emersion/go-imap"
goIMAPBackend "github.com/emersion/go-imap/backend" goIMAPBackend "github.com/emersion/go-imap/backend"
"github.com/ljanyst/peroxide/pkg/message"
"github.com/ljanyst/peroxide/pkg/pmapi"
"github.com/ljanyst/peroxide/pkg/store"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -37,20 +37,46 @@ const (
operationDeleteMessage operation = "expunge" operationDeleteMessage operation = "expunge"
) )
type updateHelper struct {
data goIMAPBackend.Update
expiration time.Time
}
type imapUpdates struct { type imapUpdates struct {
lock sync.Locker lock sync.Locker
blocking map[string]bool blocking map[string]bool
delayedExpunges map[string][]chan struct{} delayedExpunges map[string][]chan struct{}
ch chan goIMAPBackend.Update chout chan goIMAPBackend.Update
chin chan updateHelper
} }
func newIMAPUpdates() *imapUpdates { func newIMAPUpdates() *imapUpdates {
return &imapUpdates{ iu := &imapUpdates{
lock: &sync.Mutex{}, lock: &sync.Mutex{},
blocking: map[string]bool{}, blocking: map[string]bool{},
delayedExpunges: map[string][]chan struct{}{}, delayedExpunges: map[string][]chan struct{}{},
ch: make(chan goIMAPBackend.Update), chout: make(chan goIMAPBackend.Update),
chin: make(chan updateHelper, 1000),
} }
go func() {
for {
upd := <-iu.chin
if time.Now().After(upd.expiration) {
log.Warn("IMAP update could not be sent (timeout)")
continue
}
select {
case iu.chout <- upd.data:
case <-time.After(1 * time.Second):
log.Warn("IMAP update could not be sent (timeout)")
}
}
}()
return iu
} }
func (iu *imapUpdates) block(address, mailboxName string, op operation) { func (iu *imapUpdates) block(address, mailboxName string, op operation) {
@ -192,20 +218,16 @@ func (iu *imapUpdates) MailboxStatus(address, mailboxName string, total, unread,
} }
func (iu *imapUpdates) sendIMAPUpdate(update goIMAPBackend.Update, isBlocking bool) { func (iu *imapUpdates) sendIMAPUpdate(update goIMAPBackend.Update, isBlocking bool) {
if iu.ch == nil { if iu.chout == nil {
log.Trace("IMAP IDLE unavailable") log.Trace("IMAP IDLE unavailable")
return return
} }
done := update.Done() done := update.Done()
go func() { iu.chin <- updateHelper{
select { data: update,
case <-time.After(1 * time.Second): expiration: time.Now().Add(1 * time.Second),
log.Warn("IMAP update could not be sent (timeout)") }
return
case iu.ch <- update:
}
}()
if !isBlocking { if !isBlocking {
return return

Loading…
Cancel
Save