aboutsummaryrefslogtreecommitdiffstats
path: root/worker/imap/worker.go
diff options
context:
space:
mode:
authorKoni Marti <koni.marti@gmail.com>2024-01-20 11:31:03 +0100
committerRobin Jarry <robin@jarry.cc>2024-01-25 21:55:17 +0100
commit1980744f7bf9e147abf649d37a2fa7dddd4e7254 (patch)
treedba9967e81d4c65c287dd5c9c1e26c5f269891b7 /worker/imap/worker.go
parent3452c9233f623c4049098b66911ae82fc14e119c (diff)
downloadaerc-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.go98
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()
}
}
}