diff options
Diffstat (limited to 'Swiften/StreamStack')
24 files changed, 859 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..99873a0 --- /dev/null +++ b/Swiften/StreamStack/ConnectionLayer.h @@ -0,0 +1,25 @@ +#ifndef SWIFTEN_CONNECTIONLAYER_H +#define SWIFTEN_CONNECTIONLAYER_H + +#include <boost/signal.hpp> + +#include "Swiften/StreamStack/LowLayer.h" +#include "Swiften/Network/Connection.h" + +namespace Swift { + class ConnectionLayer : public LowLayer { + public: + ConnectionLayer(Connection* connection) : connection_(connection) { + connection_->onDataRead.connect(onDataRead); + } + + void writeData(const ByteArray& data) { + connection_->write(data); + } + + private: + Connection* connection_; + }; +} + +#endif 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/Makefile.inc b/Swiften/StreamStack/Makefile.inc new file mode 100644 index 0000000..51f7b68 --- /dev/null +++ b/Swiften/StreamStack/Makefile.inc @@ -0,0 +1,15 @@ +SWIFTEN_SOURCES += \ + Swiften/StreamStack/XMPPLayer.cpp \ + Swiften/StreamStack/StreamStack.cpp \ + Swiften/StreamStack/LowLayer.cpp \ + Swiften/StreamStack/HighLayer.cpp \ + Swiften/StreamStack/WhitespacePingLayer.cpp \ + Swiften/StreamStack/TLSLayerFactory.cpp \ + Swiften/StreamStack/PlatformTLSLayerFactory.cpp + +ifeq ($(HAVE_OPENSSL),yes) +SWIFTEN_SOURCES += \ + Swiften/StreamStack/OpenSSLLayer.cpp +endif + +include Swiften/StreamStack/UnitTest/Makefile.inc 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..bb1abf1 --- /dev/null +++ b/Swiften/StreamStack/PlatformTLSLayerFactory.cpp @@ -0,0 +1,34 @@ +#include "Swiften/StreamStack/PlatformTLSLayerFactory.h" + +#include <cassert> + +#ifdef HAVE_SWIFTEN_CONFIG_H +#include "Swiften/config.h" +#endif +#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 +} + +TLSLayer* PlatformTLSLayerFactory::createTLSLayer() { +#ifdef HAVE_OPENSSL + return new OpenSSLLayer(); +#else + assert(false); + return 0; +#endif +} + +} diff --git a/Swiften/StreamStack/PlatformTLSLayerFactory.h b/Swiften/StreamStack/PlatformTLSLayerFactory.h new file mode 100644 index 0000000..6ebee32 --- /dev/null +++ b/Swiften/StreamStack/PlatformTLSLayerFactory.h @@ -0,0 +1,16 @@ +#ifndef SWIFTEN_OpenSSLLayerFactory_H +#define SWIFTEN_OpenSSLLayerFactory_H + +#include "Swiften/StreamStack/TLSLayerFactory.h" + +namespace Swift { + class PlatformTLSLayerFactory : public TLSLayerFactory { + public: + PlatformTLSLayerFactory(); + + bool canCreate() const; + virtual TLSLayer* createTLSLayer(); + }; +} + +#endif 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..7dde858 --- /dev/null +++ b/Swiften/StreamStack/StreamStack.cpp @@ -0,0 +1,29 @@ +#include "Swiften/StreamStack/StreamStack.h" + +#include <boost/bind.hpp> + +#include "Swiften/StreamStack/XMPPLayer.h" +#include "Swiften/StreamStack/LowLayer.h" +#include "Swiften/StreamStack/StreamLayer.h" + +namespace Swift { + +StreamStack::StreamStack(XMPPLayer* xmppLayer, 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)); +} + +void StreamStack::addLayer(StreamLayer* newLayer) { + xmppReadSlotConnection_.disconnect(); + xmppWriteSignalConnection_.disconnect(); + + LowLayer* lowLayer = (layers_.empty() ? physicalLayer_ : *layers_.rbegin()); + + lowLayer->onDataRead.connect(boost::bind(&HighLayer::handleDataRead, newLayer, _1), boost::bsignals::at_front); + 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..bc2e89b --- /dev/null +++ b/Swiften/StreamStack/StreamStack.h @@ -0,0 +1,45 @@ +#ifndef SWIFTEN_STREAMSTACK_H +#define SWIFTEN_STREAMSTACK_H + +#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(XMPPLayer* xmppLayer, LowLayer* physicalLayer); + + void addLayer(StreamLayer*); + + XMPPLayer* getXMPPLayer() const { + return xmppLayer_; + } + + template<typename T> T* getLayer() { + foreach(StreamLayer* streamLayer, layers_) { + T* layer = dynamic_cast<T*>(streamLayer); + if (layer) { + return layer; + } + } + return 0; + } + + private: + XMPPLayer* xmppLayer_; + LowLayer* physicalLayer_; + std::vector<StreamLayer*> layers_; + boost::bsignals::connection xmppReadSlotConnection_; + boost::bsignals::connection xmppWriteSignalConnection_; + }; +} + +#endif 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..b4218a5 --- /dev/null +++ b/Swiften/StreamStack/TLSLayerFactory.h @@ -0,0 +1,16 @@ +#ifndef SWIFTEN_TLSLayerFactory_H +#define SWIFTEN_TLSLayerFactory_H + +namespace Swift { + class TLSLayer; + + class TLSLayerFactory { + public: + virtual ~TLSLayerFactory(); + virtual bool canCreate() const = 0; + + virtual TLSLayer* createTLSLayer() = 0; + }; +} + +#endif diff --git a/Swiften/StreamStack/UnitTest/Makefile.inc b/Swiften/StreamStack/UnitTest/Makefile.inc new file mode 100644 index 0000000..dba5f5a --- /dev/null +++ b/Swiften/StreamStack/UnitTest/Makefile.inc @@ -0,0 +1,3 @@ +UNITTEST_SOURCES += \ + Swiften/StreamStack/UnitTest/XMPPLayerTest.cpp \ + Swiften/StreamStack/UnitTest/StreamStackTest.cpp diff --git a/Swiften/StreamStack/UnitTest/StreamStackTest.cpp b/Swiften/StreamStack/UnitTest/StreamStackTest.cpp new file mode 100644 index 0000000..1f6aad7 --- /dev/null +++ b/Swiften/StreamStack/UnitTest/StreamStackTest.cpp @@ -0,0 +1,167 @@ +#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_ = new TestLowLayer(); + xmppStream_ = new XMPPLayer(&parserFactories_, &serializers_); + elementsReceived_ = 0; + dataWriteReceived_ = 0; + } + + void tearDown() { + delete physicalStream_; + delete xmppStream_; + } + + 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_); + std::auto_ptr<MyStreamLayer> xStream(new MyStreamLayer("X")); + testling.addLayer(xStream.get()); + + 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_); + std::auto_ptr<MyStreamLayer> xStream(new MyStreamLayer("X")); + std::auto_ptr<MyStreamLayer> yStream(new MyStreamLayer("Y")); + testling.addLayer(xStream.get()); + testling.addLayer(yStream.get()); + + 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)); + std::auto_ptr<MyStreamLayer> xStream(new MyStreamLayer("<")); + testling.addLayer(xStream.get()); + + 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)); + std::auto_ptr<MyStreamLayer> xStream(new MyStreamLayer("s")); + std::auto_ptr<MyStreamLayer> yStream(new MyStreamLayer("<")); + testling.addLayer(xStream.get()); + testling.addLayer(yStream.get()); + + 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)); + std::auto_ptr<MyStreamLayer> xStream(new MyStreamLayer("X")); + testling.addLayer(xStream.get()); + + 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_; + TestLowLayer* physicalStream_; + 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..2150f4d --- /dev/null +++ b/Swiften/StreamStack/UnitTest/XMPPLayerTest.cpp @@ -0,0 +1,116 @@ +#include <vector> +#include <boost/bind.hpp> +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.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)); + testling_->writeHeader("example.com"); + + CPPUNIT_ASSERT_EQUAL(String("<?xml version='1.0'?><stream:stream to='example.com' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' 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..b9fbbd1 --- /dev/null +++ b/Swiften/StreamStack/WhitespacePingLayer.cpp @@ -0,0 +1,30 @@ +#include "Swiften/StreamStack/WhitespacePingLayer.h" +#include "Swiften/Network/Timer.h" + +namespace Swift { + +static const int TIMEOUT_MILLISECONDS = 60000; + +WhitespacePingLayer::WhitespacePingLayer() { + timer_ = new Timer(TIMEOUT_MILLISECONDS); + timer_->onTick.connect(boost::bind(&WhitespacePingLayer::handleTimerTick, this)); + timer_->start(); +} + +WhitespacePingLayer::~WhitespacePingLayer() { + delete timer_; +} + +void WhitespacePingLayer::writeData(const ByteArray& data) { + onWriteData(data); +} + +void WhitespacePingLayer::handleDataRead(const ByteArray& data) { + onDataRead(data); +} + +void WhitespacePingLayer::handleTimerTick() { + onWriteData(" "); +} + +} diff --git a/Swiften/StreamStack/WhitespacePingLayer.h b/Swiften/StreamStack/WhitespacePingLayer.h new file mode 100644 index 0000000..01fd3e3 --- /dev/null +++ b/Swiften/StreamStack/WhitespacePingLayer.h @@ -0,0 +1,27 @@ +#ifndef SWIFTEN_WhitespacePingLayer_H +#define SWIFTEN_WhitespacePingLayer_H + +#include <boost/noncopyable.hpp> + +#include "Swiften/StreamStack/StreamLayer.h" + +namespace Swift { + class Timer; + + class WhitespacePingLayer : public StreamLayer, boost::noncopyable { + public: + WhitespacePingLayer(); + ~WhitespacePingLayer(); + + void writeData(const ByteArray& data); + void handleDataRead(const ByteArray& data); + + private: + void handleTimerTick(); + + private: + Timer* timer_; + }; +} + +#endif diff --git a/Swiften/StreamStack/XMPPLayer.cpp b/Swiften/StreamStack/XMPPLayer.cpp new file mode 100644 index 0000000..7afa762 --- /dev/null +++ b/Swiften/StreamStack/XMPPLayer.cpp @@ -0,0 +1,79 @@ +#include "Swiften/StreamStack/XMPPLayer.h" +#include "Swiften/Parser/XMPPParser.h" +#include "Swiften/Serializer/XMPPSerializer.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 String& domain) { + onWriteData(ByteArray(xmppSerializer_->serializeHeader(domain))); +} + +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() { + onStreamStart(); +} + +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..07b5b1c --- /dev/null +++ b/Swiften/StreamStack/XMPPLayer.h @@ -0,0 +1,57 @@ +#ifndef SWIFTEN_XMPPLAYER_H +#define SWIFTEN_XMPPLAYER_H + +#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 XMPPParser; + class PayloadParserFactoryCollection; + class XMPPSerializer; + class PayloadSerializerCollection; + + class XMPPLayer : public XMPPParserClient, boost::noncopyable { + public: + XMPPLayer( + PayloadParserFactoryCollection* payloadParserFactories, + PayloadSerializerCollection* payloadSerializers); + ~XMPPLayer(); + + void writeHeader(const String& domain); + void writeFooter(); + void writeElement(boost::shared_ptr<Element>); + void writeData(const String& data); + + void parseData(ByteArray data); + void resetParser(); + + public: + boost::signal<void ()> 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(); + void handleElement(boost::shared_ptr<Element>); + void handleStreamEnd(); + + void doResetParser(); + + private: + PayloadParserFactoryCollection* payloadParserFactories_; + XMPPParser* xmppParser_; + PayloadSerializerCollection* payloadSerializers_; + XMPPSerializer* xmppSerializer_; + bool resetParserAfterParse_; + bool inParser_; + }; +} + +#endif |