rudimentary bind 2 implementation
This commit is contained in:
parent
e204457c31
commit
052c58f377
|
@ -313,7 +313,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
|
||||||
private boolean handleErrorMessage(final Account account, final MessagePacket packet) {
|
private boolean handleErrorMessage(final Account account, final MessagePacket packet) {
|
||||||
if (packet.getType() == MessagePacket.TYPE_ERROR) {
|
if (packet.getType() == MessagePacket.TYPE_ERROR) {
|
||||||
if (packet.fromServer(account)) {
|
if (packet.fromServer(account)) {
|
||||||
final Pair<MessagePacket, Long> forwarded = packet.getForwardedMessagePacket("received", "urn:xmpp:carbons:2");
|
final Pair<MessagePacket, Long> forwarded = packet.getForwardedMessagePacket("received", Namespace.CARBONS);
|
||||||
if (forwarded != null) {
|
if (forwarded != null) {
|
||||||
return handleErrorMessage(account, forwarded.first);
|
return handleErrorMessage(account, forwarded.first);
|
||||||
}
|
}
|
||||||
|
@ -389,8 +389,8 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
|
||||||
return;
|
return;
|
||||||
} else if (original.fromServer(account)) {
|
} else if (original.fromServer(account)) {
|
||||||
Pair<MessagePacket, Long> f;
|
Pair<MessagePacket, Long> f;
|
||||||
f = original.getForwardedMessagePacket("received", "urn:xmpp:carbons:2");
|
f = original.getForwardedMessagePacket("received", Namespace.CARBONS);
|
||||||
f = f == null ? original.getForwardedMessagePacket("sent", "urn:xmpp:carbons:2") : f;
|
f = f == null ? original.getForwardedMessagePacket("sent", Namespace.CARBONS) : f;
|
||||||
packet = f != null ? f.first : original;
|
packet = f != null ? f.first : original;
|
||||||
if (handleErrorMessage(account, packet)) {
|
if (handleErrorMessage(account, packet)) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -25,9 +25,10 @@ public final class Namespace {
|
||||||
public static final String NICK = "http://jabber.org/protocol/nick";
|
public static final String NICK = "http://jabber.org/protocol/nick";
|
||||||
public static final String FLEXIBLE_OFFLINE_MESSAGE_RETRIEVAL = "http://jabber.org/protocol/offline";
|
public static final String FLEXIBLE_OFFLINE_MESSAGE_RETRIEVAL = "http://jabber.org/protocol/offline";
|
||||||
public static final String BIND = "urn:ietf:params:xml:ns:xmpp-bind";
|
public static final String BIND = "urn:ietf:params:xml:ns:xmpp-bind";
|
||||||
public static final String BIND2 = "urn:xmpp:bind2:0";
|
public static final String BIND2 = "urn:xmpp:bind2:1";
|
||||||
public static final String STREAM_MANAGEMENT = "urn:xmpp:sm:3";
|
public static final String STREAM_MANAGEMENT = "urn:xmpp:sm:3";
|
||||||
public static final String CSI = "urn:xmpp:csi:0";
|
public static final String CSI = "urn:xmpp:csi:0";
|
||||||
|
public static final String CARBONS = "urn:xmpp:carbons:2";
|
||||||
public static final String BOOKMARKS_CONVERSION = "urn:xmpp:bookmarks-conversion:0";
|
public static final String BOOKMARKS_CONVERSION = "urn:xmpp:bookmarks-conversion:0";
|
||||||
public static final String BOOKMARKS = "storage:bookmarks";
|
public static final String BOOKMARKS = "storage:bookmarks";
|
||||||
public static final String SYNCHRONIZATION = "im.quicksy.synchronization:0";
|
public static final String SYNCHRONIZATION = "im.quicksy.synchronization:0";
|
||||||
|
|
|
@ -13,6 +13,7 @@ import android.util.SparseArray;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
|
import com.google.common.collect.Collections2;
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
|
@ -32,6 +33,7 @@ import java.security.PrivateKey;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -274,7 +276,7 @@ public class XmppConnection implements Runnable {
|
||||||
this.attempt++;
|
this.attempt++;
|
||||||
this.verifiedHostname =
|
this.verifiedHostname =
|
||||||
null; // will be set if user entered hostname is being used or hostname was verified
|
null; // will be set if user entered hostname is being used or hostname was verified
|
||||||
// with dnssec
|
// with dnssec
|
||||||
try {
|
try {
|
||||||
Socket localSocket;
|
Socket localSocket;
|
||||||
shouldAuthenticate = !account.isOptionSet(Account.OPTION_REGISTER);
|
shouldAuthenticate = !account.isOptionSet(Account.OPTION_REGISTER);
|
||||||
|
@ -409,7 +411,7 @@ public class XmppConnection implements Runnable {
|
||||||
if (startXmpp(localSocket)) {
|
if (startXmpp(localSocket)) {
|
||||||
localSocket.setSoTimeout(
|
localSocket.setSoTimeout(
|
||||||
0); // reset to 0; once the connection is established we don’t
|
0); // reset to 0; once the connection is established we don’t
|
||||||
// want this
|
// want this
|
||||||
if (!hardcoded && !result.equals(storedBackupResult)) {
|
if (!hardcoded && !result.equals(storedBackupResult)) {
|
||||||
mXmppConnectionService.databaseBackend.saveResolverResult(
|
mXmppConnectionService.databaseBackend.saveResolverResult(
|
||||||
domain, result);
|
domain, result);
|
||||||
|
@ -615,24 +617,9 @@ public class XmppConnection implements Runnable {
|
||||||
throw new StateChangingException(Account.State.UNAUTHORIZED);
|
throw new StateChangingException(Account.State.UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
tagWriter.writeElement(response);
|
tagWriter.writeElement(response);
|
||||||
} else if (nextTag.isStart("enabled")) {
|
} else if (nextTag.isStart("enabled", Namespace.STREAM_MANAGEMENT)) {
|
||||||
final Element enabled = tagReader.readElement(nextTag);
|
final Element enabled = tagReader.readElement(nextTag);
|
||||||
if (enabled.getAttributeAsBoolean("resume")) {
|
processEnabled(enabled);
|
||||||
this.streamId = enabled.getAttribute("id");
|
|
||||||
Log.d(
|
|
||||||
Config.LOGTAG,
|
|
||||||
account.getJid().asBareJid().toString()
|
|
||||||
+ ": stream management enabled (resumable)");
|
|
||||||
} else {
|
|
||||||
Log.d(
|
|
||||||
Config.LOGTAG,
|
|
||||||
account.getJid().asBareJid().toString()
|
|
||||||
+ ": stream management enabled");
|
|
||||||
}
|
|
||||||
this.stanzasReceived = 0;
|
|
||||||
this.inSmacksSession = true;
|
|
||||||
final RequestPacket r = new RequestPacket();
|
|
||||||
tagWriter.writeStanzaAsync(r);
|
|
||||||
} else if (nextTag.isStart("resumed")) {
|
} else if (nextTag.isStart("resumed")) {
|
||||||
final Element resumed = tagReader.readElement(nextTag);
|
final Element resumed = tagReader.readElement(nextTag);
|
||||||
processResumed(resumed);
|
processResumed(resumed);
|
||||||
|
@ -771,13 +758,31 @@ public class XmppConnection implements Runnable {
|
||||||
+ ": jid changed during SASL 2.0. updating database");
|
+ ": jid changed during SASL 2.0. updating database");
|
||||||
mXmppConnectionService.databaseBackend.updateAccount(account);
|
mXmppConnectionService.databaseBackend.updateAccount(account);
|
||||||
}
|
}
|
||||||
|
final Element bound = success.findChild("bound", Namespace.BIND2);
|
||||||
final Element resumed = success.findChild("resumed", "urn:xmpp:sm:3");
|
final Element resumed = success.findChild("resumed", "urn:xmpp:sm:3");
|
||||||
final Element failed = success.findChild("failed", "urn:xmpp:sm:3");
|
final Element failed = success.findChild("failed", "urn:xmpp:sm:3");
|
||||||
|
// TODO check if resumed and bound exist and throw bind failure
|
||||||
if (resumed != null && streamId != null) {
|
if (resumed != null && streamId != null) {
|
||||||
processResumed(resumed);
|
processResumed(resumed);
|
||||||
} else if (failed != null) {
|
} else if (failed != null) {
|
||||||
processFailed(failed, false); // wait for new stream features
|
processFailed(failed, false); // wait for new stream features
|
||||||
}
|
}
|
||||||
|
if (bound != null) {
|
||||||
|
this.isBound = true;
|
||||||
|
final Element streamManagementEnabled =
|
||||||
|
bound.findChild("enabled", Namespace.STREAM_MANAGEMENT);
|
||||||
|
final Element carbonsEnabled = bound.findChild("enabled", Namespace.CARBONS);
|
||||||
|
if (streamManagementEnabled != null) {
|
||||||
|
processEnabled(streamManagementEnabled);
|
||||||
|
}
|
||||||
|
if (carbonsEnabled != null) {
|
||||||
|
Log.d(
|
||||||
|
Config.LOGTAG,
|
||||||
|
account.getJid().asBareJid() + ": successfully enabled carbons");
|
||||||
|
features.carbonsEnabled = true;
|
||||||
|
}
|
||||||
|
sendPostBindInitialization(streamManagementEnabled != null, carbonsEnabled != null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (version == SaslMechanism.Version.SASL) {
|
if (version == SaslMechanism.Version.SASL) {
|
||||||
tagReader.reset();
|
tagReader.reset();
|
||||||
|
@ -794,6 +799,27 @@ public class XmppConnection implements Runnable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void processEnabled(final Element enabled) {
|
||||||
|
final String streamId;
|
||||||
|
if (enabled.getAttributeAsBoolean("resume")) {
|
||||||
|
streamId = enabled.getAttribute("id");
|
||||||
|
Log.d(
|
||||||
|
Config.LOGTAG,
|
||||||
|
account.getJid().asBareJid().toString()
|
||||||
|
+ ": stream management enabled (resumable)");
|
||||||
|
} else {
|
||||||
|
Log.d(
|
||||||
|
Config.LOGTAG,
|
||||||
|
account.getJid().asBareJid().toString() + ": stream management enabled");
|
||||||
|
streamId = null;
|
||||||
|
}
|
||||||
|
this.streamId = streamId;
|
||||||
|
this.stanzasReceived = 0;
|
||||||
|
this.inSmacksSession = true;
|
||||||
|
final RequestPacket r = new RequestPacket();
|
||||||
|
// tagWriter.writeStanzaAsync(r);
|
||||||
|
}
|
||||||
|
|
||||||
private void processResumed(final Element resumed) throws StateChangingException {
|
private void processResumed(final Element resumed) throws StateChangingException {
|
||||||
this.inSmacksSession = true;
|
this.inSmacksSession = true;
|
||||||
this.isBound = true;
|
this.isBound = true;
|
||||||
|
@ -1241,6 +1267,16 @@ public class XmppConnection implements Runnable {
|
||||||
final boolean inlineStreamManagement =
|
final boolean inlineStreamManagement =
|
||||||
inline != null && inline.hasChild("sm", "urn:xmpp:sm:3");
|
inline != null && inline.hasChild("sm", "urn:xmpp:sm:3");
|
||||||
final boolean inlineBind2 = inline != null && inline.hasChild("bind", Namespace.BIND2);
|
final boolean inlineBind2 = inline != null && inline.hasChild("bind", Namespace.BIND2);
|
||||||
|
final Element inlineBindFeatures =
|
||||||
|
this.streamFeatures.findChild("inline", Namespace.BIND2);
|
||||||
|
if (inlineBind2 && inlineBindFeatures != null) {
|
||||||
|
final Element bind =
|
||||||
|
generateBindRequest(
|
||||||
|
Collections2.transform(
|
||||||
|
inlineBindFeatures.getChildren(),
|
||||||
|
c -> c == null ? null : c.getAttribute("var")));
|
||||||
|
authenticate.addChild(bind);
|
||||||
|
}
|
||||||
if (inlineStreamManagement && streamId != null) {
|
if (inlineStreamManagement && streamId != null) {
|
||||||
final ResumePacket resume = new ResumePacket(this.streamId, stanzasReceived);
|
final ResumePacket resume = new ResumePacket(this.streamId, stanzasReceived);
|
||||||
this.mSmCatchupMessageCounter.set(0);
|
this.mSmCatchupMessageCounter.set(0);
|
||||||
|
@ -1259,9 +1295,26 @@ public class XmppConnection implements Runnable {
|
||||||
+ "/"
|
+ "/"
|
||||||
+ saslMechanism.getMechanism());
|
+ saslMechanism.getMechanism());
|
||||||
authenticate.setAttribute("mechanism", saslMechanism.getMechanism());
|
authenticate.setAttribute("mechanism", saslMechanism.getMechanism());
|
||||||
|
Log.d(Config.LOGTAG, "authenticate " + authenticate);
|
||||||
tagWriter.writeElement(authenticate);
|
tagWriter.writeElement(authenticate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Element generateBindRequest(final Collection<String> bindFeatures) {
|
||||||
|
Log.d(Config.LOGTAG, "inline bind features: " + bindFeatures);
|
||||||
|
final Element bind = new Element("bind", Namespace.BIND2);
|
||||||
|
final Element clientId = bind.addChild("client-id");
|
||||||
|
clientId.setAttribute("tag", mXmppConnectionService.getString(R.string.app_name));
|
||||||
|
clientId.setContent(account.getUuid());
|
||||||
|
final Element features = bind.addChild("features");
|
||||||
|
if (bindFeatures.contains(Namespace.CARBONS)) {
|
||||||
|
features.addChild("enable", Namespace.CARBONS);
|
||||||
|
}
|
||||||
|
if (bindFeatures.contains(Namespace.STREAM_MANAGEMENT)) {
|
||||||
|
features.addChild("enable", Namespace.STREAM_MANAGEMENT);
|
||||||
|
}
|
||||||
|
return bind;
|
||||||
|
}
|
||||||
|
|
||||||
private static List<String> extractMechanisms(final Element stream) {
|
private static List<String> extractMechanisms(final Element stream) {
|
||||||
final ArrayList<String> mechanisms = new ArrayList<>(stream.getChildren().size());
|
final ArrayList<String> mechanisms = new ArrayList<>(stream.getChildren().size());
|
||||||
for (final Element child : stream.getChildren()) {
|
for (final Element child : stream.getChildren()) {
|
||||||
|
@ -1469,7 +1522,8 @@ public class XmppConnection implements Runnable {
|
||||||
.hasChild("optional")) {
|
.hasChild("optional")) {
|
||||||
sendStartSession();
|
sendStartSession();
|
||||||
} else {
|
} else {
|
||||||
sendPostBindInitialization();
|
final boolean waitForDisco = enableStreamManagement();
|
||||||
|
sendPostBindInitialization(waitForDisco, false);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} catch (final IllegalArgumentException e) {
|
} catch (final IllegalArgumentException e) {
|
||||||
|
@ -1565,7 +1619,8 @@ public class XmppConnection implements Runnable {
|
||||||
startSession,
|
startSession,
|
||||||
(account, packet) -> {
|
(account, packet) -> {
|
||||||
if (packet.getType() == IqPacket.TYPE.RESULT) {
|
if (packet.getType() == IqPacket.TYPE.RESULT) {
|
||||||
sendPostBindInitialization();
|
final boolean waitForDisco = enableStreamManagement();
|
||||||
|
sendPostBindInitialization(waitForDisco, false);
|
||||||
} else if (packet.getType() != IqPacket.TYPE.TIMEOUT) {
|
} else if (packet.getType() != IqPacket.TYPE.TIMEOUT) {
|
||||||
throw new StateChangingError(Account.State.SESSION_FAILURE);
|
throw new StateChangingError(Account.State.SESSION_FAILURE);
|
||||||
}
|
}
|
||||||
|
@ -1573,7 +1628,7 @@ public class XmppConnection implements Runnable {
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendPostBindInitialization() {
|
private boolean enableStreamManagement() {
|
||||||
final boolean streamManagement =
|
final boolean streamManagement =
|
||||||
this.streamFeatures.hasChild("sm", Namespace.STREAM_MANAGEMENT);
|
this.streamFeatures.hasChild("sm", Namespace.STREAM_MANAGEMENT);
|
||||||
if (streamManagement) {
|
if (streamManagement) {
|
||||||
|
@ -1583,15 +1638,22 @@ public class XmppConnection implements Runnable {
|
||||||
stanzasSent = 0;
|
stanzasSent = 0;
|
||||||
mStanzaQueue.clear();
|
mStanzaQueue.clear();
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
features.carbonsEnabled = false;
|
}
|
||||||
|
|
||||||
|
private void sendPostBindInitialization(
|
||||||
|
final boolean waitForDisco, final boolean carbonsEnabled) {
|
||||||
|
features.carbonsEnabled = carbonsEnabled;
|
||||||
features.blockListRequested = false;
|
features.blockListRequested = false;
|
||||||
synchronized (this.disco) {
|
synchronized (this.disco) {
|
||||||
this.disco.clear();
|
this.disco.clear();
|
||||||
}
|
}
|
||||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": starting service discovery");
|
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": starting service discovery");
|
||||||
mPendingServiceDiscoveries.set(0);
|
mPendingServiceDiscoveries.set(0);
|
||||||
if (!streamManagement
|
if (!waitForDisco
|
||||||
|| Patches.DISCO_EXCEPTIONS.contains(
|
|| Patches.DISCO_EXCEPTIONS.contains(
|
||||||
account.getJid().getDomain().toEscapedString())) {
|
account.getJid().getDomain().toEscapedString())) {
|
||||||
Log.d(
|
Log.d(
|
||||||
|
@ -1819,11 +1881,11 @@ public class XmppConnection implements Runnable {
|
||||||
|
|
||||||
private void sendEnableCarbons() {
|
private void sendEnableCarbons() {
|
||||||
final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
|
final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
|
||||||
iq.addChild("enable", "urn:xmpp:carbons:2");
|
iq.addChild("enable", Namespace.CARBONS);
|
||||||
this.sendIqPacket(
|
this.sendIqPacket(
|
||||||
iq,
|
iq,
|
||||||
(account, packet) -> {
|
(account, packet) -> {
|
||||||
if (!packet.hasChild("error")) {
|
if (packet.getType() == IqPacket.TYPE.RESULT) {
|
||||||
Log.d(
|
Log.d(
|
||||||
Config.LOGTAG,
|
Config.LOGTAG,
|
||||||
account.getJid().asBareJid() + ": successfully enabled carbons");
|
account.getJid().asBareJid() + ": successfully enabled carbons");
|
||||||
|
@ -2309,7 +2371,7 @@ public class XmppConnection implements Runnable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean carbons() {
|
public boolean carbons() {
|
||||||
return hasDiscoFeature(account.getDomain(), "urn:xmpp:carbons:2");
|
return hasDiscoFeature(account.getDomain(), Namespace.CARBONS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean commands() {
|
public boolean commands() {
|
||||||
|
|
Loading…
Reference in a new issue