diff options
Diffstat (limited to 'src/main/java/de/thedevstack/conversationsplus/xmpp')
-rw-r--r-- | src/main/java/de/thedevstack/conversationsplus/xmpp/XmppConnection.java | 143 |
1 files changed, 84 insertions, 59 deletions
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/XmppConnection.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/XmppConnection.java index 8f0ca78b..a89f308f 100644 --- a/src/main/java/de/thedevstack/conversationsplus/xmpp/XmppConnection.java +++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/XmppConnection.java @@ -37,6 +37,8 @@ import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.TreeSet; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.KeyManager; @@ -114,7 +116,9 @@ public class XmppConnection implements Runnable { private long lastConnect = 0; private long lastSessionStarted = 0; private long lastDiscoStarted = 0; - private int mPendingServiceDiscoveries = 0; + private AtomicInteger mPendingServiceDiscoveries = new AtomicInteger(0); + private AtomicBoolean mIsServiceItemsDiscoveryPending = new AtomicBoolean(true); + private boolean mWaitForDisco = true; private final ArrayList<String> mPendingServiceDiscoveriesIds = new ArrayList<>(); private boolean mInteractive = false; private int attempt = 0; @@ -526,7 +530,6 @@ public class XmppConnection implements Runnable { } nextTag = tagReader.readTag(); } - throw new IOException("reached end of stream. last tag was "+nextTag); } private void acknowledgeStanzaUpTo(int serverCount) { @@ -712,42 +715,7 @@ public class XmppConnection implements Runnable { } else if (this.streamFeatures.hasChild("mechanisms") && shouldAuthenticate && (features.encryptionEnabled || Config.ALLOW_NON_TLS_CONNECTIONS)) { - final List<String> mechanisms = extractMechanisms(streamFeatures - .findChild("mechanisms")); - final Element auth = new Element("auth"); - auth.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl"); - if (mechanisms.contains("EXTERNAL") && account.getPrivateKeyAlias() != null) { - saslMechanism = new External(tagWriter, account, mXmppConnectionService.getRNG()); - } else if (mechanisms.contains("SCRAM-SHA-1")) { - saslMechanism = new ScramSha1(tagWriter, account, mXmppConnectionService.getRNG()); - } else if (mechanisms.contains("PLAIN")) { - saslMechanism = new Plain(tagWriter, account); - } else if (mechanisms.contains("DIGEST-MD5")) { - saslMechanism = new DigestMd5(tagWriter, account, mXmppConnectionService.getRNG()); - } - if (saslMechanism != null) { - final JSONObject keys = account.getKeys(); - try { - if (keys.has(Account.PINNED_MECHANISM_KEY) && - keys.getInt(Account.PINNED_MECHANISM_KEY) > saslMechanism.getPriority()) { - Logging.e(Config.LOGTAG, "Auth failed. Authentication mechanism " + saslMechanism.getMechanism() + - " has lower priority (" + String.valueOf(saslMechanism.getPriority()) + - ") than pinned priority (" + keys.getInt(Account.PINNED_MECHANISM_KEY) + - "). Possible downgrade attack?"); - throw new SecurityException(); - } - } catch (final JSONException e) { - Logging.d(Config.LOGTAG, "Parse error while checking pinned auth mechanism"); - } - Logging.d(Config.LOGTAG, account.getJid().toString() + ": Authenticating with " + saslMechanism.getMechanism()); - auth.setAttribute("mechanism", saslMechanism.getMechanism()); - if (!saslMechanism.getClientFirstMessage().isEmpty()) { - auth.setContent(saslMechanism.getClientFirstMessage()); - } - tagWriter.writeElement(auth); - } else { - throw new IncompatibleServerException(); - } + authenticate(); } else if (this.streamFeatures.hasChild("sm", "urn:xmpp:sm:" + smVersion) && streamId != null) { if (Config.EXTENDED_SM_LOGGING) { Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": resuming after stanza #"+stanzasReceived); @@ -763,6 +731,45 @@ public class XmppConnection implements Runnable { } } + private void authenticate() throws IOException { + final List<String> mechanisms = extractMechanisms(streamFeatures + .findChild("mechanisms")); + final Element auth = new Element("auth"); + auth.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl"); + if (mechanisms.contains("EXTERNAL") && account.getPrivateKeyAlias() != null) { + saslMechanism = new External(tagWriter, account, mXmppConnectionService.getRNG()); + } else if (mechanisms.contains("SCRAM-SHA-1")) { + saslMechanism = new ScramSha1(tagWriter, account, mXmppConnectionService.getRNG()); + } else if (mechanisms.contains("PLAIN")) { + saslMechanism = new Plain(tagWriter, account); + } else if (mechanisms.contains("DIGEST-MD5")) { + saslMechanism = new DigestMd5(tagWriter, account, mXmppConnectionService.getRNG()); + } + if (saslMechanism != null) { + final JSONObject keys = account.getKeys(); + try { + if (keys.has(Account.PINNED_MECHANISM_KEY) && + keys.getInt(Account.PINNED_MECHANISM_KEY) > saslMechanism.getPriority()) { + Logging.e(Config.LOGTAG, "Auth failed. Authentication mechanism " + saslMechanism.getMechanism() + + " has lower priority (" + String.valueOf(saslMechanism.getPriority()) + + ") than pinned priority (" + keys.getInt(Account.PINNED_MECHANISM_KEY) + + "). Possible downgrade attack?"); + throw new SecurityException(); + } + } catch (final JSONException e) { + Logging.d(Config.LOGTAG, "Parse error while checking pinned auth mechanism"); + } + Logging.d(Config.LOGTAG, account.getJid().toString() + ": Authenticating with " + saslMechanism.getMechanism()); + auth.setAttribute("mechanism", saslMechanism.getMechanism()); + if (!saslMechanism.getClientFirstMessage().isEmpty()) { + auth.setContent(saslMechanism.getClientFirstMessage()); + } + tagWriter.writeElement(auth); + } else { + throw new IncompatibleServerException(); + } + } + private List<String> extractMechanisms(final Element stream) { final ArrayList<String> mechanisms = new ArrayList<>(stream .getChildren().size()); @@ -980,11 +987,12 @@ public class XmppConnection implements Runnable { synchronized (this.disco) { this.disco.clear(); } - mPendingServiceDiscoveries = mServerIdentity == Identity.NIMBUZZ ? 1 : 0; + mPendingServiceDiscoveries.set(0); + mIsServiceItemsDiscoveryPending.set(true); + mWaitForDisco = mServerIdentity != Identity.NIMBUZZ; lastDiscoStarted = SystemClock.elapsedRealtime(); Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": starting service discovery"); mXmppConnectionService.scheduleWakeUpCall(Config.CONNECT_DISCO_TIMEOUT, account.getUuid().hashCode()); - sendServiceDiscoveryItems(account.getServer()); Element caps = streamFeatures.findChild("c"); final String hash = caps == null ? null : caps.getAttribute("hash"); final String ver = caps == null ? null : caps.getAttribute("ver"); @@ -999,13 +1007,15 @@ public class XmppConnection implements Runnable { disco.put(account.getServer(), discoveryResult); } sendServiceDiscoveryInfo(account.getJid().toBareJid()); + sendServiceDiscoveryItems(account.getServer()); + if (!mWaitForDisco) { + finalizeBind(); + } this.lastSessionStarted = SystemClock.elapsedRealtime(); } private void sendServiceDiscoveryInfo(final Jid jid) { - if (mServerIdentity != Identity.NIMBUZZ) { - mPendingServiceDiscoveries++; - } + mPendingServiceDiscoveries.incrementAndGet(); final IqPacket iq = new IqPacket(IqPacket.TYPE.GET); iq.setTo(jid); iq.query("http://jabber.org/protocol/disco#info"); @@ -1049,14 +1059,10 @@ public class XmppConnection implements Runnable { Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not query disco info for " + jid.toString()); } if (packet.getType() != IqPacket.TYPE.TIMEOUT) { - mPendingServiceDiscoveries--; - if (mPendingServiceDiscoveries == 0) { - Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": done with service discovery"); - Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": online with resource " + account.getResource()); - if (bindListener != null) { - bindListener.onBind(account); - } - changeStatus(Account.State.ONLINE); + if (mPendingServiceDiscoveries.decrementAndGet() == 0 + && !mIsServiceItemsDiscoveryPending.get() + && mWaitForDisco) { + finalizeBind(); } } } @@ -1066,6 +1072,14 @@ public class XmppConnection implements Runnable { } } + private void finalizeBind() { + Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": online with resource " + account.getResource()); + if (bindListener != null) { + bindListener.onBind(account); + } + changeStatus(Account.State.ONLINE); + } + private void enableAdvancedStreamFeatures() { if (getFeatures().carbons() && !features.carbonsEnabled) { sendEnableCarbons(); @@ -1083,7 +1097,7 @@ public class XmppConnection implements Runnable { final IqPacket iq = new IqPacket(IqPacket.TYPE.GET); iq.setTo(server.toDomainJid()); iq.query("http://jabber.org/protocol/disco#items"); - this.sendIqPacket(iq, new OnIqPacketReceived() { + String id = this.sendIqPacket(iq, new OnIqPacketReceived() { @Override public void onIqPacketReceived(final Account account, final IqPacket packet) { @@ -1100,8 +1114,17 @@ public class XmppConnection implements Runnable { } else { Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not query disco items of " + server); } + if (packet.getType() != IqPacket.TYPE.TIMEOUT) { + mIsServiceItemsDiscoveryPending.set(false); + if (mPendingServiceDiscoveries.get() == 0 && mWaitForDisco) { + finalizeBind(); + } + } } }); + synchronized (this.mPendingServiceDiscoveriesIds) { + this.mPendingServiceDiscoveriesIds.add(id); + } } private void sendEnableCarbons() { @@ -1482,13 +1505,15 @@ public class XmppConnection implements Runnable { public boolean pep() { synchronized (XmppConnection.this.disco) { - ServiceDiscoveryResult info = disco.get(account.getServer()); - if (info != null && info.hasIdentity("pubsub", "pep")) { - return true; - } else { - info = disco.get(account.getJid().toBareJid()); - return info != null && info.hasIdentity("pubsub", "pep"); - } + ServiceDiscoveryResult info = disco.get(account.getJid().toBareJid()); + return info != null && info.hasIdentity("pubsub", "pep"); + } + } + + public boolean pepPersistent() { + synchronized (XmppConnection.this.disco) { + ServiceDiscoveryResult info = disco.get(account.getJid().toBareJid()); + return info != null && info.getFeatures().contains("http://jabber.org/protocol/pubsub#persistent-items"); } } |