diff options
Diffstat (limited to 'Swiften/StreamStack')
23 files changed, 877 insertions, 0 deletions
diff --git a/Swiften/StreamStack/CompressionLayer.h b/Swiften/StreamStack/CompressionLayer.h new file mode 100644 index 0000000..6a8f2b3 --- /dev/null +++ b/Swiften/StreamStack/CompressionLayer.h @@ -0,0 +1,48 @@ +#ifndef SWIFTEN_COMPRESSIONLAYER_H +#define SWIFTEN_COMPRESSIONLAYER_H + +#include <boost/noncopyable.hpp> +#include <boost/signal.hpp> + +#include "Swiften/Base/ByteArray.h" +#include "Swiften/StreamStack/StreamLayer.h" +#include "Swiften/Compress/ZLibException.h" +#include "Swiften/Compress/ZLibCompressor.h" +#include "Swiften/Compress/ZLibDecompressor.h" + +namespace Swift { + class ZLibCompressor; + class ZLibDecompressor; + + class CompressionLayer : public StreamLayer, boost::noncopyable { + public: + CompressionLayer() {} + + virtual void writeData(const ByteArray& data) { + try { + onWriteData(compressor_.process(data)); + } + catch (const ZLibException& e) { + onError(); + } + } + + virtual void handleDataRead(const ByteArray& data) { + try { + onDataRead(decompressor_.process(data)); + } + catch (const ZLibException& e) { + onError(); + } + } + + public: + boost::signal<void ()> onError; + + private: + ZLibCompressor compressor_; + ZLibDecompressor decompressor_; + }; +} + +#endif diff --git a/Swiften/StreamStack/ConnectionLayer.h b/Swiften/StreamStack/ConnectionLayer.h new file mode 100644 index 0000000..7688f78 --- /dev/null +++ b/Swiften/StreamStack/ConnectionLayer.h @@ -0,0 +1,23 @@ +#pragma once + +#include <boost/signal.hpp> +#include <boost/shared_ptr.hpp> + +#include "Swiften/StreamStack/LowLayer.h" +#include "Swiften/Network/Connection.h" + +namespace Swift { + class ConnectionLayer : public LowLayer { + public: + ConnectionLayer(boost::shared_ptr<Connection> connection) : connection(connection) { + connection->onDataRead.connect(onDataRead); + } + + void writeData(const ByteArray& data) { + connection->write(data); + } + + private: + boost::shared_ptr<Connection> connection; + }; +} diff --git a/Swiften/StreamStack/HighLayer.cpp b/Swiften/StreamStack/HighLayer.cpp new file mode 100644 index 0000000..2f5e1df --- /dev/null +++ b/Swiften/StreamStack/HighLayer.cpp @@ -0,0 +1,8 @@ +#include "Swiften/StreamStack/HighLayer.h" + +namespace Swift { + +HighLayer::~HighLayer() { +} + +} diff --git a/Swiften/StreamStack/HighLayer.h b/Swiften/StreamStack/HighLayer.h new file mode 100644 index 0000000..bd6c6e6 --- /dev/null +++ b/Swiften/StreamStack/HighLayer.h @@ -0,0 +1,19 @@ +#ifndef SWIFTEN_HIGHLAYER_H +#define SWIFTEN_HIGHLAYER_H + +#include <boost/signal.hpp> + +#include "Swiften/Base/ByteArray.h" + +namespace Swift { + class HighLayer { + public: + virtual ~HighLayer(); + + virtual void handleDataRead(const ByteArray& data) = 0; + + boost::signal<void (const ByteArray&)> onWriteData; + }; +} + +#endif diff --git a/Swiften/StreamStack/LowLayer.cpp b/Swiften/StreamStack/LowLayer.cpp new file mode 100644 index 0000000..24aa7a2 --- /dev/null +++ b/Swiften/StreamStack/LowLayer.cpp @@ -0,0 +1,8 @@ +#include "Swiften/StreamStack/LowLayer.h" + +namespace Swift { + +LowLayer::~LowLayer() { +} + +} diff --git a/Swiften/StreamStack/LowLayer.h b/Swiften/StreamStack/LowLayer.h new file mode 100644 index 0000000..763cfc4 --- /dev/null +++ b/Swiften/StreamStack/LowLayer.h @@ -0,0 +1,19 @@ +#ifndef SWIFTEN_LOWLAYER_H +#define SWIFTEN_LOWLAYER_H + +#include <boost/signal.hpp> + +#include "Swiften/Base/ByteArray.h" + +namespace Swift { + class LowLayer { + public: + virtual ~LowLayer(); + + virtual void writeData(const ByteArray& data) = 0; + + boost::signal<void (const ByteArray&)> onDataRead; + }; +} + +#endif diff --git a/Swiften/StreamStack/OpenSSLLayer.cpp b/Swiften/StreamStack/OpenSSLLayer.cpp new file mode 100644 index 0000000..da93e4e --- /dev/null +++ b/Swiften/StreamStack/OpenSSLLayer.cpp @@ -0,0 +1,28 @@ +#include "Swiften/StreamStack/OpenSSLLayer.h" + +namespace Swift { + +OpenSSLLayer::OpenSSLLayer() { + context_.onDataForNetwork.connect(onWriteData); + context_.onDataForApplication.connect(onDataRead); + context_.onConnected.connect(onConnected); + context_.onError.connect(onError); +} + +void OpenSSLLayer::connect() { + context_.connect(); +} + +void OpenSSLLayer::writeData(const ByteArray& data) { + context_.handleDataFromApplication(data); +} + +void OpenSSLLayer::handleDataRead(const ByteArray& data) { + context_.handleDataFromNetwork(data); +} + +bool OpenSSLLayer::setClientCertificate(const PKCS12Certificate& certificate) { + return context_.setClientCertificate(certificate); +} + +} diff --git a/Swiften/StreamStack/OpenSSLLayer.h b/Swiften/StreamStack/OpenSSLLayer.h new file mode 100644 index 0000000..615c75e --- /dev/null +++ b/Swiften/StreamStack/OpenSSLLayer.h @@ -0,0 +1,27 @@ +#ifndef SWIFTEN_OpenSSLLayer_H +#define SWIFTEN_OpenSSLLayer_H + +#include <boost/noncopyable.hpp> +#include <boost/signal.hpp> + +#include "Swiften/Base/ByteArray.h" +#include "Swiften/StreamStack/TLSLayer.h" +#include "Swiften/TLS/OpenSSL/OpenSSLContext.h" + +namespace Swift { + class OpenSSLLayer : public TLSLayer, boost::noncopyable { + public: + OpenSSLLayer(); + + virtual void connect(); + virtual bool setClientCertificate(const PKCS12Certificate&); + + virtual void writeData(const ByteArray& data); + virtual void handleDataRead(const ByteArray& data); + + private: + OpenSSLContext context_; + }; +} + +#endif diff --git a/Swiften/StreamStack/PlatformTLSLayerFactory.cpp b/Swiften/StreamStack/PlatformTLSLayerFactory.cpp new file mode 100644 index 0000000..cdcb8a7 --- /dev/null +++ b/Swiften/StreamStack/PlatformTLSLayerFactory.cpp @@ -0,0 +1,31 @@ +#include "Swiften/StreamStack/PlatformTLSLayerFactory.h" + +#include <cassert> + +#ifdef HAVE_OPENSSL +#include "Swiften/StreamStack/OpenSSLLayer.h" +#endif + +namespace Swift { + +PlatformTLSLayerFactory::PlatformTLSLayerFactory() { +} + +bool PlatformTLSLayerFactory::canCreate() const { +#ifdef HAVE_OPENSSL + return true; +#else + return false; +#endif +} + +boost::shared_ptr<TLSLayer> PlatformTLSLayerFactory::createTLSLayer() { +#ifdef HAVE_OPENSSL + return boost::shared_ptr<TLSLayer>(new OpenSSLLayer()); +#else + assert(false); + return boost::shared_ptr<TLSLayer>(); +#endif +} + +} diff --git a/Swiften/StreamStack/PlatformTLSLayerFactory.h b/Swiften/StreamStack/PlatformTLSLayerFactory.h new file mode 100644 index 0000000..6c343b0 --- /dev/null +++ b/Swiften/StreamStack/PlatformTLSLayerFactory.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Swiften/StreamStack/TLSLayerFactory.h" + +namespace Swift { + class PlatformTLSLayerFactory : public TLSLayerFactory { + public: + PlatformTLSLayerFactory(); + + bool canCreate() const; + virtual boost::shared_ptr<TLSLayer> createTLSLayer(); + }; +} diff --git a/Swiften/StreamStack/SConscript b/Swiften/StreamStack/SConscript new file mode 100644 index 0000000..449a39b --- /dev/null +++ b/Swiften/StreamStack/SConscript @@ -0,0 +1,21 @@ +Import("swiften_env") + +myenv = swiften_env.Clone() +myenv.MergeFlags(swiften_env["OPENSSL_FLAGS"]) + +sources = [ + "HighLayer.cpp", + "LowLayer.cpp", + "PlatformTLSLayerFactory.cpp", + "StreamStack.cpp", + "TLSLayerFactory.cpp", + "WhitespacePingLayer.cpp", + "XMPPLayer.cpp", + ] + +if myenv.get("HAVE_OPENSSL", 0) : + myenv.Append(CPPDEFINES = "HAVE_OPENSSL") + sources += ["OpenSSLLayer.cpp"] + +objects = myenv.StaticObject(sources) +swiften_env.Append(SWIFTEN_OBJECTS = [objects]) diff --git a/Swiften/StreamStack/StreamLayer.h b/Swiften/StreamStack/StreamLayer.h new file mode 100644 index 0000000..6ea2051 --- /dev/null +++ b/Swiften/StreamStack/StreamLayer.h @@ -0,0 +1,16 @@ +#ifndef SWIFTEN_STREAMLAYER_H +#define SWIFTEN_STREAMLAYER_H + +#include <boost/signal.hpp> + +#include "Swiften/StreamStack/LowLayer.h" +#include "Swiften/StreamStack/HighLayer.h" + +namespace Swift { + class StreamLayer : public LowLayer, public HighLayer { + public: + StreamLayer() {} + }; +} + +#endif diff --git a/Swiften/StreamStack/StreamStack.cpp b/Swiften/StreamStack/StreamStack.cpp new file mode 100644 index 0000000..9eb9b4e --- /dev/null +++ b/Swiften/StreamStack/StreamStack.cpp @@ -0,0 +1,40 @@ +#include "Swiften/StreamStack/StreamStack.h" + +#include <boost/bind.hpp> + +#include "Swiften/Base/foreach.h" +#include "Swiften/StreamStack/XMPPLayer.h" +#include "Swiften/StreamStack/LowLayer.h" +#include "Swiften/StreamStack/StreamLayer.h" + +namespace Swift { + +StreamStack::StreamStack(boost::shared_ptr<XMPPLayer> xmppLayer, boost::shared_ptr<LowLayer> physicalLayer) : xmppLayer_(xmppLayer), physicalLayer_(physicalLayer) { + xmppReadSlotConnection_ = physicalLayer_->onDataRead.connect(boost::bind(&XMPPLayer::parseData, xmppLayer_, _1)); + xmppWriteSignalConnection_ = xmppLayer_->onWriteData.connect(boost::bind(&LowLayer::writeData, physicalLayer_, _1)); +} + +StreamStack::~StreamStack() { + // Disconnect the write signal connections to break cyclic signal + // dependencies. The read signal connections have + // to remain, since these can be reached from the main event loop. + xmppWriteSignalConnection_.disconnect(); + foreach(const boost::bsignals::connection& connection, writeSignalConnections_) { + connection.disconnect(); + } +} + +void StreamStack::addLayer(boost::shared_ptr<StreamLayer> newLayer) { + xmppReadSlotConnection_.disconnect(); + xmppWriteSignalConnection_.disconnect(); + + boost::shared_ptr<LowLayer> lowLayer = (layers_.empty() ? physicalLayer_ : *layers_.rbegin()); + + lowLayer->onDataRead.connect(boost::bind(&HighLayer::handleDataRead, newLayer, _1), boost::bsignals::at_front); + writeSignalConnections_.push_back(newLayer->onWriteData.connect(boost::bind(&LowLayer::writeData, lowLayer, _1), boost::bsignals::at_front)); + xmppWriteSignalConnection_ = xmppLayer_->onWriteData.connect(boost::bind(&LowLayer::writeData, newLayer, _1), boost::bsignals::at_front); + xmppReadSlotConnection_ = newLayer->onDataRead.connect(boost::bind(&XMPPLayer::parseData, xmppLayer_, _1), boost::bsignals::at_front); + layers_.push_back(newLayer); +} + +} diff --git a/Swiften/StreamStack/StreamStack.h b/Swiften/StreamStack/StreamStack.h new file mode 100644 index 0000000..87c522d --- /dev/null +++ b/Swiften/StreamStack/StreamStack.h @@ -0,0 +1,44 @@ +#pragma once + +#include <boost/shared_ptr.hpp> +#include <boost/signal.hpp> +#include <vector> + +#include "Swiften/Elements/Stanza.h" +#include "Swiften/Base/foreach.h" + +namespace Swift { + class XMPPLayer; + class LowLayer; + class StreamLayer; + + class StreamStack { + public: + StreamStack(boost::shared_ptr<XMPPLayer> xmppLayer, boost::shared_ptr<LowLayer> physicalLayer); + ~StreamStack(); + + void addLayer(boost::shared_ptr<StreamLayer>); + + boost::shared_ptr<XMPPLayer> getXMPPLayer() const { + return xmppLayer_; + } + + template<typename T> boost::shared_ptr<T> getLayer() { + foreach(const boost::shared_ptr<StreamLayer>& streamLayer, layers_) { + boost::shared_ptr<T> layer = boost::dynamic_pointer_cast<T>(streamLayer); + if (layer) { + return layer; + } + } + return boost::shared_ptr<T>(); + } + + private: + boost::shared_ptr<XMPPLayer> xmppLayer_; + boost::shared_ptr<LowLayer> physicalLayer_; + std::vector< boost::shared_ptr<StreamLayer> > layers_; + boost::bsignals::connection xmppReadSlotConnection_; + boost::bsignals::connection xmppWriteSignalConnection_; + std::vector< boost::bsignals::connection > writeSignalConnections_; + }; +} diff --git a/Swiften/StreamStack/TLSLayer.h b/Swiften/StreamStack/TLSLayer.h new file mode 100644 index 0000000..490e16c --- /dev/null +++ b/Swiften/StreamStack/TLSLayer.h @@ -0,0 +1,19 @@ +#ifndef SWIFTEN_TLSLayer_H +#define SWIFTEN_TLSLayer_H + +#include "Swiften/StreamStack/StreamLayer.h" +#include "Swiften/TLS/PKCS12Certificate.h" + +namespace Swift { + class TLSLayer : public StreamLayer { + public: + virtual void connect() = 0; + virtual bool setClientCertificate(const PKCS12Certificate&) = 0; + + public: + boost::signal<void ()> onError; + boost::signal<void ()> onConnected; + }; +} + +#endif diff --git a/Swiften/StreamStack/TLSLayerFactory.cpp b/Swiften/StreamStack/TLSLayerFactory.cpp new file mode 100644 index 0000000..15de455 --- /dev/null +++ b/Swiften/StreamStack/TLSLayerFactory.cpp @@ -0,0 +1,8 @@ +#include "Swiften/StreamStack/TLSLayerFactory.h" + +namespace Swift { + +TLSLayerFactory::~TLSLayerFactory() { +} + +} diff --git a/Swiften/StreamStack/TLSLayerFactory.h b/Swiften/StreamStack/TLSLayerFactory.h new file mode 100644 index 0000000..1b0bfc1 --- /dev/null +++ b/Swiften/StreamStack/TLSLayerFactory.h @@ -0,0 +1,15 @@ +#pragma once + +#include <boost/shared_ptr.hpp> + +namespace Swift { + class TLSLayer; + + class TLSLayerFactory { + public: + virtual ~TLSLayerFactory(); + virtual bool canCreate() const = 0; + + virtual boost::shared_ptr<TLSLayer> createTLSLayer() = 0; + }; +} diff --git a/Swiften/StreamStack/UnitTest/StreamStackTest.cpp b/Swiften/StreamStack/UnitTest/StreamStackTest.cpp new file mode 100644 index 0000000..6b97c0d --- /dev/null +++ b/Swiften/StreamStack/UnitTest/StreamStackTest.cpp @@ -0,0 +1,165 @@ +#include <vector> +#include <boost/bind.hpp> +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include "Swiften/Base/ByteArray.h" +#include "Swiften/StreamStack/StreamStack.h" +#include "Swiften/StreamStack/LowLayer.h" +#include "Swiften/StreamStack/XMPPLayer.h" +#include "Swiften/StreamStack/StreamLayer.h" +#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h" +#include "Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h" + +using namespace Swift; + +class StreamStackTest : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(StreamStackTest); + CPPUNIT_TEST(testWriteData_NoIntermediateStreamStack); + CPPUNIT_TEST(testWriteData_OneIntermediateStream); + CPPUNIT_TEST(testWriteData_TwoIntermediateStreamStack); + CPPUNIT_TEST(testReadData_NoIntermediateStreamStack); + CPPUNIT_TEST(testReadData_OneIntermediateStream); + CPPUNIT_TEST(testReadData_TwoIntermediateStreamStack); + CPPUNIT_TEST(testAddLayer_ExistingOnWriteDataSlot); + CPPUNIT_TEST_SUITE_END(); + + public: + StreamStackTest() {} + + void setUp() { + physicalStream_ = boost::shared_ptr<TestLowLayer>(new TestLowLayer()); + xmppStream_ = boost::shared_ptr<XMPPLayer>(new XMPPLayer(&parserFactories_, &serializers_)); + elementsReceived_ = 0; + dataWriteReceived_ = 0; + } + + void tearDown() { + } + + void testWriteData_NoIntermediateStreamStack() { + StreamStack testling(xmppStream_, physicalStream_); + + xmppStream_->writeData("foo"); + + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), physicalStream_->data_.size()); + CPPUNIT_ASSERT_EQUAL(ByteArray("foo"), physicalStream_->data_[0]); + } + + void testWriteData_OneIntermediateStream() { + StreamStack testling(xmppStream_, physicalStream_); + boost::shared_ptr<MyStreamLayer> xStream(new MyStreamLayer("X")); + testling.addLayer(xStream); + + xmppStream_->writeData("foo"); + + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), physicalStream_->data_.size()); + CPPUNIT_ASSERT_EQUAL(ByteArray("Xfoo"), physicalStream_->data_[0]); + } + + void testWriteData_TwoIntermediateStreamStack() { + StreamStack testling(xmppStream_, physicalStream_); + boost::shared_ptr<MyStreamLayer> xStream(new MyStreamLayer("X")); + boost::shared_ptr<MyStreamLayer> yStream(new MyStreamLayer("Y")); + testling.addLayer(xStream); + testling.addLayer(yStream); + + xmppStream_->writeData("foo"); + + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), physicalStream_->data_.size()); + CPPUNIT_ASSERT_EQUAL(ByteArray("XYfoo"), physicalStream_->data_[0]); + } + + void testReadData_NoIntermediateStreamStack() { + StreamStack testling(xmppStream_, physicalStream_); + xmppStream_->onElement.connect(boost::bind(&StreamStackTest::handleElement, this, _1)); + + physicalStream_->onDataRead(ByteArray("<stream:stream xmlns:stream='http://etherx.jabber.org/streams'><presence/>")); + + CPPUNIT_ASSERT_EQUAL(1, elementsReceived_); + } + + void testReadData_OneIntermediateStream() { + StreamStack testling(xmppStream_, physicalStream_); + xmppStream_->onElement.connect(boost::bind(&StreamStackTest::handleElement, this, _1)); + boost::shared_ptr<MyStreamLayer> xStream(new MyStreamLayer("<")); + testling.addLayer(xStream); + + physicalStream_->onDataRead(ByteArray("stream:stream xmlns:stream='http://etherx.jabber.org/streams'><presence/>")); + + CPPUNIT_ASSERT_EQUAL(1, elementsReceived_); + } + + void testReadData_TwoIntermediateStreamStack() { + StreamStack testling(xmppStream_, physicalStream_); + xmppStream_->onElement.connect(boost::bind(&StreamStackTest::handleElement, this, _1)); + boost::shared_ptr<MyStreamLayer> xStream(new MyStreamLayer("s")); + boost::shared_ptr<MyStreamLayer> yStream(new MyStreamLayer("<")); + testling.addLayer(xStream); + testling.addLayer(yStream); + + physicalStream_->onDataRead(ByteArray("tream:stream xmlns:stream='http://etherx.jabber.org/streams'><presence/>")); + + CPPUNIT_ASSERT_EQUAL(1, elementsReceived_); + } + + void testAddLayer_ExistingOnWriteDataSlot() { + StreamStack testling(xmppStream_, physicalStream_); + xmppStream_->onWriteData.connect(boost::bind(&StreamStackTest::handleWriteData, this, _1)); + boost::shared_ptr<MyStreamLayer> xStream(new MyStreamLayer("X")); + testling.addLayer(xStream); + + xmppStream_->writeData("foo"); + + CPPUNIT_ASSERT_EQUAL(1, dataWriteReceived_); + } + + void handleElement(boost::shared_ptr<Element>) { + ++elementsReceived_; + } + + void handleWriteData(ByteArray) { + ++dataWriteReceived_; + } + + private: + class MyStreamLayer : public StreamLayer { + public: + MyStreamLayer(const String& prepend) : prepend_(prepend) { + } + + virtual void writeData(const ByteArray& data) { + onWriteData(ByteArray(prepend_) + data); + } + + virtual void handleDataRead(const ByteArray& data) { + onDataRead(ByteArray(prepend_) + data); + } + + private: + String prepend_; + }; + + class TestLowLayer : public LowLayer { + public: + TestLowLayer() { + } + + virtual void writeData(const ByteArray& data) { + data_.push_back(data); + } + + std::vector<ByteArray> data_; + }; + + private: + FullPayloadParserFactoryCollection parserFactories_; + FullPayloadSerializerCollection serializers_; + boost::shared_ptr<TestLowLayer> physicalStream_; + boost::shared_ptr<XMPPLayer> xmppStream_; + int elementsReceived_; + int dataWriteReceived_; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(StreamStackTest); diff --git a/Swiften/StreamStack/UnitTest/XMPPLayerTest.cpp b/Swiften/StreamStack/UnitTest/XMPPLayerTest.cpp new file mode 100644 index 0000000..e284ba9 --- /dev/null +++ b/Swiften/StreamStack/UnitTest/XMPPLayerTest.cpp @@ -0,0 +1,119 @@ +#include <vector> +#include <boost/bind.hpp> +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include "Swiften/Elements/ProtocolHeader.h" +#include "Swiften/Elements/Presence.h" +#include "Swiften/Base/ByteArray.h" +#include "Swiften/StreamStack/XMPPLayer.h" +#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h" +#include "Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h" + +using namespace Swift; + +class XMPPLayerTest : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(XMPPLayerTest); + CPPUNIT_TEST(testParseData_Error); + CPPUNIT_TEST(testResetParser); + CPPUNIT_TEST(testResetParser_FromSlot); + CPPUNIT_TEST(testWriteHeader); + CPPUNIT_TEST(testWriteElement); + CPPUNIT_TEST(testWriteFooter); + CPPUNIT_TEST_SUITE_END(); + + public: + XMPPLayerTest() {} + + void setUp() { + testling_ = new XMPPLayer(&parserFactories_, &serializers_); + elementsReceived_ = 0; + dataReceived_ = ""; + errorReceived_ = 0; + } + + void tearDown() { + delete testling_; + } + + void testParseData_Error() { + testling_->onError.connect(boost::bind(&XMPPLayerTest::handleError, this)); + + testling_->parseData("<iq>"); + + CPPUNIT_ASSERT_EQUAL(1, errorReceived_); + } + + void testResetParser() { + testling_->onElement.connect(boost::bind(&XMPPLayerTest::handleElement, this, _1)); + testling_->onError.connect(boost::bind(&XMPPLayerTest::handleError, this)); + + testling_->parseData("<stream:stream to=\"example.com\" xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" >"); + testling_->resetParser(); + testling_->parseData("<stream:stream to=\"example.com\" xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" >"); + testling_->parseData("<presence/>"); + + CPPUNIT_ASSERT_EQUAL(1, elementsReceived_); + CPPUNIT_ASSERT_EQUAL(0, errorReceived_); + } + + void testResetParser_FromSlot() { + testling_->onElement.connect(boost::bind(&XMPPLayerTest::handleElementAndReset, this, _1)); + testling_->parseData("<stream:stream to=\"example.com\" xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" ><presence/>"); + testling_->parseData("<stream:stream to=\"example.com\" xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" ><presence/>"); + + CPPUNIT_ASSERT_EQUAL(2, elementsReceived_); + CPPUNIT_ASSERT_EQUAL(0, errorReceived_); + } + + void testWriteHeader() { + testling_->onWriteData.connect(boost::bind(&XMPPLayerTest::handleWriteData, this, _1)); + ProtocolHeader header; + header.setTo("example.com"); + testling_->writeHeader(header); + + CPPUNIT_ASSERT_EQUAL(String("<?xml version=\"1.0\"?><stream:stream xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" to=\"example.com\" version=\"1.0\">"), dataReceived_); + } + + void testWriteElement() { + testling_->onWriteData.connect(boost::bind(&XMPPLayerTest::handleWriteData, this, _1)); + testling_->writeElement(boost::shared_ptr<Presence>(new Presence())); + + CPPUNIT_ASSERT_EQUAL(String("<presence/>"), dataReceived_); + } + + void testWriteFooter() { + testling_->onWriteData.connect(boost::bind(&XMPPLayerTest::handleWriteData, this, _1)); + testling_->writeFooter(); + + CPPUNIT_ASSERT_EQUAL(String("</stream:stream>"), dataReceived_); + } + + void handleElement(boost::shared_ptr<Element>) { + ++elementsReceived_; + } + + void handleElementAndReset(boost::shared_ptr<Element>) { + ++elementsReceived_; + testling_->resetParser(); + } + + void handleWriteData(ByteArray ba) { + dataReceived_ += std::string(ba.getData(), ba.getSize()); + } + + void handleError() { + ++errorReceived_; + } + + private: + FullPayloadParserFactoryCollection parserFactories_; + FullPayloadSerializerCollection serializers_; + XMPPLayer* testling_; + int elementsReceived_; + String dataReceived_; + int errorReceived_; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(XMPPLayerTest); diff --git a/Swiften/StreamStack/WhitespacePingLayer.cpp b/Swiften/StreamStack/WhitespacePingLayer.cpp new file mode 100644 index 0000000..5c1eb00 --- /dev/null +++ b/Swiften/StreamStack/WhitespacePingLayer.cpp @@ -0,0 +1,39 @@ +#include "Swiften/StreamStack/WhitespacePingLayer.h" + +#include <boost/bind.hpp> + +#include "Swiften/Network/TimerFactory.h" +#include "Swiften/Network/Timer.h" + +namespace Swift { + +static const int TIMEOUT_MILLISECONDS = 60000; + +WhitespacePingLayer::WhitespacePingLayer(TimerFactory* timerFactory) : isActive(false) { + timer = timerFactory->createTimer(TIMEOUT_MILLISECONDS); + timer->onTick.connect(boost::bind(&WhitespacePingLayer::handleTimerTick, this)); +} + +void WhitespacePingLayer::writeData(const ByteArray& data) { + onWriteData(data); +} + +void WhitespacePingLayer::handleDataRead(const ByteArray& data) { + onDataRead(data); +} + +void WhitespacePingLayer::handleTimerTick() { + onWriteData(" "); +} + +void WhitespacePingLayer::setActive() { + isActive = true; + timer->start(); +} + +void WhitespacePingLayer::setInactive() { + timer->stop(); + isActive = false; +} + +} diff --git a/Swiften/StreamStack/WhitespacePingLayer.h b/Swiften/StreamStack/WhitespacePingLayer.h new file mode 100644 index 0000000..18ea39a --- /dev/null +++ b/Swiften/StreamStack/WhitespacePingLayer.h @@ -0,0 +1,32 @@ +#pragma once + +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> + +#include "Swiften/StreamStack/StreamLayer.h" + +namespace Swift { + class Timer; + class TimerFactory; + + class WhitespacePingLayer : public StreamLayer, boost::noncopyable { + public: + WhitespacePingLayer(TimerFactory* timerFactory); + + void setActive(); + void setInactive(); + void writeData(const ByteArray& data); + void handleDataRead(const ByteArray& data); + + bool getIsActive() const { + return isActive; + } + + private: + void handleTimerTick(); + + private: + bool isActive; + boost::shared_ptr<Timer> timer; + }; +} diff --git a/Swiften/StreamStack/XMPPLayer.cpp b/Swiften/StreamStack/XMPPLayer.cpp new file mode 100644 index 0000000..b87cb4a --- /dev/null +++ b/Swiften/StreamStack/XMPPLayer.cpp @@ -0,0 +1,80 @@ +#include "Swiften/StreamStack/XMPPLayer.h" +#include "Swiften/Parser/XMPPParser.h" +#include "Swiften/Serializer/XMPPSerializer.h" +#include "Swiften/Elements/ProtocolHeader.h" + +namespace Swift { + +XMPPLayer::XMPPLayer( + PayloadParserFactoryCollection* payloadParserFactories, + PayloadSerializerCollection* payloadSerializers) : + payloadParserFactories_(payloadParserFactories), + payloadSerializers_(payloadSerializers), + resetParserAfterParse_(false), + inParser_(false) { + xmppParser_ = new XMPPParser(this, payloadParserFactories_); + xmppSerializer_ = new XMPPSerializer(payloadSerializers_); +} + +XMPPLayer::~XMPPLayer() { + delete xmppSerializer_; + delete xmppParser_; +} + +void XMPPLayer::writeHeader(const ProtocolHeader& header) { + onWriteData(ByteArray(xmppSerializer_->serializeHeader(header))); +} + +void XMPPLayer::writeFooter() { + onWriteData(ByteArray(xmppSerializer_->serializeFooter())); +} + +void XMPPLayer::writeElement(boost::shared_ptr<Element> element) { + onWriteData(ByteArray(xmppSerializer_->serializeElement(element))); +} + +void XMPPLayer::writeData(const String& data) { + onWriteData(ByteArray(data)); +} + +void XMPPLayer::parseData(ByteArray data) { + onDataRead(data); + inParser_ = true; + if (!xmppParser_->parse(String(data.getData(), data.getSize()))) { + inParser_ = false; + onError(); + return; + } + inParser_ = false; + if (resetParserAfterParse_) { + doResetParser(); + } +} + +void XMPPLayer::doResetParser() { + delete xmppParser_; + xmppParser_ = new XMPPParser(this, payloadParserFactories_); + resetParserAfterParse_ = false; +} + +void XMPPLayer::handleStreamStart(const ProtocolHeader& header) { + onStreamStart(header); +} + +void XMPPLayer::handleElement(boost::shared_ptr<Element> stanza) { + onElement(stanza); +} + +void XMPPLayer::handleStreamEnd() { +} + +void XMPPLayer::resetParser() { + if (inParser_) { + resetParserAfterParse_ = true; + } + else { + doResetParser(); + } +} + +} diff --git a/Swiften/StreamStack/XMPPLayer.h b/Swiften/StreamStack/XMPPLayer.h new file mode 100644 index 0000000..7974811 --- /dev/null +++ b/Swiften/StreamStack/XMPPLayer.h @@ -0,0 +1,55 @@ +#pragma once + +#include <boost/shared_ptr.hpp> +#include <boost/signal.hpp> +#include <boost/noncopyable.hpp> + +#include "Swiften/Base/ByteArray.h" +#include "Swiften/Elements/Element.h" +#include "Swiften/Parser/XMPPParserClient.h" + +namespace Swift { + class ProtocolHeader; + class XMPPParser; + class PayloadParserFactoryCollection; + class XMPPSerializer; + class PayloadSerializerCollection; + + class XMPPLayer : public XMPPParserClient, boost::noncopyable { + public: + XMPPLayer( + PayloadParserFactoryCollection* payloadParserFactories, + PayloadSerializerCollection* payloadSerializers); + ~XMPPLayer(); + + void writeHeader(const ProtocolHeader& header); + void writeFooter(); + void writeElement(boost::shared_ptr<Element>); + void writeData(const String& data); + + void parseData(ByteArray data); + void resetParser(); + + public: + boost::signal<void (const ProtocolHeader&)> onStreamStart; + boost::signal<void (boost::shared_ptr<Element>)> onElement; + boost::signal<void (const ByteArray&)> onWriteData; + boost::signal<void (const ByteArray&)> onDataRead; + boost::signal<void ()> onError; + + private: + void handleStreamStart(const ProtocolHeader&); + void handleElement(boost::shared_ptr<Element>); + void handleStreamEnd(); + + void doResetParser(); + + private: + PayloadParserFactoryCollection* payloadParserFactories_; + XMPPParser* xmppParser_; + PayloadSerializerCollection* payloadSerializers_; + XMPPSerializer* xmppSerializer_; + bool resetParserAfterParse_; + bool inParser_; + }; +} |