diff --git a/src/main/java/eu/siacs/conversations/entities/Account.java b/src/main/java/eu/siacs/conversations/entities/Account.java
index dc354adc4..9220cc192 100644
--- a/src/main/java/eu/siacs/conversations/entities/Account.java
+++ b/src/main/java/eu/siacs/conversations/entities/Account.java
@@ -640,6 +640,7 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
TLS_ERROR,
TLS_ERROR_DOMAIN,
INCOMPATIBLE_SERVER,
+ INCOMPATIBLE_CLIENT,
TOR_NOT_AVAILABLE,
DOWNGRADE_ATTACK,
SESSION_FAILURE,
@@ -709,6 +710,8 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
return R.string.account_status_tls_error_domain;
case INCOMPATIBLE_SERVER:
return R.string.account_status_incompatible_server;
+ case INCOMPATIBLE_CLIENT:
+ return R.string.account_status_incompatible_client;
case TOR_NOT_AVAILABLE:
return R.string.account_status_tor_unavailable;
case BIND_FAILURE:
diff --git a/src/main/java/eu/siacs/conversations/xml/Tag.java b/src/main/java/eu/siacs/conversations/xml/Tag.java
index 2e6098522..db2b11172 100644
--- a/src/main/java/eu/siacs/conversations/xml/Tag.java
+++ b/src/main/java/eu/siacs/conversations/xml/Tag.java
@@ -56,11 +56,17 @@ public class Tag {
this.attributes = attributes;
}
- public boolean isStart(String needle) {
- if (needle == null) return false;
+ public boolean isStart(final String needle) {
+ if (needle == null) {
+ return false;
+ }
return (this.type == START) && (needle.equals(this.name));
}
+ public boolean isStart(final String name, final String namespace) {
+ return isStart(name) && namespace != null && namespace.equals(this.getAttribute("xmlns"));
+ }
+
public boolean isEnd(String needle) {
if (needle == null) return false;
return (this.type == END) && (needle.equals(this.name));
diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
index bd11fcbe2..0fbb85768 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
@@ -466,7 +466,7 @@ public class XmppConnection implements Runnable {
processStreamError(nextTag);
} else if (nextTag.isStart("features")) {
processStreamFeatures(nextTag);
- } else if (nextTag.isStart("proceed")) {
+ } else if (nextTag.isStart("proceed", Namespace.TLS)) {
switchOverToTls();
} else if (nextTag.isStart("success")) {
final Element success = tagReader.readElement(nextTag);
@@ -499,8 +499,13 @@ public class XmppConnection implements Runnable {
account.setKey(
Account.PINNED_MECHANISM_KEY, String.valueOf(saslMechanism.getPriority()));
if (version == SaslMechanism.Version.SASL_2) {
- final String authorizationIdentifier = success.findChildContent("authorization-identifier");
- Log.d(Config.LOGTAG,account.getJid().asBareJid()+": SASL 2.0 authorization identifier was "+authorizationIdentifier);
+ final String authorizationIdentifier =
+ success.findChildContent("authorization-identifier");
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid()
+ + ": SASL 2.0 authorization identifier was "
+ + authorizationIdentifier);
}
if (version == SaslMechanism.Version.SASL) {
tagReader.reset();
@@ -513,11 +518,10 @@ public class XmppConnection implements Runnable {
}
break;
}
+ } else if (nextTag.isStart("failure", Namespace.TLS)) {
+ throw new StateChangingException(Account.State.TLS_ERROR);
} else if (nextTag.isStart("failure")) {
final Element failure = tagReader.readElement(nextTag);
- if (Namespace.TLS.equals(failure.getNamespace())) {
- throw new StateChangingException(Account.State.TLS_ERROR);
- }
final SaslMechanism.Version version;
try {
version = SaslMechanism.Version.of(failure);
@@ -547,6 +551,8 @@ public class XmppConnection implements Runnable {
}
}
throw new StateChangingException(Account.State.UNAUTHORIZED);
+ } else if (nextTag.isStart("continue", Namespace.SASL_2)) {
+ throw new StateChangingException(Account.State.INCOMPATIBLE_CLIENT);
} else if (nextTag.isStart("challenge")) {
final Element challenge = tagReader.readElement(nextTag);
final SaslMechanism.Version version;
@@ -575,12 +581,19 @@ public class XmppConnection implements Runnable {
final Element enabled = tagReader.readElement(nextTag);
if ("true".equals(enabled.getAttribute("resume"))) {
this.streamId = enabled.getAttribute("id");
- Log.d(Config.LOGTAG, account.getJid().asBareJid().toString()
- + ": stream management(" + smVersion
- + ") enabled (resumable)");
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid().toString()
+ + ": stream management("
+ + smVersion
+ + ") enabled (resumable)");
} else {
- Log.d(Config.LOGTAG, account.getJid().asBareJid().toString()
- + ": stream management(" + smVersion + ") enabled");
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid().toString()
+ + ": stream management("
+ + smVersion
+ + ") enabled");
}
this.stanzasReceived = 0;
this.inSmacksSession = true;
@@ -599,11 +612,15 @@ public class XmppConnection implements Runnable {
synchronized (this.mStanzaQueue) {
final int serverCount = Integer.parseInt(h);
if (serverCount < stanzasSent) {
- Log.d(Config.LOGTAG, account.getJid().asBareJid().toString()
- + ": session resumed with lost packages");
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid().toString()
+ + ": session resumed with lost packages");
stanzasSent = serverCount;
} else {
- Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": session resumed");
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid().toString() + ": session resumed");
}
acknowledgedMessages = acknowledgeStanzaUpTo(serverCount);
for (int i = 0; i < this.mStanzaQueue.size(); ++i) {
@@ -618,7 +635,8 @@ public class XmppConnection implements Runnable {
for (AbstractAcknowledgeableStanza packet : failedStanzas) {
if (packet instanceof MessagePacket) {
MessagePacket message = (MessagePacket) packet;
- mXmppConnectionService.markMessage(account,
+ mXmppConnectionService.markMessage(
+ account,
message.getTo().asBareJid(),
message.getId(),
Message.STATUS_UNSEND);
@@ -627,12 +645,20 @@ public class XmppConnection implements Runnable {
}
} catch (final NumberFormatException ignored) {
}
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": online with resource " + account.getResource());
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid()
+ + ": online with resource "
+ + account.getResource());
changeStatus(Account.State.ONLINE);
} else if (nextTag.isStart("r")) {
tagReader.readElement(nextTag);
if (Config.EXTENDED_SM_LOGGING) {
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": acknowledging stanza #" + this.stanzasReceived);
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid()
+ + ": acknowledging stanza #"
+ + this.stanzasReceived);
}
final AckPacket ack = new AckPacket(this.stanzasReceived, smVersion);
tagWriter.writeStanzaAsync(ack);
@@ -642,10 +668,19 @@ public class XmppConnection implements Runnable {
if (mWaitingForSmCatchup.compareAndSet(true, false)) {
final int messageCount = mSmCatchupMessageCounter.get();
final int pendingIQs = packetCallbacks.size();
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": SM catchup complete (messages=" + messageCount + ", pending IQs=" + pendingIQs + ")");
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid()
+ + ": SM catchup complete (messages="
+ + messageCount
+ + ", pending IQs="
+ + pendingIQs
+ + ")");
accountUiNeedsRefresh = true;
if (messageCount > 0) {
- mXmppConnectionService.getNotificationService().finishBacklog(true, account);
+ mXmppConnectionService
+ .getNotificationService()
+ .finishBacklog(true, account);
}
}
}
@@ -664,13 +699,20 @@ public class XmppConnection implements Runnable {
mXmppConnectionService.updateConversationUi();
}
} catch (NumberFormatException | NullPointerException e) {
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": server send ack without sequence number");
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid()
+ + ": server send ack without sequence number");
}
} else if (nextTag.isStart("failed")) {
Element failed = tagReader.readElement(nextTag);
try {
final int serverCount = Integer.parseInt(failed.getAttribute("h"));
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": resumption failed but server acknowledged stanza #" + serverCount);
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid()
+ + ": resumption failed but server acknowledged stanza #"
+ + serverCount);
final boolean acknowledgedMessages;
synchronized (this.mStanzaQueue) {
acknowledgedMessages = acknowledgeStanzaUpTo(serverCount);
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index c51be907e..cd4412588 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -168,6 +168,7 @@
Domain not verifiable
Policy violation
Incompatible server
+ Incompatible client
Stream error
Stream opening error
Unencrypted