explicitly search for namespaces when processing stream features

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/crypto/sasl/SaslMechanism.java |  4 
src/main/java/eu/siacs/conversations/xml/Namespace.java             |  2 
src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java       | 48 
3 files changed, 42 insertions(+), 12 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/xml/Namespace.java 🔗

@@ -7,6 +7,7 @@ public final class Namespace {
     public static final String BLOCKING = "urn:xmpp:blocking";
     public static final String ROSTER = "jabber:iq:roster";
     public static final String REGISTER = "jabber:iq:register";
+    public static final String REGISTER_STREAM_FEATURE = "http://jabber.org/features/iq-register";
     public static final String BYTE_STREAMS = "http://jabber.org/protocol/bytestreams";
     public static final String HTTP_UPLOAD = "urn:xmpp:http:upload:0";
     public static final String HTTP_UPLOAD_LEGACY = "urn:xmpp:http:upload";
@@ -15,6 +16,7 @@ public final class Namespace {
     public static final String DATA = "jabber:x:data";
     public static final String OOB = "jabber:x:oob";
     public static final String SASL = "urn:ietf:params:xml:ns:xmpp-sasl";
+    public static final String SASL_2 = "urn:xmpp:sasl:1";
     public static final String TLS = "urn:ietf:params:xml:ns:xmpp-tls";
     public static final String PUBSUB = "http://jabber.org/protocol/pubsub";
     public static final String PUBSUB_PUBLISH_OPTIONS = PUBSUB + "#publish-options";

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

@@ -848,40 +848,64 @@ public class XmppConnection implements Runnable {
 
     private void processStreamFeatures(final Tag currentTag) throws IOException {
         this.streamFeatures = tagReader.readElement(currentTag);
-        final boolean isSecure = features.encryptionEnabled || Config.ALLOW_NON_TLS_CONNECTIONS || account.isOnion();
+        Log.d(Config.LOGTAG, this.streamFeatures.toString());
+        final boolean isSecure =
+                features.encryptionEnabled || Config.ALLOW_NON_TLS_CONNECTIONS || account.isOnion();
         final boolean needsBinding = !isBound && !account.isOptionSet(Account.OPTION_REGISTER);
-        if (this.streamFeatures.hasChild("starttls") && !features.encryptionEnabled) {
+        if (this.streamFeatures.hasChild("starttls", Namespace.TLS)
+                && !features.encryptionEnabled) {
             sendStartTLS();
-        } else if (this.streamFeatures.hasChild("register") && account.isOptionSet(Account.OPTION_REGISTER)) {
+        } else if (this.streamFeatures.hasChild("register", Namespace.REGISTER_STREAM_FEATURE)
+                && account.isOptionSet(Account.OPTION_REGISTER)) {
             if (isSecure) {
                 register();
             } else {
-                Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": unable to find STARTTLS for registration process " + XmlHelper.printElementNames(this.streamFeatures));
+                Log.d(
+                        Config.LOGTAG,
+                        account.getJid().asBareJid()
+                                + ": unable to find STARTTLS for registration process "
+                                + XmlHelper.printElementNames(this.streamFeatures));
                 throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER);
             }
-        } else if (!this.streamFeatures.hasChild("register") && account.isOptionSet(Account.OPTION_REGISTER)) {
+        } else if (!this.streamFeatures.hasChild("register", Namespace.REGISTER_STREAM_FEATURE)
+                && account.isOptionSet(Account.OPTION_REGISTER)) {
             throw new StateChangingException(Account.State.REGISTRATION_NOT_SUPPORTED);
-        } else if (this.streamFeatures.hasChild("mechanisms") && shouldAuthenticate && isSecure) {
-            authenticate();
-        } else if (this.streamFeatures.hasChild("sm", "urn:xmpp:sm:" + smVersion) && streamId != null) {
+        } else if (this.streamFeatures.hasChild("mechanisms", Namespace.SASL_2)
+                && shouldAuthenticate
+                && isSecure) {
+            authenticate(SaslMechanism.Version.SASL_2);
+        } else if (this.streamFeatures.hasChild("mechanisms", Namespace.SASL)
+                && shouldAuthenticate
+                && isSecure) {
+            authenticate(SaslMechanism.Version.SASL);
+        } else if (this.streamFeatures.hasChild("sm", "urn:xmpp:sm:" + smVersion)
+                && streamId != null) {
             if (Config.EXTENDED_SM_LOGGING) {
-                Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": resuming after stanza #" + stanzasReceived);
+                Log.d(
+                        Config.LOGTAG,
+                        account.getJid().asBareJid()
+                                + ": resuming after stanza #"
+                                + stanzasReceived);
             }
             final ResumePacket resume = new ResumePacket(this.streamId, stanzasReceived, smVersion);
             this.mSmCatchupMessageCounter.set(0);
             this.mWaitingForSmCatchup.set(true);
             this.tagWriter.writeStanzaAsync(resume);
         } else if (needsBinding) {
-            if (this.streamFeatures.hasChild("bind") && isSecure) {
+            if (this.streamFeatures.hasChild("bind", Namespace.BIND) && isSecure) {
                 sendBindRequest();
             } else {
-                Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": unable to find bind feature " + XmlHelper.printElementNames(this.streamFeatures));
+                Log.d(
+                        Config.LOGTAG,
+                        account.getJid().asBareJid()
+                                + ": unable to find bind feature "
+                                + XmlHelper.printElementNames(this.streamFeatures));
                 throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER);
             }
         }
     }
 
-    private void authenticate() throws IOException {
+    private void authenticate(final SaslMechanism.Version version) throws IOException {
         final List<String> mechanisms = extractMechanisms(streamFeatures.findChild("mechanisms"));
         final Element auth = new Element("auth", Namespace.SASL);
         if (mechanisms.contains(External.MECHANISM) && account.getPrivateKeyAlias() != null) {