From 816655ed98893382b82569ccbbebdb04c4af9127 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Fri, 21 Sep 2012 19:37:25 +0100 Subject: Update some interfaces for consistency with Swiften. Makes ClientOptions do more. 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() { - 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() { + 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() { - 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() { - - 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>() { - public void call(Collection 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>() { + public void call(Collection 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 onConnectFinished = new Signal1(); + public final Signal2 onConnectFinished = new Signal2(); - 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) { serviceQueryResults = new ArrayList(); 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(); - addressQueryResults.addAll(addresses); - tryNextAddress(); + foundSomeDNS = true; + addressQueryResults = new ArrayList(); + 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() { 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) { -- cgit v0.10.2-6-g49f6