aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/de/thedevstack/conversationsplus/xmpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/de/thedevstack/conversationsplus/xmpp')
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/XmppConnection.java143
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");
}
}