diff options
author | Kevin Smith <git@kismith.co.uk> | 2011-07-01 09:19:49 (GMT) |
---|---|---|
committer | Kevin Smith <git@kismith.co.uk> | 2011-07-01 09:19:49 (GMT) |
commit | 2da71a8a85486a494343f1662d64fb5ae5a2a44e (patch) | |
tree | 23992f9f2a00bac23b345e5c2cc9c1194efc25be /src/com/isode/stroke/session | |
download | stroke-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.java | 252 | ||||
-rw-r--r-- | src/com/isode/stroke/session/Session.java | 183 | ||||
-rw-r--r-- | src/com/isode/stroke/session/SessionStream.java | 88 | ||||
-rw-r--r-- | src/com/isode/stroke/session/SessionTracer.java | 43 |
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 |