Ensure MinimalFolder remote open forces closed on hard errors

Minimal folder should force a folder closed when an error occurrs that
it isn't going to be able to recover from.
This commit is contained in:
Michael Gratton 2019-01-14 11:29:27 +11:00
parent b0f85b3af8
commit 46bb4d1b6c
3 changed files with 61 additions and 44 deletions

View file

@ -960,12 +960,18 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
); );
return; return;
} catch (Error err) { } catch (Error err) {
debug("Other error: %s", err.message); ErrorContext context = new ErrorContext(err);
// Notify that there was a connection error, but don't if (is_unrecoverable_failure(err)) {
// force the folder closed, since it might come good again debug("Unrecoverable failure opening remote, forcing closed: %s",
// if the user fixes an auth problem or the network comes context.format_full_error());
// back or whatever. yield force_close(
notify_open_failed(Folder.OpenFailed.REMOTE_ERROR, err); CloseReason.LOCAL_CLOSE, CloseReason.REMOTE_ERROR
);
} else {
debug("Recoverable error opening remote: %s",
context.format_full_error());
notify_open_failed(Folder.OpenFailed.REMOTE_ERROR, err);
}
return; return;
} }

View file

@ -519,11 +519,11 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
} catch (Error replay_err) { } catch (Error replay_err) {
debug("Replay remote error for %s on %s: %s (%s)", op.to_string(), to_string(), debug("Replay remote error for %s on %s: %s (%s)", op.to_string(), to_string(),
replay_err.message, op.on_remote_error.to_string()); replay_err.message, op.on_remote_error.to_string());
// If a hard failure and operation allows remote replay and not closing, // If a recoverable failure and operation allows
// re-schedule now // remote replay and not closing, re-schedule now
if ((op.on_remote_error == ReplayOperation.OnError.RETRY) if ((op.on_remote_error == ReplayOperation.OnError.RETRY)
&& is_hard_failure(replay_err) && !is_unrecoverable_failure(replay_err)
&& state == State.OPEN) { && state == State.OPEN) {
debug("Schedule op retry %s on %s", op.to_string(), to_string()); debug("Schedule op retry %s on %s", op.to_string(), to_string());

View file

@ -6,40 +6,51 @@
namespace Geary.ImapEngine { namespace Geary.ImapEngine {
/** /**
* A hard failure is defined as one due to hardware or connectivity issues, where a soft failure * Determines if retrying an operation might succeed or not.
* is due to software reasons, like credential failure or protocol violation. *
*/ * A recoverable failure is defined as one that may not occur
private static bool is_hard_failure(Error err) { * again if the operation that caused it is retried, without
// CANCELLED is not a hard error * needing to make some change in the mean time. For example,
if (err is IOError.CANCELLED) * recoverable failures may occur due to transient network
return false; * connectivity issues or server rate limiting. On the other hand,
* an unrecoverable failure is due to some problem that will not
* succeed if tried again unless some action is taken, such as
* authentication failures, protocol parsing errors, and so on.
*/
private static bool is_unrecoverable_failure(GLib.Error err) {
return !(
err is EngineError.SERVER_UNAVAILABLE ||
err is IOError.BROKEN_PIPE ||
err is IOError.BUSY ||
err is IOError.CONNECTION_CLOSED ||
err is IOError.NOT_CONNECTED ||
err is IOError.TIMED_OUT ||
err is ImapError.NOT_CONNECTED ||
err is ImapError.TIMED_OUT ||
err is ImapError.UNAVAILABLE
);
}
// Treat other errors -- most likely IOErrors -- as hard failures /**
if (!(err is ImapError) && !(err is EngineError)) * Determines if an error was caused by the remote host or not.
return true; */
private static bool is_remote_error(GLib.Error err) {
return err is ImapError.NOT_CONNECTED return (
|| err is ImapError.TIMED_OUT err is EngineError.NOT_FOUND ||
|| err is ImapError.SERVER_ERROR err is EngineError.SERVER_UNAVAILABLE ||
|| err is EngineError.SERVER_UNAVAILABLE; err is IOError.CONNECTION_CLOSED ||
} err is IOError.CONNECTION_REFUSED ||
err is IOError.HOST_UNREACHABLE ||
/** err is IOError.MESSAGE_TOO_LARGE ||
* Determines if this IOError related to a remote host or not. err is IOError.NETWORK_UNREACHABLE ||
*/ err is IOError.NOT_CONNECTED ||
private static bool is_remote_error(GLib.Error err) { err is IOError.PROXY_AUTH_FAILED ||
return err is ImapError err is IOError.PROXY_FAILED ||
|| err is IOError.CONNECTION_CLOSED err is IOError.PROXY_NEED_AUTH ||
|| err is IOError.CONNECTION_REFUSED err is IOError.PROXY_NOT_ALLOWED ||
|| err is IOError.HOST_UNREACHABLE err is ImapError
|| err is IOError.MESSAGE_TOO_LARGE );
|| err is IOError.NETWORK_UNREACHABLE }
|| err is IOError.NOT_CONNECTED
|| err is IOError.PROXY_AUTH_FAILED
|| err is IOError.PROXY_FAILED
|| err is IOError.PROXY_NEED_AUTH
|| err is IOError.PROXY_NOT_ALLOWED;
}
} }