summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Swiften/Base/Debug.cpp141
-rw-r--r--Swiften/Base/Debug.h20
-rw-r--r--Swiften/QA/FileTransferTest/.gitignore1
-rw-r--r--Swiften/QA/FileTransferTest/FileTransferTest.cpp331
-rw-r--r--Swiften/QA/FileTransferTest/SConscript17
-rw-r--r--Swiften/QA/SConscript1
-rw-r--r--Swiften/SConscript1
7 files changed, 512 insertions, 0 deletions
diff --git a/Swiften/Base/Debug.cpp b/Swiften/Base/Debug.cpp
new file mode 100644
index 0000000..82dbec6
--- /dev/null
+++ b/Swiften/Base/Debug.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/Base/Debug.h>
+
+#include <iostream>
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/Client/ClientError.h>
+#include <Swiften/Serializer/PayloadSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h>
+#include <Swiften/Serializer/XMPPSerializer.h>
+
+std::ostream& operator<<(std::ostream& os, const Swift::ClientError& error) {
+ os << "ClientError(";
+ switch(error.getType()) {
+ case Swift::ClientError::UnknownError:
+ os << "UnknownError";
+ break;
+ case Swift::ClientError::DomainNameResolveError:
+ os << "DomainNameResolveError";
+ break;
+ case Swift::ClientError::ConnectionError:
+ os << "ConnectionError";
+ break;
+ case Swift::ClientError::ConnectionReadError:
+ os << "ConnectionReadError";
+ break;
+ case Swift::ClientError::ConnectionWriteError:
+ os << "ConnectionWriteError";
+ break;
+ case Swift::ClientError::XMLError:
+ os << "XMLError";
+ break;
+ case Swift::ClientError::AuthenticationFailedError:
+ os << "AuthenticationFailedError";
+ break;
+ case Swift::ClientError::CompressionFailedError:
+ os << "CompressionFailedError";
+ break;
+ case Swift::ClientError::ServerVerificationFailedError:
+ os << "ServerVerificationFailedError";
+ break;
+ case Swift::ClientError::NoSupportedAuthMechanismsError:
+ os << "NoSupportedAuthMechanismsError";
+ break;
+ case Swift::ClientError::UnexpectedElementError:
+ os << "UnexpectedElementError";
+ break;
+ case Swift::ClientError::ResourceBindError:
+ os << "ResourceBindError";
+ break;
+ case Swift::ClientError::SessionStartError:
+ os << "SessionStartError";
+ break;
+ case Swift::ClientError::StreamError:
+ os << "StreamError";
+ break;
+ case Swift::ClientError::TLSError:
+ os << "TLSError";
+ break;
+ case Swift::ClientError::ClientCertificateLoadError:
+ os << "ClientCertificateLoadError";
+ break;
+ case Swift::ClientError::ClientCertificateError:
+ os << "ClientCertificateError";
+ break;
+ case Swift::ClientError::CertificateCardRemoved:
+ os << "CertificateCardRemoved";
+ break;
+ case Swift::ClientError::UnknownCertificateError:
+ os << "UnknownCertificateError";
+ break;
+ case Swift::ClientError::CertificateExpiredError:
+ os << "CertificateExpiredError";
+ break;
+ case Swift::ClientError::CertificateNotYetValidError:
+ os << "CertificateNotYetValidError";
+ break;
+ case Swift::ClientError::CertificateSelfSignedError:
+ os << "CertificateSelfSignedError";
+ break;
+ case Swift::ClientError::CertificateRejectedError:
+ os << "CertificateRejectedError";
+ break;
+ case Swift::ClientError::CertificateUntrustedError:
+ os << "CertificateUntrustedError";
+ break;
+ case Swift::ClientError::InvalidCertificatePurposeError:
+ os << "InvalidCertificatePurposeError";
+ break;
+ case Swift::ClientError::CertificatePathLengthExceededError:
+ os << "CertificatePathLengthExceededError";
+ break;
+ case Swift::ClientError::InvalidCertificateSignatureError:
+ os << "InvalidCertificateSignatureError";
+ break;
+ case Swift::ClientError::InvalidCAError:
+ os << "InvalidCAError";
+ break;
+ case Swift::ClientError::InvalidServerIdentityError:
+ os << "InvalidServerIdentityError";
+ break;
+ case Swift::ClientError::RevokedError:
+ os << "RevokedError";
+ break;
+ case Swift::ClientError::RevocationCheckFailedError:
+ os << "RevocationCheckFailedError";
+ break;
+ }
+ os << ")";
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os, Swift::Element* ele) {
+ using namespace Swift;
+
+ boost::shared_ptr<Element> element = boost::shared_ptr<Element>(ele);
+
+ boost::shared_ptr<Payload> payload = boost::dynamic_pointer_cast<Payload>(element);
+ if (payload) {
+ FullPayloadSerializerCollection payloadSerializerCollection;
+ PayloadSerializer *serializer = payloadSerializerCollection.getPayloadSerializer(payload);
+ os << "Payload(" << serializer->serialize(payload) << ")";
+ return os;
+ }
+ boost::shared_ptr<ToplevelElement> topLevelElement = boost::dynamic_pointer_cast<ToplevelElement>(element);
+ if (topLevelElement) {
+ FullPayloadSerializerCollection payloadSerializerCollection;
+ XMPPSerializer xmppSerializer(&payloadSerializerCollection, ClientStreamType, false);
+ SafeByteArray serialized = xmppSerializer.serializeElement(topLevelElement);
+ os << "TopLevelElement(" << safeByteArrayToString(serialized) << ")";
+ return os;
+ }
+ os << "Element(Unknown)";
+ return os;
+}
diff --git a/Swiften/Base/Debug.h b/Swiften/Base/Debug.h
new file mode 100644
index 0000000..33d439e
--- /dev/null
+++ b/Swiften/Base/Debug.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <iosfwd>
+
+namespace Swift {
+ class ClientError;
+ class Element;
+}
+
+namespace boost {
+ template<class T> class shared_ptr;
+}
+
+std::ostream& operator<<(std::ostream& os, const Swift::ClientError& error);
+
+std::ostream& operator<<(std::ostream& os, Swift::Element* ele);
diff --git a/Swiften/QA/FileTransferTest/.gitignore b/Swiften/QA/FileTransferTest/.gitignore
new file mode 100644
index 0000000..768c501
--- /dev/null
+++ b/Swiften/QA/FileTransferTest/.gitignore
@@ -0,0 +1 @@
+FileTransferTest
diff --git a/Swiften/QA/FileTransferTest/FileTransferTest.cpp b/Swiften/QA/FileTransferTest/FileTransferTest.cpp
new file mode 100644
index 0000000..764dffb
--- /dev/null
+++ b/Swiften/QA/FileTransferTest/FileTransferTest.cpp
@@ -0,0 +1,331 @@
+/*
+ * Copyright (c) 2014-2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <fstream>
+
+#include <boost/numeric/conversion/cast.hpp>
+#include <boost/filesystem.hpp>
+
+#include <Swiften/Base/sleep.h>
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Client/ClientXMLTracer.h>
+#include <Swiften/Client/Client.h>
+#include <Swiften/EventLoop/SimpleEventLoop.h>
+#include <Swiften/Network/BoostNetworkFactories.h>
+#include <Swiften/Network/Timer.h>
+#include <Swiften/Network/TimerFactory.h>
+#include <Swiften/Disco/EntityCapsProvider.h>
+#include <Swiften/Elements/Presence.h>
+#include <Swiften/FileTransfer/ReadBytestream.h>
+#include <Swiften/Base/BoostRandomGenerator.h>
+#include <Swiften/FileTransfer/FileReadBytestream.h>
+#include <Swiften/FileTransfer/OutgoingFileTransfer.h>
+#include <Swiften/FileTransfer/FileTransferManager.h>
+#include <Swiften/Disco/ClientDiscoManager.h>
+#include <Swiften/FileTransfer/FileWriteBytestream.h>
+#include <Swiften/Base/Debug.h>
+
+using namespace Swift;
+
+static const std::string CLIENT_NAME = "Swiften FT Test";
+static const std::string CLIENT_NODE = "http://swift.im";
+
+static boost::shared_ptr<SimpleEventLoop> eventLoop;
+static boost::shared_ptr<BoostNetworkFactories> networkFactories;
+
+BoostRandomGenerator randGen;
+
+enum Candidate {
+ InBandBytestream = 1,
+ S5B_Direct = 2,
+ S5B_Proxied = 4,
+ S5B_Assisted = 8,
+};
+
+class FileTransferTest {
+ public:
+ FileTransferTest(int senderCandidates, int receiverCandidates) : senderCandidates_(senderCandidates), senderIsDone_(false), receiverCandidates_(receiverCandidates), receiverIsDone_(false) {
+ sender_ = boost::make_shared<Client>(JID(getenv("SWIFT_FILETRANSFERTEST_JID")), getenv("SWIFT_FILETRANSFERTEST_PASS"), networkFactories.get());
+ sender_->onDisconnected.connect(boost::bind(&FileTransferTest::handleSenderDisconnected, this, _1));
+ sender_->onConnected.connect(boost::bind(&FileTransferTest::handleSenderConnected, this));
+ sender_->getEntityCapsProvider()->onCapsChanged.connect(boost::bind(&FileTransferTest::handleSenderCapsChanged, this, _1));
+
+ receiver_ = boost::make_shared<Client>(JID(getenv("SWIFT_FILETRANSFERTEST2_JID")), getenv("SWIFT_FILETRANSFERTEST2_PASS"), networkFactories.get());
+ receiver_->onConnected.connect(boost::bind(&FileTransferTest::handleReceiverConnected, this));
+ receiver_->onDisconnected.connect(boost::bind(&FileTransferTest::handleReceiverDisconnected, this, _1));
+
+ new ClientXMLTracer(sender_.get());
+ new ClientXMLTracer(receiver_.get());
+
+ ClientOptions options;
+ options.useTLS = ClientOptions::NeverUseTLS;
+ options.useStreamCompression = false;
+ options.useStreamResumption = false;
+ options.useAcks = false;
+
+ sender_->connect(options);
+ receiver_->connect(options);
+
+ timeOut_ = networkFactories->getTimerFactory()->createTimer(60000);
+ timeOut_->onTick.connect(boost::bind(&FileTransferTest::handleTimeOut, this));
+
+ // Create randomly sized data to exchange.
+ sendFilePath_ = boost::filesystem::unique_path();
+ receiveFilePath_ = boost::filesystem::unique_path();
+
+ size_t size = 1024 + boost::numeric_cast<size_t>(randGen.generateRandomInteger(1024 * 10));
+ sendData_.resize(size);
+ for (size_t n = 0; n < sendData_.size(); n++) {
+ sendData_[n] = boost::numeric_cast<unsigned char>(randGen.generateRandomInteger(255));
+ }
+
+ std::ofstream outfile(sendFilePath_.native().c_str(), std::ios::out | std::ios::binary);
+ outfile.write(reinterpret_cast<char *>(&sendData_[0]), sendData_.size());
+ outfile.close();
+ }
+
+ ~FileTransferTest() {
+ timeOut_->stop();
+
+ if(boost::filesystem::exists(sendFilePath_)) {
+ boost::filesystem::remove(sendFilePath_);
+ }
+
+ if(boost::filesystem::exists(receiveFilePath_)) {
+ boost::filesystem::remove(receiveFilePath_);
+ }
+ }
+
+ void handleSenderConnected() {
+ sender_->sendPresence(Presence::create());
+ }
+
+ void handleReceiverConnected() {
+ receiver_->getFileTransferManager()->onIncomingFileTransfer.connect(boost::bind(&FileTransferTest::handleReceiverIncomingFileTransfer, this, _1));
+
+ DiscoInfo discoInfo;
+ discoInfo.addIdentity(DiscoInfo::Identity(CLIENT_NAME, "client", "pc"));
+ discoInfo.addFeature(DiscoInfo::JingleFeature);
+ discoInfo.addFeature(DiscoInfo::JingleFTFeature);
+ discoInfo.addFeature(DiscoInfo::Bytestream);
+ discoInfo.addFeature(DiscoInfo::JingleTransportsIBBFeature);
+ discoInfo.addFeature(DiscoInfo::JingleTransportsS5BFeature);
+ receiver_->getDiscoManager()->setCapsNode(CLIENT_NODE);
+ receiver_->getDiscoManager()->setDiscoInfo(discoInfo);
+ receiver_->getPresenceSender()->sendPresence(Presence::create());
+ }
+
+ void handleReceiverIncomingFileTransfer(IncomingFileTransfer::ref transfer) {
+ incomingFileTransfers_.push_back(transfer);
+ boost::shared_ptr<FileWriteBytestream> out = boost::make_shared<FileWriteBytestream>(receiveFilePath_.native());
+ transfer->onFinished.connect(boost::bind(&FileTransferTest::handleReceiverFileTransferFinished, this, _1, out));
+
+ FileTransferOptions options;
+ options = options.withInBandAllowed(receiverCandidates_ & InBandBytestream);
+ options = options.withDirectAllowed(receiverCandidates_ & S5B_Direct);
+ options = options.withAssistedAllowed(receiverCandidates_ & S5B_Assisted);
+ options = options.withProxiedAllowed(receiverCandidates_ & S5B_Proxied);
+
+ std::cout << "Incoming transfer options: " << "IBB (" << options.isInBandAllowed() << ")" << ", ";
+ std::cout << "S5B Direct (" << options.isDirectAllowed() << ")" << ", ";
+ std::cout << "S5B Assisted (" << options.isAssistedAllowed() << ")" << ", ";
+ std::cout << "S5B Proxied (" << options.isProxiedAllowed() << ")" << std::endl;
+
+ transfer->accept(out, options);
+ }
+
+ void handleSenderCapsChanged(const JID &jid) {
+ if (receiver_ && (receiver_->getJID().toBare() == jid.toBare())) {
+ boost::shared_ptr<FileReadBytestream> fileStream = boost::make_shared<FileReadBytestream>(sendFilePath_);
+
+ FileTransferOptions options;
+ options = options.withInBandAllowed(senderCandidates_ & InBandBytestream);
+ options = options.withDirectAllowed(senderCandidates_ & S5B_Direct);
+ options = options.withAssistedAllowed(senderCandidates_ & S5B_Assisted);
+ options = options.withProxiedAllowed(senderCandidates_ & S5B_Proxied);
+
+ std::cout << "Outgoing transfer options: " << "IBB (" << options.isInBandAllowed() << ")" << ", ";
+ std::cout << "S5B Direct (" << options.isDirectAllowed() << ")" << ", ";
+ std::cout << "S5B Assisted (" << options.isAssistedAllowed() << ")" << ", ";
+ std::cout << "S5B Proxied (" << options.isProxiedAllowed() << ")" << std::endl;
+
+ outgoingFileTransfer_ = sender_->getFileTransferManager()->createOutgoingFileTransfer(jid.toBare(), sendFilePath_, "Some File!", fileStream, options);
+
+ if (outgoingFileTransfer_) {
+ outgoingFileTransfer_->onFinished.connect(boost::bind(&FileTransferTest::handleSenderFileTransferFinished, this, _1));
+ outgoingFileTransfer_->start();
+ } else {
+ std::cout << "ERROR: No outgoing file transfer returned." << std::endl;
+ endTest();
+ }
+ }
+ }
+
+ void handleReceiverFileTransferFinished(const boost::optional<FileTransferError>& error, boost::shared_ptr<FileWriteBytestream> out) {
+ out->close();
+ receiverError_ = error;
+ receiverIsDone_ = true;
+ if (senderIsDone_) {
+ timeOut_->stop();
+ timeOut_ = networkFactories->getTimerFactory()->createTimer(1000);
+ timeOut_->onTick.connect(boost::bind(&FileTransferTest::endTest, this));
+ timeOut_->start();
+ }
+ }
+
+ void handleSenderDisconnected(const boost::optional<ClientError>& error) {
+ if (error) {
+ std::cout << this << " " << "handleSenderDisconnected: error: " << error.get() << std::endl;
+ }
+ sender_.reset();
+ if (!sender_ && !receiver_) {
+ eventLoop->stop();
+ }
+ }
+
+ void handleReceiverDisconnected(const boost::optional<ClientError>& error) {
+ if (error) {
+ std::cout << this << " " << "handleReceiverDisconnected: error: " << error.get() << std::endl;
+ }
+ receiver_.reset();
+ if (!sender_ && !receiver_) {
+ eventLoop->stop();
+ }
+ }
+
+ void handleSenderFileTransferFinished(const boost::optional<FileTransferError>& error) {
+ senderError_ = error;
+ senderIsDone_ = true;
+ if (receiverIsDone_) {
+ timeOut_->stop();
+ timeOut_ = networkFactories->getTimerFactory()->createTimer(1000);
+ timeOut_->onTick.connect(boost::bind(&FileTransferTest::endTest, this));
+ timeOut_->start();
+ }
+ }
+
+ void run() {
+ timeOut_->start();
+ eventLoop->run();
+ }
+
+ void endTest() {
+ if (sender_) {
+ sender_->disconnect();
+ }
+ if (receiver_) {
+ receiver_->disconnect();
+ }
+ }
+
+ void handleTimeOut() {
+ std::cout << "Test timed out!!!" << std::endl;
+ endTest();
+ }
+
+ bool isDone() const {
+ return senderIsDone_ && receiverIsDone_;
+ }
+
+ bool wasSuccessful() const {
+ return !senderError_ && !receiverError_;
+ }
+
+ private:
+ int senderCandidates_;
+ boost::shared_ptr<Client> sender_;
+ ByteArray sendData_;
+ OutgoingFileTransfer::ref outgoingFileTransfer_;
+ boost::filesystem::path sendFilePath_;
+ boost::optional<FileTransferError> senderError_;
+ bool senderIsDone_;
+
+ int receiverCandidates_;
+ boost::shared_ptr<Client> receiver_;
+ ByteArray receiveData_;
+ std::vector<IncomingFileTransfer::ref> incomingFileTransfers_;
+ boost::filesystem::path receiveFilePath_;
+ boost::optional<FileTransferError> receiverError_;
+ bool receiverIsDone_;
+
+ Timer::ref timeOut_;
+};
+
+bool runTest(int senderCandidates, int receiverCandidates) {
+ bool success = false;
+
+ std::cout << "senderCandidates: " << senderCandidates << ", receiverCandidates: " << receiverCandidates << std::endl;
+ bool expectSuccess = (senderCandidates & receiverCandidates) > 0;
+
+ eventLoop = boost::make_shared<SimpleEventLoop>();
+ networkFactories = boost::make_shared<BoostNetworkFactories>(eventLoop.get());
+
+ boost::shared_ptr<FileTransferTest> testRun = boost::make_shared<FileTransferTest>(senderCandidates, receiverCandidates);
+
+ testRun->run();
+
+ if (testRun->isDone()) {
+ bool wasSuccessful = testRun->wasSuccessful();
+ if (expectSuccess == wasSuccessful) {
+ success = true;
+ } else {
+ std::cout << "expected success: " << expectSuccess << ", wasSuccessful: " << wasSuccessful << std::endl;
+ }
+ } else {
+ std::cout << "Failed to run test! Sender candidates = " << senderCandidates << ", receiver candidates = " << receiverCandidates << "." << std::endl;
+ }
+
+ testRun.reset();
+ networkFactories.reset();
+ eventLoop->runUntilEvents();
+
+ eventLoop->stop();
+ eventLoop.reset();
+
+ return success;
+}
+
+/**
+ * This program test file-transfer interop between Swift and itself with various connection candidates.
+ * The all combinations of the candidates, IBB, S5B (direct) and S5B (proxied), on sender and receiver side are tested.
+ */
+int main(int argc, char** argv) {
+ int failedTests = 0;
+
+ std::vector<std::pair<int, int> > failedTestPairs;
+ std::cout << "Swiften File-Transfer Connectivity Test Suite" << std::endl;
+ if (argc == 1) {
+ for (int n = 0; n < (1 << 7); n++) {
+ int senderCandidates = n & 0xF;
+ int receiverCandidates = (n >> 4) & 0xF;
+ std::cout << "Run test " << n + 1 << " of " << (1 << 7) << ", (" << senderCandidates << ", " << receiverCandidates << ")" << std::endl;
+ if (!runTest(senderCandidates, receiverCandidates)) {
+ failedTests++;
+ failedTestPairs.push_back(std::pair<int, int>(senderCandidates, receiverCandidates));
+ }
+ }
+
+ typedef std::pair<int, int> IntPair;
+ foreach(IntPair failedTest, failedTestPairs) {
+ std::cout << "Failed test: " << "( " << failedTest.first << ", " << failedTest.second << ") " << std::endl;
+ }
+ }
+ else if (argc == 3) {
+ Log::setLogLevel(Log::debug);
+ int senderCandidates = atoi(argv[1]);
+ int receiverCandidates = atoi(argv[2]);
+ if (!runTest(senderCandidates, receiverCandidates)) {
+ failedTests++;
+ }
+ }
+ else {
+ std::cout << "Usage:" << std::endl;
+ std::cout << "\t- to test all combinations pass no arguments" << std::endl;
+ std::cout << "\t- to test a specific combination pass two integers describing sender and receiver candidates" << std::endl;
+ }
+ return failedTests;
+}
diff --git a/Swiften/QA/FileTransferTest/SConscript b/Swiften/QA/FileTransferTest/SConscript
new file mode 100644
index 0000000..d17b12a
--- /dev/null
+++ b/Swiften/QA/FileTransferTest/SConscript
@@ -0,0 +1,17 @@
+import os
+
+Import("env")
+
+if env["TEST"] :
+ myenv = env.Clone()
+ myenv.UseFlags(myenv["SWIFTEN_FLAGS"])
+ myenv.UseFlags(myenv["SWIFTEN_DEP_FLAGS"])
+
+ for i in ["SWIFT_FILETRANSFERTEST_JID", "SWIFT_FILETRANSFERTEST_PASS", "SWIFT_FILETRANSFERTEST2_JID", "SWIFT_FILETRANSFERTEST2_PASS"]:
+ if ARGUMENTS.get(i.lower(), False) :
+ myenv["ENV"][i] = ARGUMENTS[i.lower()]
+ elif os.environ.get(i, "") :
+ myenv["ENV"][i] = os.environ[i]
+
+ tester = myenv.Program("FileTransferTest", ["FileTransferTest.cpp"])
+ myenv.Test(tester, "system")
diff --git a/Swiften/QA/SConscript b/Swiften/QA/SConscript
index 2f2be6e..2135ef4 100644
--- a/Swiften/QA/SConscript
+++ b/Swiften/QA/SConscript
@@ -9,4 +9,5 @@ SConscript(dirs = [
"TLSTest",
"ScriptedTests",
"ProxyProviderTest",
+ "FileTransferTest",
])
diff --git a/Swiften/SConscript b/Swiften/SConscript
index 685b3d5..340688b 100644
--- a/Swiften/SConscript
+++ b/Swiften/SConscript
@@ -101,6 +101,7 @@ if env["SCONS_STAGE"] == "build" :
# TODO: Move all this to a submodule SConscript
sources = [
+ "Base/Debug.cpp",
"Chat/ChatStateTracker.cpp",
"Chat/ChatStateNotifier.cpp",
"Client/ClientSessionStanzaChannel.cpp",