summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/com/isode/stroke/client/Client.java4
-rw-r--r--src/com/isode/stroke/client/ClientOptions.java36
-rw-r--r--src/com/isode/stroke/client/ClientSession.java22
-rw-r--r--src/com/isode/stroke/client/CoreClient.java137
-rw-r--r--src/com/isode/stroke/examples/ConnectDisconnect.java2
-rw-r--r--src/com/isode/stroke/examples/gui/StrokeGUI.java2
-rw-r--r--src/com/isode/stroke/network/Connector.java74
-rw-r--r--src/com/isode/stroke/session/Session.java2
-rw-r--r--test/com/isode/stroke/parser/XMLParserTest.java2
-rw-r--r--test/com/isode/stroke/parser/payloadparsers/PayloadsParserTester.java2
10 files changed, 190 insertions, 93 deletions
diff --git a/src/com/isode/stroke/client/Client.java b/src/com/isode/stroke/client/Client.java
index 69c893f..741597c 100644
--- a/src/com/isode/stroke/client/Client.java
+++ b/src/com/isode/stroke/client/Client.java
@@ -46,8 +46,8 @@ public class Client extends CoreClient {
* @param networkFactories An implementation of network interaction, must
* not be null.
*/
- public Client(EventLoop eventLoop, JID jid, String password, NetworkFactories networkFactories) {
- super(eventLoop,jid, password, networkFactories);
+ public Client(JID jid, String password, NetworkFactories networkFactories) {
+ super(jid, password, networkFactories);
stanzaChannelPresenceSender = new StanzaChannelPresenceSender(getStanzaChannel());
directedPresenceSender = new DirectedPresenceSender(stanzaChannelPresenceSender);
diff --git a/src/com/isode/stroke/client/ClientOptions.java b/src/com/isode/stroke/client/ClientOptions.java
index 7a7e47e..4090d1f 100644
--- a/src/com/isode/stroke/client/ClientOptions.java
+++ b/src/com/isode/stroke/client/ClientOptions.java
@@ -16,13 +16,18 @@ public class ClientOptions {
public enum UseTLS {
NeverUseTLS,
- UseTLSWhenAvailable
+ UseTLSWhenAvailable,
+ RequireTLS
}
public ClientOptions() {
useStreamCompression = true;
useTLS = UseTLS.UseTLSWhenAvailable;
useStreamResumption = false;
+ allowPLAINWithoutTLS = false;
+ useAcks = true;
+ manualHostname = "";
+ manualPort = -1;
}
@Override
@@ -46,10 +51,37 @@ public class ClientOptions {
*/
public UseTLS useTLS;
/**
- * Use XEP-196 stream resumption when available.
+ * Sets whether plaintext authentication is
+ * allowed over non-TLS-encrypted connections.
+ *
+ * Default: false
+ */
+ public boolean allowPLAINWithoutTLS;
+ /**
+ * Use XEP-198 stream resumption when available.
*
* Default: false
*/
public boolean useStreamResumption;
+ /**
+ * Use XEP-0198 acks in the stream when available.
+ * Default: true
+ */
+ public boolean useAcks;
+
+ /**
+ * The hostname to connect to.
+ * Leave this empty for standard XMPP connection, based on the JID domain.
+ */
+ public String manualHostname;
+
+ /**
+ * The port to connect to.
+ * Leave this to -1 to use the port discovered by SRV lookups, and 5222 as a
+ * fallback.
+ */
+ public int manualPort;
+
+
}
diff --git a/src/com/isode/stroke/client/ClientSession.java b/src/com/isode/stroke/client/ClientSession.java
index 78594b0..b0afb7b 100644
--- a/src/com/isode/stroke/client/ClientSession.java
+++ b/src/com/isode/stroke/client/ClientSession.java
@@ -104,14 +104,18 @@ public class ClientSession {
public enum UseTLS {
NeverUseTLS,
- UseTLSWhenAvailable
+ UseTLSWhenAvailable,
+ RequireTLS
}
private ClientSession(JID jid, SessionStream stream) {
localJID = jid;
state = State.Initial;
this.stream = stream;
- allowPLAINOverNonTLS = true; /* FIXME: false */
+ allowPLAINOverNonTLS = false;
+ useStreamCompression = true;
+ useTLS = UseTLS.UseTLSWhenAvailable;
+ useAcks = true;
needSessionStart = false;
needResourceBind = false;
needAcking = false;
@@ -138,6 +142,10 @@ public class ClientSession {
useTLS = use;
}
+ public void setUseAcks(boolean use) {
+ useAcks = use;
+ }
+
public boolean getStreamManagementEnabled() {
return stanzaAckRequester_ != null;
}
@@ -291,11 +299,14 @@ public class ClientSession {
return;
}
- if (streamFeatures.hasStartTLS() && stream.supportsTLSEncryption()) {
+ if (streamFeatures.hasStartTLS() && stream.supportsTLSEncryption() && !UseTLS.NeverUseTLS.equals(useTLS)) {
state = State.WaitingForEncrypt;
stream.writeElement(new StartTLSRequest());
}
- else if (false && streamFeatures.hasCompressionMethod("zlib")) { /*FIXME: test and enable!*/
+ else if (UseTLS.RequireTLS.equals(useTLS) && !stream.isTLSEncrypted()) {
+ finishSession(Error.Type.NoSupportedAuthMechanismsError);
+ }
+ else if (false && useStreamCompression && streamFeatures.hasCompressionMethod("zlib")) { /*FIXME: test and enable!*/
state = State.Compressing;
stream.writeElement(new CompressRequest("zlib"));
}
@@ -344,7 +355,7 @@ public class ClientSession {
stream.setWhitespacePingEnabled(true);
needSessionStart = streamFeatures.hasSession();
needResourceBind = streamFeatures.hasResourceBind();
- needAcking = streamFeatures.hasStreamManagement();
+ needAcking = streamFeatures.hasStreamManagement() && useAcks;
if (!needResourceBind) {
// Resource binding is a MUST
finishSession(Error.Type.ResourceBindError);
@@ -605,6 +616,7 @@ public class ClientSession {
private boolean allowPLAINOverNonTLS;
private boolean useStreamCompression;
private UseTLS useTLS;
+ private boolean useAcks;
private boolean needSessionStart;
private boolean needResourceBind;
private boolean needAcking;
diff --git a/src/com/isode/stroke/client/CoreClient.java b/src/com/isode/stroke/client/CoreClient.java
index 4326d44..55284d6 100644
--- a/src/com/isode/stroke/client/CoreClient.java
+++ b/src/com/isode/stroke/client/CoreClient.java
@@ -9,6 +9,7 @@
package com.isode.stroke.client;
import com.isode.stroke.base.NotNull;
+import com.isode.stroke.client.ClientSession.UseTLS;
import com.isode.stroke.elements.Message;
import com.isode.stroke.elements.Presence;
import com.isode.stroke.elements.Stanza;
@@ -18,6 +19,7 @@ import com.isode.stroke.jid.JID;
import com.isode.stroke.network.Connection;
import com.isode.stroke.network.ConnectionFactory;
import com.isode.stroke.network.Connector;
+import com.isode.stroke.network.DomainNameResolveError;
import com.isode.stroke.network.NetworkFactories;
import com.isode.stroke.parser.payloadparsers.FullPayloadParserFactoryCollection;
import com.isode.stroke.queries.IQRouter;
@@ -29,6 +31,7 @@ import com.isode.stroke.signals.Signal1;
import com.isode.stroke.signals.SignalConnection;
import com.isode.stroke.signals.Slot;
import com.isode.stroke.signals.Slot1;
+import com.isode.stroke.signals.Slot2;
import com.isode.stroke.tls.Certificate;
import com.isode.stroke.tls.CertificateTrustChecker;
import com.isode.stroke.tls.CertificateVerificationError;
@@ -119,47 +122,76 @@ public class CoreClient {
delete stanzaChannel_;
}*/
+
/**
* Connect using the standard XMPP connection rules (i.e. SRV then A/AAAA).
*
* @param o Client options to use in the connection, must not be null
*/
public void connect(ClientOptions o) {
- options = o;
- connect(jid_.getDomain(), 5222);
- }
-
- /**
- * Connect to the specified host, overriding the standard lookup rules for the JID.
- *
- * Internal method, do not use.
- *
- * @param host Host to connect to, non-null.
- * @param port Default port to use if SRV fails.
- */
- public void connect(String host, int port) {
- options = new ClientOptions();
+ forceReset();
disconnectRequested_ = false;
assert (connector_ == null);
/* FIXME: Port Proxies */
- connector_ = Connector.create(host, networkFactories.getDomainNameResolver(), networkFactories.getConnectionFactory(), networkFactories.getTimerFactory(), port);
- connectorConnectFinishedConnection_ = connector_.onConnectFinished.connect(new Slot1<Connection>() {
- public void call(Connection p1) {
- handleConnectorFinished(p1);
+ String host = o.manualHostname.isEmpty() ? jid_.getDomain() : o.manualHostname;
+ int port = o.manualPort;
+ connector_ = Connector.create(host, port, o.manualHostname.isEmpty(), networkFactories.getDomainNameResolver(), networkFactories.getConnectionFactory(), networkFactories.getTimerFactory());
+ connectorConnectFinishedConnection_ = connector_.onConnectFinished.connect(new Slot2<Connection, com.isode.stroke.base.Error>() {
+ public void call(Connection p1, com.isode.stroke.base.Error p2) {
+ handleConnectorFinished(p1, p2);
}
});
connector_.setTimeoutMilliseconds(60 * 1000);
connector_.start();
}
- void handleConnectorFinished(Connection connection) {
- if (connectorConnectFinishedConnection_ != null) {
- connectorConnectFinishedConnection_.disconnect();
+ private void bindSessionToStream() {
+ session_ = ClientSession.create(jid_, sessionStream_);
+ session_.setCertificateTrustChecker(certificateTrustChecker);
+ session_.setUseStreamCompression(options.useStreamCompression);
+ session_.setAllowPLAINOverNonTLS(options.allowPLAINWithoutTLS);
+ switch (options.useTLS) {
+ case UseTLSWhenAvailable:
+ session_.setUseTLS(ClientSession.UseTLS.UseTLSWhenAvailable);
+ session_.setCertificateTrustChecker(certificateTrustChecker);
+ break;
+ case NeverUseTLS:
+ session_.setUseTLS(ClientSession.UseTLS.NeverUseTLS);
+ break;
+ case RequireTLS:
+ session_.setUseTLS(ClientSession.UseTLS.RequireTLS);
+ break;
}
+ session_.setUseAcks(options.useAcks);
+ stanzaChannel_.setSession(session_);
+ sessionFinishedConnection_ = session_.onFinished.connect(new Slot1<com.isode.stroke.base.Error>() {
- connector_ = null;
+ public void call(com.isode.stroke.base.Error p1) {
+ handleSessionFinished(p1);
+ }
+ });
+ sessionNeedCredentialsConnection_ = session_.onNeedCredentials.connect(new Slot() {
+
+ public void call() {
+ handleNeedCredentials();
+ }
+ });
+ session_.start();
+ }
+
+ void handleConnectorFinished(Connection connection, com.isode.stroke.base.Error error) {
+ resetConnector();
+
if (connection == null) {
- onDisconnected.emit(disconnectRequested_ ? null : new ClientError(ClientError.Type.ConnectionError));
+ ClientError clientError = null;
+ if (!disconnectRequested_) {
+ if (error instanceof DomainNameResolveError) {
+ clientError = new ClientError(ClientError.Type.DomainNameResolveError);
+ } else {
+ clientError = new ClientError(ClientError.Type.ConnectionError);
+ }
+ }
+ onDisconnected.emit(clientError);
} else {
assert (connection_ == null);
connection_ = connection;
@@ -183,35 +215,12 @@ public class CoreClient {
}
});
- session_ = ClientSession.create(jid_, sessionStream_);
- session_.setCertificateTrustChecker(certificateTrustChecker);
- session_.setUseStreamCompression(options.useStreamCompression);
- switch (options.useTLS) {
- case UseTLSWhenAvailable:
- session_.setUseTLS(ClientSession.UseTLS.UseTLSWhenAvailable);
- session_.setCertificateTrustChecker(certificateTrustChecker);
- break;
- case NeverUseTLS:
- session_.setUseTLS(ClientSession.UseTLS.NeverUseTLS);
- break;
- }
- stanzaChannel_.setSession(session_);
- sessionFinishedConnection_ = session_.onFinished.connect(new Slot1<com.isode.stroke.base.Error>() {
-
- public void call(com.isode.stroke.base.Error p1) {
- handleSessionFinished(p1);
- }
- });
- sessionNeedCredentialsConnection_ = session_.onNeedCredentials.connect(new Slot() {
-
- public void call() {
- handleNeedCredentials();
- }
- });
- session_.start();
+ bindSessionToStream();
}
}
+
+
/**
* Close the stream and disconnect from the server.
*/
@@ -419,6 +428,26 @@ public class CoreClient {
return (isSessionTLSEncrypted() ? sessionStream_.getPeerCertificate() : null);
}
+ private void resetConnector() {
+ if (connectorConnectFinishedConnection_ != null) {
+ connectorConnectFinishedConnection_.disconnect();
+ }
+ connector_ = null;
+ }
+
+ private void resetSession() {
+ session_.onFinished.disconnectAll();
+ session_.onNeedCredentials.disconnectAll();
+
+ sessionStream_.onDataRead.disconnectAll();
+ sessionStream_.onDataWritten.disconnectAll();
+
+ if (connection_ != null) {
+ connection_.disconnect();
+ }
+ sessionStream_ = null;
+ connection_ = null;
+ }
/**
* @return JID of the client, will never be null. After the session was
@@ -433,6 +462,16 @@ public class CoreClient {
return jid_;
}
}
+
+ private void forceReset() {
+ if (connector_ != null) {
+ resetConnector();
+ }
+ if (sessionStream_ != null || connection_ != null) {
+ resetSession();
+ }
+
+ }
@Override
public String toString()
diff --git a/src/com/isode/stroke/examples/ConnectDisconnect.java b/src/com/isode/stroke/examples/ConnectDisconnect.java
index 5181fcc..6b4cbc4 100644
--- a/src/com/isode/stroke/examples/ConnectDisconnect.java
+++ b/src/com/isode/stroke/examples/ConnectDisconnect.java
@@ -70,7 +70,7 @@ public class ConnectDisconnect {
DummyEventLoop eventLoop = new DummyEventLoop();
JavaNetworkFactories factories = new JavaNetworkFactories(eventLoop);
- client = new CoreClient(eventLoop, jid, password, factories);
+ client = new CoreClient(jid, password, factories);
client.onConnected.connect(new Slot() {
public void call() {
diff --git a/src/com/isode/stroke/examples/gui/StrokeGUI.java b/src/com/isode/stroke/examples/gui/StrokeGUI.java
index e8580fc..282fde6 100644
--- a/src/com/isode/stroke/examples/gui/StrokeGUI.java
+++ b/src/com/isode/stroke/examples/gui/StrokeGUI.java
@@ -157,7 +157,7 @@ public class StrokeGUI extends javax.swing.JFrame {
}
};
- client_ = new CoreClient(eventLoop, JID.fromString(loginJID_.getText()), loginPassword_.getText(), new JavaNetworkFactories(eventLoop));
+ client_ = new CoreClient(JID.fromString(loginJID_.getText()), loginPassword_.getText(), new JavaNetworkFactories(eventLoop));
System.out.println("Connecting");
try {
client_.connect(new ClientOptions());
diff --git a/src/com/isode/stroke/network/Connector.java b/src/com/isode/stroke/network/Connector.java
index fba0b95..b1ee73c 100644
--- a/src/com/isode/stroke/network/Connector.java
+++ b/src/com/isode/stroke/network/Connector.java
@@ -10,6 +10,7 @@ package com.isode.stroke.network;
import com.isode.stroke.network.DomainNameServiceQuery.Result;
import com.isode.stroke.signals.Signal1;
+import com.isode.stroke.signals.Signal2;
import com.isode.stroke.signals.SignalConnection;
import com.isode.stroke.signals.Slot;
import com.isode.stroke.signals.Slot1;
@@ -19,8 +20,8 @@ import java.util.Collection;
public class Connector {
- public static Connector create(String hostname, DomainNameResolver resolver, ConnectionFactory connectionFactory, TimerFactory timerFactory, int defaultPort) {
- return new Connector(hostname, resolver, connectionFactory, timerFactory, defaultPort);
+ public static Connector create(String hostname, int port, boolean doServiceLookups, DomainNameResolver resolver, ConnectionFactory connectionFactory, TimerFactory timerFactory) {
+ return new Connector(hostname, port, doServiceLookups, resolver, connectionFactory, timerFactory);
}
public void setTimeoutMilliseconds(int milliseconds) {
@@ -32,42 +33,51 @@ public class Connector {
assert serviceQuery == null;
assert timer == null;
queriedAllServices = false;
- serviceQuery = resolver.createServiceQuery("_xmpp-client._tcp." + hostname);
- serviceQuery.onResult.connect(new Slot1<Collection<DomainNameServiceQuery.Result>>() {
- public void call(Collection<Result> p1) {
- handleServiceQueryResult(p1);
- }
- });
- if (timeoutMilliseconds > 0) {
- timer = timerFactory.createTimer(timeoutMilliseconds);
- timer.onTick.connect(new Slot() {
- public void call() {
- handleTimeout();
+ if (doServiceLookups) {
+ serviceQuery = resolver.createServiceQuery("_xmpp-client._tcp." + hostname);
+ serviceQuery.onResult.connect(new Slot1<Collection<DomainNameServiceQuery.Result>>() {
+ public void call(Collection<Result> p1) {
+ handleServiceQueryResult(p1);
}
});
- timer.start();
+ if (timeoutMilliseconds > 0) {
+ timer = timerFactory.createTimer(timeoutMilliseconds);
+ timer.onTick.connect(new Slot() {
+ public void call() {
+ handleTimeout();
+ }
+ });
+ timer.start();
+ }
+ serviceQuery.run();
+ }
+ else {
+ queryAddress(hostname);
}
- serviceQuery.run();
}
public void stop() {
finish(null);
}
- public final Signal1<Connection> onConnectFinished = new Signal1<Connection>();
+ public final Signal2<Connection, com.isode.stroke.base.Error> onConnectFinished = new Signal2<Connection, com.isode.stroke.base.Error>();
- private Connector(String hostname, DomainNameResolver resolver, ConnectionFactory connectionFactory, TimerFactory timerFactory, int defaultPort) {
+ private Connector(String hostname,int port, boolean doServiceLookups, DomainNameResolver resolver, ConnectionFactory connectionFactory, TimerFactory timerFactory) {
this.hostname = hostname;
this.resolver = resolver;
this.connectionFactory = connectionFactory;
this.timerFactory = timerFactory;
- this.defaultPort = defaultPort;
+ this.port = port;
+ this.doServiceLookups = doServiceLookups;
}
private void handleServiceQueryResult(Collection<Result> result) {
serviceQueryResults = new ArrayList<Result>();
serviceQueryResults.addAll(result);
serviceQuery = null;
+ if (!serviceQueryResults.isEmpty()) {
+ foundSomeDNS = true;
+ }
tryNextServiceOrFallback();
}
@@ -75,15 +85,16 @@ public class Connector {
//std::cout << "Connector::handleAddressQueryResult(): Start" << std::endl;
addressQuery = null;
if (error != null || addresses.isEmpty()) {
- if (!serviceQueryResults.isEmpty()) {
- serviceQueryResults.remove(0);
- }
- tryNextServiceOrFallback();
+ if (!serviceQueryResults.isEmpty()) {
+ serviceQueryResults.remove(0);
+ }
+ tryNextServiceOrFallback();
}
else {
- addressQueryResults = new ArrayList<HostAddress>();
- addressQueryResults.addAll(addresses);
- tryNextAddress();
+ foundSomeDNS = true;
+ addressQueryResults = new ArrayList<HostAddress>();
+ addressQueryResults.addAll(addresses);
+ tryNextAddress();
}
}
@@ -129,12 +140,12 @@ public class Connector {
HostAddress address = addressQueryResults.get(0);
addressQueryResults.remove(0);
- int port = defaultPort;
+ int connectPort = (port == -1) ? 5222 : port;
if (!serviceQueryResults.isEmpty()) {
- port = serviceQueryResults.get(0).port;
+ connectPort = serviceQueryResults.get(0).port;
}
- tryConnect(new HostAddressPort(address, port));
+ tryConnect(new HostAddressPort(address, connectPort));
}
}
@@ -189,7 +200,8 @@ public class Connector {
currentConnectionConnectFinishedConnection.disconnect();
currentConnection = null;
}
- onConnectFinished.emit(connection);
+
+ onConnectFinished.emit(connection, (connection != null || foundSomeDNS) ? null : new DomainNameResolveError());
}
private void handleTimeout() {
@@ -216,5 +228,7 @@ public class Connector {
private boolean queriedAllServices = true;
private Connection currentConnection;
private SignalConnection currentConnectionConnectFinishedConnection;
- private final int defaultPort;
+ private final int port;
+ private final boolean doServiceLookups;
+ private boolean foundSomeDNS = false;
}
diff --git a/src/com/isode/stroke/session/Session.java b/src/com/isode/stroke/session/Session.java
index e57dc84..eb64051 100644
--- a/src/com/isode/stroke/session/Session.java
+++ b/src/com/isode/stroke/session/Session.java
@@ -111,7 +111,7 @@ public abstract class Session {
protected abstract void handleStreamStart(ProtocolHeader header);
protected void initializeStreamStack() {
- xmppLayer = new XMPPLayer(payloadParserFactories, payloadSerializers, StreamType.ClientStreamType, eventLoop);
+ xmppLayer = new XMPPLayer(payloadParserFactories, payloadSerializers, StreamType.ClientStreamType);
xmppLayer.onStreamStart.connect(new Slot1<ProtocolHeader>() {
public void call(ProtocolHeader header) {
diff --git a/test/com/isode/stroke/parser/XMLParserTest.java b/test/com/isode/stroke/parser/XMLParserTest.java
index a1b5d1c..d9406bd 100644
--- a/test/com/isode/stroke/parser/XMLParserTest.java
+++ b/test/com/isode/stroke/parser/XMLParserTest.java
@@ -27,7 +27,7 @@ public class XMLParserTest {
}
private XMLParser parser() {
- return PlatformXMLParserFactory.createXMLParser(client_, new SimpleEventLoop());
+ return PlatformXMLParserFactory.createXMLParser(client_);
}
private void join(XMLParser parser) {
diff --git a/test/com/isode/stroke/parser/payloadparsers/PayloadsParserTester.java b/test/com/isode/stroke/parser/payloadparsers/PayloadsParserTester.java
index d5614fb..429892e 100644
--- a/test/com/isode/stroke/parser/payloadparsers/PayloadsParserTester.java
+++ b/test/com/isode/stroke/parser/payloadparsers/PayloadsParserTester.java
@@ -26,7 +26,7 @@ public class PayloadsParserTester implements XMLParserClient {
public PayloadsParserTester(EventLoop eventLoop) {
level = 0;
- xmlParser = PlatformXMLParserFactory.createXMLParser(this, eventLoop);
+ xmlParser = PlatformXMLParserFactory.createXMLParser(this);
}
public boolean parse(String data) {