diff options
author | Koni Marti <koni.marti@gmail.com> | 2024-01-20 11:31:03 +0100 |
---|---|---|
committer | Robin Jarry <robin@jarry.cc> | 2024-01-25 21:55:17 +0100 |
commit | 1980744f7bf9e147abf649d37a2fa7dddd4e7254 (patch) | |
tree | dba9967e81d4c65c287dd5c9c1e26c5f269891b7 /worker/imap/worker.go | |
parent | 3452c9233f623c4049098b66911ae82fc14e119c (diff) | |
download | aerc-1980744f7bf9e147abf649d37a2fa7dddd4e7254.tar.gz |
idler: improve the imap idler
Rewrite the imap idler to make it more fault tolerant and prevent hangs
(and possibly short writes).
Fixes: https://todo.sr.ht/~rjarry/aerc/208
Signed-off-by: Koni Marti <koni.marti@gmail.com>
Tested-by: Karel Balej <balejk@matfyz.cz>
Acked-by: Robin Jarry <robin@jarry.cc>
Diffstat (limited to 'worker/imap/worker.go')
-rw-r--r-- | worker/imap/worker.go | 98 |
1 files changed, 80 insertions, 18 deletions
diff --git a/worker/imap/worker.go b/worker/imap/worker.go index 7ef759d4..391f365a 100644 --- a/worker/imap/worker.go +++ b/worker/imap/worker.go @@ -80,29 +80,37 @@ type IMAPWorker struct { threadAlgorithm sortthread.ThreadAlgorithm liststatus bool + + executeIdle chan struct{} } func NewIMAPWorker(worker *types.Worker) (types.Backend, error) { return &IMAPWorker{ - updates: make(chan client.Update, 50), - worker: worker, - selected: &imap.MailboxStatus{}, - idler: newIdler(imapConfig{}, worker), - observer: newObserver(imapConfig{}, worker), - caps: &models.Capabilities{}, + updates: make(chan client.Update, 50), + worker: worker, + selected: &imap.MailboxStatus{}, + idler: nil, // will be set in configure() + observer: nil, // will be set in configure() + caps: &models.Capabilities{}, + executeIdle: make(chan struct{}), }, nil } func (w *IMAPWorker) newClient(c *client.Client) { - c.Updates = w.updates + c.Updates = nil w.client = &imapClient{ c, sortthread.NewThreadClient(c), sortthread.NewSortClient(c), extensions.NewListStatusClient(c), } - w.idler.SetClient(w.client) - w.observer.SetClient(w.client) + if w.idler != nil { + w.idler.SetClient(w.client) + c.Updates = w.updates + } + if w.observer != nil { + w.observer.SetClient(w.client) + } sort, err := w.client.sort.SupportSort() if err == nil && sort { w.caps.Sort = true @@ -125,7 +133,7 @@ func (w *IMAPWorker) newClient(c *client.Client) { xgmext, err := w.client.Support("X-GM-EXT-1") if err == nil && xgmext && w.config.useXGMEXT { w.worker.Debugf("Server Capability found: X-GM-EXT-1") - w.worker = middleware.NewGmailWorker(w.worker, w.client.Client, w.idler) + w.worker = middleware.NewGmailWorker(w.worker, w.client.Client) } if err == nil && !xgmext && w.config.useXGMEXT { w.worker.Infof("X-GM-EXT-1 requested, but it is not supported") @@ -133,13 +141,6 @@ func (w *IMAPWorker) newClient(c *client.Client) { } func (w *IMAPWorker) handleMessage(msg types.WorkerMessage) error { - defer func() { - w.idler.Start() - }() - if err := w.idler.Stop(); err != nil { - return err - } - var reterr error // will be returned at the end, needed to support idle // when client is nil allow only certain messages to be handled @@ -200,12 +201,14 @@ func (w *IMAPWorker) handleMessage(msg types.WorkerMessage) error { case *types.Disconnect: w.observer.SetAutoReconnect(false) w.observer.Stop() - if w.client == nil || w.client.State() != imap.SelectedState { + + if w.client == nil || (w.client != nil && w.client.State() != imap.SelectedState) { reterr = errNotConnected break } if err := w.client.Logout(); err != nil { + w.terminate() reterr = err break } @@ -298,10 +301,64 @@ func (w *IMAPWorker) handleImapUpdate(update client.Update) { } } +func (w *IMAPWorker) terminate() { + if w.observer != nil { + w.observer.Stop() + w.observer.SetClient(nil) + } + + if w.client != nil { + w.client.Updates = nil + if err := w.client.Terminate(); err != nil { + w.worker.Errorf("could not terminate connection: %v", err) + } + } + + w.client = nil + w.selected = &imap.MailboxStatus{} + + if w.idler != nil { + w.idler.SetClient(nil) + } +} + +func (w *IMAPWorker) stopIdler() error { + if w.idler == nil { + return nil + } + + if err := w.idler.Stop(); err != nil { + w.terminate() + w.observer.EmitIfNotConnected() + w.worker.Errorf("idler stopped with error:%v", err) + return err + } + + return nil +} + +func (w *IMAPWorker) startIdler() { + if w.idler == nil { + return + } + + w.idler.Start() +} + func (w *IMAPWorker) Run() { for { select { case msg := <-w.worker.Actions(): + + if err := w.stopIdler(); err != nil { + w.worker.PostMessage(&types.Error{ + Message: types.RespondTo(msg), + Error: err, + }, nil) + break + } + w.worker.Tracef("ready to handle %T", msg) + msg = w.worker.ProcessAction(msg) if err := w.handleMessage(msg); errors.Is(err, errUnsupported) { @@ -315,8 +372,13 @@ func (w *IMAPWorker) Run() { }, nil) } + w.startIdler() + case update := <-w.updates: w.handleImapUpdate(update) + + case <-w.executeIdle: + w.idler.Execute() } } } |