summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Smith <git@kismith.co.uk>2011-07-01 09:19:49 (GMT)
committerKevin Smith <git@kismith.co.uk>2011-07-01 09:19:49 (GMT)
commit2da71a8a85486a494343f1662d64fb5ae5a2a44e (patch)
tree23992f9f2a00bac23b345e5c2cc9c1194efc25be /src/com/isode/stroke/session
downloadstroke-2da71a8a85486a494343f1662d64fb5ae5a2a44e.zip
stroke-2da71a8a85486a494343f1662d64fb5ae5a2a44e.tar.bz2
Initial import
Diffstat (limited to 'src/com/isode/stroke/session')
-rw-r--r--src/com/isode/stroke/session/BasicSessionStream.java252
-rw-r--r--src/com/isode/stroke/session/Session.java183
-rw-r--r--src/com/isode/stroke/session/SessionStream.java88
-rw-r--r--src/com/isode/stroke/session/SessionTracer.java43
4 files changed, 566 insertions, 0 deletions
diff --git a/src/com/isode/stroke/session/BasicSessionStream.java b/src/com/isode/stroke/session/BasicSessionStream.java
new file mode 100644
index 0000000..c7c98cf
--- /dev/null
+++ b/src/com/isode/stroke/session/BasicSessionStream.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2010 Remko Tron¨on
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+/*
+ * Copyright (c) 2010, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.session;
+
+import com.isode.stroke.base.ByteArray;
+import com.isode.stroke.elements.Element;
+import com.isode.stroke.elements.ProtocolHeader;
+import com.isode.stroke.elements.StreamType;
+import com.isode.stroke.eventloop.EventLoop;
+import com.isode.stroke.network.Connection;
+import com.isode.stroke.network.TimerFactory;
+import com.isode.stroke.parser.PayloadParserFactoryCollection;
+import com.isode.stroke.serializer.PayloadSerializerCollection;
+import com.isode.stroke.signals.Slot;
+import com.isode.stroke.signals.Slot1;
+import com.isode.stroke.streamstack.CompressionLayer;
+import com.isode.stroke.streamstack.ConnectionLayer;
+import com.isode.stroke.streamstack.StreamStack;
+import com.isode.stroke.streamstack.TLSLayer;
+import com.isode.stroke.streamstack.WhitespacePingLayer;
+import com.isode.stroke.tls.TLSContextFactory;
+import com.isode.stroke.streamstack.XMPPLayer;
+import com.isode.stroke.tls.Certificate;
+import com.isode.stroke.tls.CertificateVerificationError;
+
+public class BasicSessionStream extends SessionStream {
+
+ public BasicSessionStream(
+ StreamType streamType,
+ Connection connection,
+ PayloadParserFactoryCollection payloadParserFactories,
+ PayloadSerializerCollection payloadSerializers,
+ TLSContextFactory tlsContextFactory,
+ TimerFactory timerFactory,
+ EventLoop eventLoop) {
+ available = false;
+ this.connection = connection;
+ this.payloadParserFactories = payloadParserFactories;
+ this.payloadSerializers = payloadSerializers;
+ this.tlsContextFactory = tlsContextFactory;
+ this.timerFactory = timerFactory;
+ if (timerFactory == null) {
+ throw new IllegalStateException(); //FIXME: remove conditional, debugging only.
+ }
+ this.streamType = streamType;
+ this.compressionLayer = null;
+ this.tlsLayer = null;
+ this.whitespacePingLayer = null;
+
+ xmppLayer = new XMPPLayer(payloadParserFactories, payloadSerializers, streamType, eventLoop);
+ xmppLayer.onStreamStart.connect(new Slot1<ProtocolHeader>() {
+
+ public void call(ProtocolHeader p1) {
+ handleStreamStartReceived(p1);
+ }
+ });
+ xmppLayer.onElement.connect(new Slot1<Element>() {
+
+ public void call(Element p1) {
+ handleElementReceived(p1);
+ }
+ });
+ xmppLayer.onError.connect(new Slot() {
+
+ public void call() {
+ handleXMPPError();
+ }
+ });
+ xmppLayer.onDataRead.connect(new Slot1<ByteArray>() {
+
+ public void call(ByteArray p1) {
+ handleDataRead(p1);
+ }
+ });
+ xmppLayer.onWriteData.connect(new Slot1<ByteArray>() {
+
+ public void call(ByteArray p1) {
+ handleDataWritten(p1);
+ }
+ });
+
+ connection.onDisconnected.connect(new Slot1<Connection.Error>() {
+
+ public void call(Connection.Error p1) {
+ handleConnectionFinished(p1);
+ }
+ });
+ connectionLayer = new ConnectionLayer(connection);
+
+ streamStack = new StreamStack(xmppLayer, connectionLayer);
+
+ available = true;
+
+ }
+
+ public void writeHeader(ProtocolHeader header) {
+ assert available;
+ xmppLayer.writeHeader(header);
+ }
+
+ public void writeElement(Element element) {
+ assert available;
+ xmppLayer.writeElement(element);
+ }
+
+ public void writeFooter() {
+ assert available;
+ xmppLayer.writeFooter();
+ }
+
+ public void writeData(String data) {
+ assert available;
+ xmppLayer.writeData(data);
+ }
+
+ public void close() {
+ connection.disconnect();
+ }
+
+ public boolean isOpen() {
+ return available;
+ }
+
+ public boolean supportsTLSEncryption() {
+ return tlsContextFactory != null && tlsContextFactory.canCreate();
+ }
+
+ public void addTLSEncryption() {
+ assert available;
+ tlsLayer = new TLSLayer(tlsContextFactory);
+ if (hasTLSCertificate() && !tlsLayer.setClientCertificate(getTLSCertificate())) {
+ onClosed.emit(new Error(Error.Type.InvalidTLSCertificateError));
+ } else {
+ streamStack.addLayer(tlsLayer);
+ tlsLayer.onError.connect(new Slot() {
+
+ public void call() {
+ handleTLSError();
+ }
+ });
+ tlsLayer.onConnected.connect(new Slot() {
+
+ public void call() {
+ handleTLSConnected();
+ }
+ });
+ tlsLayer.connect();
+ }
+ }
+
+ public boolean isTLSEncrypted() {
+ return tlsLayer != null;
+ }
+
+ public Certificate getPeerCertificate() {
+ return tlsLayer.getPeerCertificate();
+ }
+
+ public CertificateVerificationError getPeerCertificateVerificationError() {
+ return tlsLayer.getPeerCertificateVerificationError();
+ }
+
+ public ByteArray getTLSFinishMessage() {
+ return tlsLayer.getContext().getFinishMessage();
+ }
+
+ public void addZLibCompression() {
+ compressionLayer = new CompressionLayer();
+ streamStack.addLayer(compressionLayer);
+ }
+
+ public void setWhitespacePingEnabled(boolean enabled) {
+ if (enabled) {
+ if (whitespacePingLayer == null) {
+ whitespacePingLayer = new WhitespacePingLayer(timerFactory);
+ streamStack.addLayer(whitespacePingLayer);
+ }
+ whitespacePingLayer.setActive();
+ }
+ else if (whitespacePingLayer != null) {
+ whitespacePingLayer.setInactive();
+ }
+ }
+
+ public void resetXMPPParser() {
+ xmppLayer.resetParser();
+ }
+
+ private void handleStreamStartReceived(ProtocolHeader header) {
+ onStreamStartReceived.emit(header);
+ }
+
+ private void handleElementReceived(Element element) {
+ onElementReceived.emit(element);
+ }
+
+ private void handleXMPPError() {
+ available = false;
+ onClosed.emit(new Error(Error.Type.ParseError));
+ }
+
+ private void handleTLSConnected() {
+ onTLSEncrypted.emit();
+ }
+
+ private void handleTLSError() {
+ available = false;
+ onClosed.emit(new Error(Error.Type.TLSError));
+ }
+
+ private void handleConnectionFinished(Connection.Error error) {
+ available = false;
+ if (Connection.Error.ReadError.equals(error)) {
+ onClosed.emit(new Error(Error.Type.ConnectionReadError));
+ }
+ else if (error != null) {
+ onClosed.emit(new Error(Error.Type.ConnectionWriteError));
+ }
+ else {
+ onClosed.emit(null);
+ }
+ }
+
+ private void handleDataRead(ByteArray data) {
+ onDataRead.emit(data.toString());
+ }
+
+ private void handleDataWritten(ByteArray data) {
+ onDataWritten.emit(data.toString());
+ }
+ private boolean available;
+ private Connection connection;
+ private PayloadParserFactoryCollection payloadParserFactories;
+ private PayloadSerializerCollection payloadSerializers;
+ private TLSContextFactory tlsContextFactory;
+ private TimerFactory timerFactory;
+ private StreamType streamType;
+ private XMPPLayer xmppLayer;
+ private ConnectionLayer connectionLayer;
+ private CompressionLayer compressionLayer;
+ private TLSLayer tlsLayer;
+ private WhitespacePingLayer whitespacePingLayer;
+ private StreamStack streamStack;
+
+}
diff --git a/src/com/isode/stroke/session/Session.java b/src/com/isode/stroke/session/Session.java
new file mode 100644
index 0000000..79132e4
--- /dev/null
+++ b/src/com/isode/stroke/session/Session.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2010 Remko Tron¨on
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+/*
+ * Copyright (c) 2010-2011, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.session;
+
+import com.isode.stroke.base.ByteArray;
+import com.isode.stroke.elements.Element;
+import com.isode.stroke.elements.ProtocolHeader;
+import com.isode.stroke.elements.StreamType;
+import com.isode.stroke.eventloop.EventLoop;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.network.Connection;
+import com.isode.stroke.parser.PayloadParserFactoryCollection;
+import com.isode.stroke.serializer.PayloadSerializerCollection;
+import com.isode.stroke.signals.Signal1;
+import com.isode.stroke.signals.Slot;
+import com.isode.stroke.signals.Slot1;
+import com.isode.stroke.streamstack.ConnectionLayer;
+import com.isode.stroke.streamstack.StreamStack;
+import com.isode.stroke.streamstack.XMPPLayer;
+
+public abstract class Session {
+
+ public enum SessionError {
+
+ ConnectionReadError,
+ ConnectionWriteError,
+ XMLError,
+ AuthenticationFailedError,
+ NoSupportedAuthMechanismsError,
+ UnexpectedElementError,
+ ResourceBindError,
+ SessionStartError,
+ TLSError,
+ ClientCertificateLoadError,
+ ClientCertificateError
+ };
+
+ public Session(
+ final Connection connection,
+ final PayloadParserFactoryCollection payloadParserFactories,
+ final PayloadSerializerCollection payloadSerializers,
+ final EventLoop eventLoop) {
+ this.connection = connection;
+ this.eventLoop = eventLoop;
+ this.payloadParserFactories = payloadParserFactories;
+ this.payloadSerializers = payloadSerializers;
+ finishing = false;
+ }
+
+
+ public void startSession() {
+ initializeStreamStack();
+ handleSessionStarted();
+ }
+
+ public void finishSession() {
+ finishing = true;
+ connection.disconnect();
+ handleSessionFinished(null);
+ finishing = false;
+ onSessionFinished.emit(null);
+ }
+
+ public void sendElement(Element stanza) {
+ xmppLayer.writeElement(stanza);
+ }
+
+ public JID getLocalJID() {
+ return localJID;
+ }
+
+ public JID getRemoteJID() {
+ return remoteJID;
+ }
+ public final Signal1<Element> onElementReceived = new Signal1<Element>();
+ public final Signal1<SessionError> onSessionFinished = new Signal1<SessionError>();
+ public final Signal1<ByteArray> onDataWritten = new Signal1<ByteArray>();
+ public final Signal1<ByteArray> onDataRead = new Signal1<ByteArray>();
+
+ protected void setRemoteJID(JID j) {
+ remoteJID = j;
+ }
+
+ protected void setLocalJID(JID j) {
+ localJID = j;
+ }
+
+ protected void finishSession(SessionError error) {
+ finishing = true;
+ connection.disconnect();
+ handleSessionFinished(error);
+ finishing = false;
+ onSessionFinished.emit(error);
+ }
+
+ protected void handleSessionStarted() {
+ }
+
+ protected void handleSessionFinished(SessionError error) {
+ }
+
+ protected abstract void handleElement(Element element);
+
+ protected abstract void handleStreamStart(ProtocolHeader header);
+
+ protected void initializeStreamStack() {
+ xmppLayer = new XMPPLayer(payloadParserFactories, payloadSerializers, StreamType.ClientStreamType, eventLoop);
+ xmppLayer.onStreamStart.connect(new Slot1<ProtocolHeader>() {
+
+ public void call(ProtocolHeader header) {
+ handleStreamStart(header);
+ }
+ });
+ xmppLayer.onElement.connect(new Slot1<Element>() {
+
+ public void call(Element p1) {
+ handleElement(p1);
+ }
+ });
+ xmppLayer.onError.connect(new Slot() {
+
+ public void call() {
+ finishSession(SessionError.XMLError);
+ }
+ });
+ xmppLayer.onDataRead.connect(onDataRead);
+ xmppLayer.onWriteData.connect(onDataWritten);
+ connection.onDisconnected.connect(new Slot1<Connection.Error>() {
+
+ public void call(Connection.Error p1) {
+ handleDisconnected(p1);
+ }
+ });
+ connectionLayer = new ConnectionLayer(connection);
+ streamStack = new StreamStack(xmppLayer, connectionLayer);
+ }
+
+ public XMPPLayer getXMPPLayer() {
+ return xmppLayer;
+
+
+ }
+
+ public StreamStack getStreamStack() {
+ return streamStack;
+
+
+ }
+
+ /*void setFinished();*/ /* This seems to be unused in Swiften*/
+
+ private void handleDisconnected(Connection.Error connectionError) {
+ if (connectionError != null) {
+ switch (connectionError) {
+ case ReadError:
+ finishSession(SessionError.ConnectionReadError);
+ break;
+ case WriteError:
+ finishSession(SessionError.ConnectionWriteError);
+ break;
+ }
+ } else {
+ finishSession();
+ }
+ }
+ private JID localJID;
+ private JID remoteJID;
+ private Connection connection;
+ private PayloadParserFactoryCollection payloadParserFactories;
+ private PayloadSerializerCollection payloadSerializers;
+ private XMPPLayer xmppLayer;
+ private ConnectionLayer connectionLayer;
+ private StreamStack streamStack;
+ private boolean finishing;
+ private final EventLoop eventLoop;
+}
diff --git a/src/com/isode/stroke/session/SessionStream.java b/src/com/isode/stroke/session/SessionStream.java
new file mode 100644
index 0000000..c70622b
--- /dev/null
+++ b/src/com/isode/stroke/session/SessionStream.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2010 Remko Tron¨on
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+/*
+ * Copyright (c) 2010-2011, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.session;
+
+import com.isode.stroke.base.ByteArray;
+import com.isode.stroke.elements.Element;
+import com.isode.stroke.elements.ProtocolHeader;
+import com.isode.stroke.signals.Signal;
+import com.isode.stroke.signals.Signal1;
+import com.isode.stroke.tls.Certificate;
+import com.isode.stroke.tls.CertificateVerificationError;
+import com.isode.stroke.tls.PKCS12Certificate;
+
+public abstract class SessionStream {
+
+ public static class Error implements com.isode.stroke.base.Error {
+
+ public enum Type {
+
+ ParseError,
+ TLSError,
+ InvalidTLSCertificateError,
+ ConnectionReadError,
+ ConnectionWriteError
+ };
+
+ public Error(Type type) {
+ this.type = type;
+ }
+ public final Type type;
+ };
+
+ public abstract void close();
+
+ public abstract boolean isOpen();
+
+ public abstract void writeHeader(ProtocolHeader header);
+
+ public abstract void writeFooter();
+
+ public abstract void writeElement(Element element);
+
+ public abstract void writeData(String data);
+
+ public abstract void addZLibCompression();
+
+ public abstract boolean supportsTLSEncryption();
+
+ public abstract void addTLSEncryption();
+
+ public abstract boolean isTLSEncrypted();
+
+ public abstract void setWhitespacePingEnabled(boolean enabled);
+
+ public abstract void resetXMPPParser();
+
+ public void setTLSCertificate(PKCS12Certificate cert) {
+ certificate = cert;
+ }
+
+ public boolean hasTLSCertificate() {
+ return certificate != null && !certificate.isNull();
+ }
+
+ public abstract Certificate getPeerCertificate();
+
+ public abstract CertificateVerificationError getPeerCertificateVerificationError();
+
+ public abstract ByteArray getTLSFinishMessage();
+
+ public final Signal1<ProtocolHeader> onStreamStartReceived = new Signal1<ProtocolHeader>();
+ public final Signal1<Element> onElementReceived = new Signal1<Element>();
+ public final Signal1<Error> onClosed = new Signal1<Error>();
+ public final Signal onTLSEncrypted = new Signal();
+ public final Signal1<String> onDataRead = new Signal1<String>();
+ public final Signal1<String> onDataWritten = new Signal1<String>();
+ protected PKCS12Certificate getTLSCertificate() {
+ return certificate;
+ }
+ private PKCS12Certificate certificate;
+}
diff --git a/src/com/isode/stroke/session/SessionTracer.java b/src/com/isode/stroke/session/SessionTracer.java
new file mode 100644
index 0000000..93c51a8
--- /dev/null
+++ b/src/com/isode/stroke/session/SessionTracer.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2010 Remko Tron¨on
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+/*
+ * Copyright (c) 2010, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.session;
+
+import com.isode.stroke.base.ByteArray;
+import com.isode.stroke.signals.Slot1;
+
+public class SessionTracer {
+
+ public SessionTracer(Session session) {
+ this.session = session;
+ session.onDataRead.connect(new Slot1<ByteArray>() {
+
+ public void call(ByteArray p1) {
+ printData('<', p1);
+ }
+ });
+
+ session.onDataWritten.connect(new Slot1<ByteArray>() {
+
+ public void call(ByteArray p1) {
+ printData('>', p1);
+ }
+ });
+ }
+
+ private void printData(char direction, ByteArray data) {
+ System.err.print("" + direction + direction + " " + session.getLocalJID().toString() + " ");
+ for (int i = 0; i < 72 - session.getLocalJID().toString().length() - session.getRemoteJID().toString().length(); ++i) {
+ System.err.print(direction);
+ }
+ System.err.println(" " + session.getRemoteJID().toString() + " " + direction + direction);
+ System.err.println(data);
+ }
+ private Session session;
+} \ No newline at end of file