unified all account state exceptions

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/entities/Account.java        |  12 
src/main/java/eu/siacs/conversations/xmpp/OnIqPacketReceived.java |   2 
src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java     | 100 
src/main/res/values/strings.xml                                   |   4 
4 files changed, 49 insertions(+), 69 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/entities/Account.java 🔗

@@ -118,9 +118,11 @@ public class Account extends AbstractEntity {
 		REGISTRATION_CONFLICT(true),
 		REGISTRATION_SUCCESSFUL,
 		REGISTRATION_NOT_SUPPORTED(true),
-		SECURITY_ERROR(true),
+		TLS_ERROR(true),
 		INCOMPATIBLE_SERVER(true),
 		TOR_NOT_AVAILABLE(true),
+		DOWNGRADE_ATTACK(true),
+		SESSION_FAILURE(true),
 		BIND_FAILURE(true),
 		HOST_UNKNOWN(true),
 		REGISTRATION_PLEASE_WAIT(true),
@@ -168,14 +170,18 @@ public class Account extends AbstractEntity {
 					return R.string.account_status_regis_success;
 				case REGISTRATION_NOT_SUPPORTED:
 					return R.string.account_status_regis_not_sup;
-				case SECURITY_ERROR:
-					return R.string.account_status_security_error;
+				case TLS_ERROR:
+					return R.string.account_status_tls_error;
 				case INCOMPATIBLE_SERVER:
 					return R.string.account_status_incompatible_server;
 				case TOR_NOT_AVAILABLE:
 					return R.string.account_status_tor_unavailable;
 				case BIND_FAILURE:
 					return R.string.account_status_bind_failure;
+				case SESSION_FAILURE:
+					return R.string.session_failure;
+				case DOWNGRADE_ATTACK:
+					return R.string.downgrade_attack;
 				case HOST_UNKNOWN:
 					return R.string.account_status_host_unknown;
 				case POLICY_VIOLATION:

src/main/java/eu/siacs/conversations/xmpp/OnIqPacketReceived.java 🔗

@@ -4,5 +4,5 @@ import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.xmpp.stanzas.IqPacket;
 
 public interface OnIqPacketReceived extends PacketReceived {
-	public void onIqPacketReceived(Account account, IqPacket packet);
+	void onIqPacketReceived(Account account, IqPacket packet);
 }

src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java 🔗

@@ -299,7 +299,7 @@ public class XmppConnection implements Runnable {
 							final SSLSession session = ((SSLSocket) localSocket).getSession();
 							if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), session)) {
 								Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
-								throw new SecurityException();
+								throw new StateChangingException(Account.State.TLS_ERROR);
 							}
 						} catch (KeyManagementException e) {
 							features.encryptionEnabled = false;
@@ -388,7 +388,7 @@ public class XmppConnection implements Runnable {
 
 							if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), ((SSLSocket) localSocket).getSession())) {
 								Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
-								throw new SecurityException();
+								throw new StateChangingException(Account.State.TLS_ERROR);
 							}
 						}
 						if (startXmpp(localSocket)) {
@@ -396,7 +396,7 @@ public class XmppConnection implements Runnable {
 						} else {
 							localSocket.close();
 						}
-					} catch (final SecurityException e) {
+					} catch (final StateChangingException e) {
 						throw e;
 					} catch (InterruptedException e) {
 						Log.d(Config.LOGTAG,account.getJid().toBareJid()+": thread was interrupted before beginning stream");
@@ -410,28 +410,14 @@ public class XmppConnection implements Runnable {
 				}
 			}
 			processStream();
-		}  catch (final java.lang.SecurityException e) {
+		}  catch (final SecurityException e) {
 			this.changeStatus(Account.State.MISSING_INTERNET_PERMISSION);
-		} catch (final RegistrationNotSupportedException e) {
-			this.changeStatus(Account.State.REGISTRATION_NOT_SUPPORTED);
-		} catch (final IncompatibleServerException e) {
-			this.changeStatus(Account.State.INCOMPATIBLE_SERVER);
-		} catch (final SecurityException e) {
-			this.changeStatus(Account.State.SECURITY_ERROR);
-		} catch (final UnauthorizedException e) {
-			this.changeStatus(Account.State.UNAUTHORIZED);
-		} catch (final PaymentRequiredException e) {
-			this.changeStatus(Account.State.PAYMENT_REQUIRED);
+		} catch(final StateChangingException e) {
+			this.changeStatus(e.state);
 		} catch (final UnknownHostException | ConnectException e) {
 			this.changeStatus(Account.State.SERVER_NOT_FOUND);
 		} catch (final SocksSocketFactory.SocksProxyNotFoundException e) {
 			this.changeStatus(Account.State.TOR_NOT_AVAILABLE);
-		} catch(final StreamErrorHostUnknown e) {
-			this.changeStatus(Account.State.HOST_UNKNOWN);
-		} catch(final StreamErrorPolicyViolation e) {
-			this.changeStatus(Account.State.POLICY_VIOLATION);
-		} catch(final StreamError e) {
-			this.changeStatus(Account.State.STREAM_ERROR);
 		} catch (final IOException | XmlPullParserException | NoSuchAlgorithmException e) {
 			Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage());
 			this.changeStatus(Account.State.OFFLINE);
@@ -528,8 +514,8 @@ public class XmppConnection implements Runnable {
 				try {
 					saslMechanism.getResponse(challenge);
 				} catch (final SaslMechanism.AuthenticationException e) {
-					disconnect(true);
 					Log.e(Config.LOGTAG, String.valueOf(e));
+					throw new StateChangingException(Account.State.UNAUTHORIZED);
 				}
 				Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": logged in");
 				account.setKey(Account.PINNED_MECHANISM_KEY,
@@ -551,9 +537,9 @@ public class XmppConnection implements Runnable {
 						&& text.contains("renew")
 						&& Config.MAGIC_CREATE_DOMAIN != null
 						&& text.contains(Config.MAGIC_CREATE_DOMAIN)) {
-					throw new PaymentRequiredException();
+					throw new StateChangingException(Account.State.PAYMENT_REQUIRED);
 				} else {
-					throw new UnauthorizedException();
+					throw new StateChangingException(Account.State.UNAUTHORIZED);
 				}
 			} else if (nextTag.isStart("challenge")) {
 				final String challenge = tagReader.readElement(nextTag).getContent();
@@ -776,7 +762,11 @@ public class XmppConnection implements Runnable {
 				}
 			}
 			if (callback != null) {
-				callback.onIqPacketReceived(account,packet);
+				try {
+					callback.onIqPacketReceived(account, packet);
+				} catch (StateChangingError error) {
+					throw new StateChangingException(error.state);
+				}
 			}
 		}
 	}
@@ -819,7 +809,7 @@ public class XmppConnection implements Runnable {
 
 			if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), sslSocket.getSession())) {
 				Log.d(Config.LOGTAG,account.getJid().toBareJid()+": TLS certificate verification failed");
-				throw new SecurityException();
+				throw new StateChangingException(Account.State.TLS_ERROR);
 			}
 			tagReader.setInputStream(sslSocket.getInputStream());
 			tagWriter.setOutputStream(sslSocket.getOutputStream());
@@ -835,7 +825,7 @@ public class XmppConnection implements Runnable {
 			sslSocket.close();
 		} catch (final NoSuchAlgorithmException | KeyManagementException e1) {
 			Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
-			throw new SecurityException();
+			throw new StateChangingException(Account.State.TLS_ERROR);
 		}
 	}
 
@@ -848,10 +838,10 @@ public class XmppConnection implements Runnable {
 			if (features.encryptionEnabled || Config.ALLOW_NON_TLS_CONNECTIONS) {
 				sendRegistryRequest();
 			} else {
-				throw new IncompatibleServerException();
+				throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER);
 			}
 		} else if (!this.streamFeatures.hasChild("register") && account.isOptionSet(Account.OPTION_REGISTER)) {
-			throw new RegistrationNotSupportedException();
+			throw new StateChangingException(Account.State.REGISTRATION_NOT_SUPPORTED);
 		} else if (this.streamFeatures.hasChild("mechanisms")
 				&& shouldAuthenticate
 				&& (features.encryptionEnabled || Config.ALLOW_NON_TLS_CONNECTIONS)) {
@@ -868,7 +858,7 @@ public class XmppConnection implements Runnable {
 			if (this.streamFeatures.hasChild("bind")) {
 				sendBindRequest();
 			} else {
-				throw new IncompatibleServerException();
+				throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER);
 			}
 		}
 	}
@@ -898,7 +888,7 @@ public class XmppConnection implements Runnable {
 						" has lower priority (" + String.valueOf(saslMechanism.getPriority()) +
 						") than pinned priority (" + pinnedMechanism +
 						"). Possible downgrade attack?");
-				throw new SecurityException();
+				throw new StateChangingException(Account.State.DOWNGRADE_ATTACK);
 			}
 			Log.d(Config.LOGTAG, account.getJid().toString() + ": Authenticating with " + saslMechanism.getMechanism());
 			auth.setAttribute("mechanism", saslMechanism.getMechanism());
@@ -907,7 +897,7 @@ public class XmppConnection implements Runnable {
 			}
 			tagWriter.writeElement(auth);
 		} else {
-			throw new IncompatibleServerException();
+			throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER);
 		}
 	}
 
@@ -1042,8 +1032,7 @@ public class XmppConnection implements Runnable {
 				} else {
 					Log.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure (" + packet.toString());
 				}
-				forceCloseSocket();
-				changeStatus(Account.State.BIND_FAILURE);
+				throw new StateChangingError(Account.State.BIND_FAILURE);
 			}
 		});
 	}
@@ -1085,8 +1074,7 @@ public class XmppConnection implements Runnable {
 				if (packet.getType() == IqPacket.TYPE.RESULT) {
 					sendPostBindInitialization();
 				} else if (packet.getType() != IqPacket.TYPE.TIMEOUT) {
-					Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not init sessions");
-					disconnect(true);
+					throw new StateChangingError(Account.State.SESSION_FAILURE);
 				}
 			}
 		});
@@ -1266,12 +1254,12 @@ public class XmppConnection implements Runnable {
 					+ account.getResource() + ")");
 			throw new IOException();
 		} else if (streamError.hasChild("host-unknown")) {
-			throw new StreamErrorHostUnknown();
+			throw new StateChangingException(Account.State.HOST_UNKNOWN);
 		} else if (streamError.hasChild("policy-violation")) {
-			throw new StreamErrorPolicyViolation();
+			throw new StateChangingException(Account.State.POLICY_VIOLATION);
 		} else {
 			Log.d(Config.LOGTAG,account.getJid().toBareJid()+": stream error "+streamError.toString());
-			throw new StreamError();
+			throw new StateChangingException(Account.State.STREAM_ERROR);
 		}
 	}
 
@@ -1561,36 +1549,20 @@ public class XmppConnection implements Runnable {
 		return Identity.UNKNOWN;
 	}
 
-	private class UnauthorizedException extends IOException {
-
-	}
-
-	private class SecurityException extends IOException {
-
-	}
-
-	private class IncompatibleServerException extends IOException {
-
-	}
-
-	private class StreamErrorHostUnknown extends StreamError {
-
-	}
-
-	private class StreamErrorPolicyViolation extends StreamError {
-
-	}
-
-	private class StreamError extends IOException {
-
-	}
-
-	private class PaymentRequiredException extends IOException {
+	private class StateChangingError extends Error {
+		private final Account.State state;
 
+		public StateChangingError(Account.State state) {
+			this.state = state;
+		}
 	}
 
-	private class RegistrationNotSupportedException extends IOException {
+	private class StateChangingException extends IOException {
+		private final Account.State state;
 
+		public StateChangingException(Account.State state) {
+			this.state = state;
+		}
 	}
 
 	public enum Identity {

src/main/res/values/strings.xml 🔗

@@ -158,7 +158,7 @@
 	<string name="account_status_regis_conflict">Username already in use</string>
 	<string name="account_status_regis_success">Registration completed</string>
 	<string name="account_status_regis_not_sup">Server does not support registration</string>
-	<string name="account_status_security_error">Security error</string>
+	<string name="account_status_tls_error">TLS error</string>
 	<string name="account_status_policy_violation">Policy violation</string>
 	<string name="account_status_incompatible_server">Incompatible server</string>
 	<string name="account_status_stream_error">Stream error</string>
@@ -747,4 +747,6 @@
 	<string name="block_entire_domain">Block entire domain</string>
 	<string name="online_right_now">online right now</string>
 	<string name="retry_decryption">Retry decryption</string>
+	<string name="session_failure">Session failure</string>
+	<string name="downgrade_attack">Downgrade attack</string>
 </resources>