imap: improve reconnect stability

improves the robustness of the imap reconnect feature which was
introduced in commit beae17a6da ("imap: auto-reconnects on connection
error").

If a connection error is emitted, the message list is cleared and a
corresponding error message is shown in the ui. Status bar is updated as
well. Upon reconnect, the directories and the message list will be
re-fetched (same behavior as the connect command).

Reconnect can be enabled and disabled with the connect and the
disconnect commands.

Signed-off-by: Koni Marti <koni.marti@gmail.com>
This commit is contained in:
Koni Marti 2022-02-05 00:07:16 +01:00 committed by Robin Jarry
parent 4bd4f4664a
commit 05ad96a30c
2 changed files with 22 additions and 8 deletions

View file

@ -289,8 +289,10 @@ func (acct *AccountView) onMessage(msg types.WorkerMessage) {
case *types.LabelList: case *types.LabelList:
acct.labels = msg.Labels acct.labels = msg.Labels
case *types.ConnError: case *types.ConnError:
acct.logger.Printf("Connection error = %v", msg.Error) acct.logger.Printf("connection error: %v", msg.Error)
acct.host.SetStatus("Disconnected.")
acct.aerc.PushError(fmt.Sprintf("%v", msg.Error)) acct.aerc.PushError(fmt.Sprintf("%v", msg.Error))
acct.msglist.SetStore(nil)
acct.worker.PostAction(&types.Reconnect{}, nil) acct.worker.PostAction(&types.Reconnect{}, nil)
case *types.Error: case *types.Error:
acct.logger.Printf("%v", msg.Error) acct.logger.Printf("%v", msg.Error)

View file

@ -85,6 +85,11 @@ func (w *IMAPWorker) handleMessage(msg types.WorkerMessage) error {
} }
}() }()
checkConn := func() {
w.stopConnectionObserver()
w.startConnectionObserver()
}
var reterr error // will be returned at the end, needed to support idle var reterr error // will be returned at the end, needed to support idle
switch msg := msg.(type) { switch msg := msg.(type) {
@ -171,9 +176,15 @@ func (w *IMAPWorker) handleMessage(msg types.WorkerMessage) error {
} }
case *types.Connect: case *types.Connect:
if w.client != nil && w.client.State() == imap.SelectedState { if w.client != nil && w.client.State() == imap.SelectedState {
if !w.autoReconnect {
w.autoReconnect = true
checkConn()
}
reterr = fmt.Errorf("Already connected") reterr = fmt.Errorf("Already connected")
break break
} }
w.autoReconnect = true
c, err := w.connect() c, err := w.connect()
if err != nil { if err != nil {
reterr = err reterr = err
@ -187,7 +198,6 @@ func (w *IMAPWorker) handleMessage(msg types.WorkerMessage) error {
w.startConnectionObserver() w.startConnectionObserver()
w.autoReconnect = true
w.worker.PostMessage(&types.Done{types.RespondTo(msg)}, nil) w.worker.PostMessage(&types.Done{types.RespondTo(msg)}, nil)
case *types.Reconnect: case *types.Reconnect:
if !w.autoReconnect { if !w.autoReconnect {
@ -196,6 +206,7 @@ func (w *IMAPWorker) handleMessage(msg types.WorkerMessage) error {
} }
c, err := w.connect() c, err := w.connect()
if err != nil { if err != nil {
checkConn()
reterr = err reterr = err
break break
} }
@ -209,18 +220,17 @@ func (w *IMAPWorker) handleMessage(msg types.WorkerMessage) error {
w.worker.PostMessage(&types.Done{types.RespondTo(msg)}, nil) w.worker.PostMessage(&types.Done{types.RespondTo(msg)}, nil)
case *types.Disconnect: case *types.Disconnect:
w.autoReconnect = false
w.stopConnectionObserver()
if w.client == nil || w.client.State() != imap.SelectedState { if w.client == nil || w.client.State() != imap.SelectedState {
reterr = fmt.Errorf("Not connected") reterr = fmt.Errorf("Not connected")
break break
} }
w.stopConnectionObserver()
if err := w.client.Logout(); err != nil { if err := w.client.Logout(); err != nil {
reterr = err reterr = err
break break
} }
w.autoReconnect = false
w.worker.PostMessage(&types.Done{types.RespondTo(msg)}, nil) w.worker.PostMessage(&types.Done{types.RespondTo(msg)}, nil)
case *types.ListDirectories: case *types.ListDirectories:
w.handleListDirectories(msg) w.handleListDirectories(msg)
@ -306,9 +316,11 @@ func (w *IMAPWorker) startConnectionObserver() {
go func() { go func() {
select { select {
case <-w.client.LoggedOut(): case <-w.client.LoggedOut():
w.worker.PostMessage(&types.ConnError{ if w.autoReconnect {
Error: fmt.Errorf("Logged Out"), w.worker.PostMessage(&types.ConnError{
}, nil) Error: fmt.Errorf("imap: logged out"),
}, nil)
}
case <-w.done: case <-w.done:
return return
} }