summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRemko Tronçon <git@el-tramo.be>2010-10-17 12:13:36 (GMT)
committerRemko Tronçon <git@el-tramo.be>2010-10-21 18:25:00 (GMT)
commit1b58ef2af54456004390a0888c3edf104e3baa99 (patch)
treedbe4ae29de1b765a88ea704dfaa1c03af4b196b3 /Swiften
parent07402c4e3451f2084a1c3ddc5bacfb38a66899a7 (diff)
downloadswift-contrib-1b58ef2af54456004390a0888c3edf104e3baa99.zip
swift-contrib-1b58ef2af54456004390a0888c3edf104e3baa99.tar.bz2
Added beginnings of outgoing file transfer to Swiften.
Diffstat (limited to 'Swiften')
-rw-r--r--Swiften/Base/ByteArray.h4
-rw-r--r--Swiften/Base/StartStopper.h23
-rw-r--r--Swiften/Component/UnitTest/ComponentConnectorTest.cpp1
-rw-r--r--Swiften/Elements/Bytestreams.h59
-rw-r--r--Swiften/Elements/Form.cpp14
-rw-r--r--Swiften/Elements/Form.h2
-rw-r--r--Swiften/Elements/IBB.h103
-rw-r--r--Swiften/Elements/StreamInitiation.h76
-rw-r--r--Swiften/Elements/UnitTest/FormTest.cpp20
-rw-r--r--Swiften/Examples/SConscript1
-rw-r--r--Swiften/Examples/SendFile/.gitignore1
-rw-r--r--Swiften/Examples/SendFile/SConscript13
-rw-r--r--Swiften/Examples/SendFile/SendFile.cpp124
-rw-r--r--Swiften/FileTransfer/ByteArrayReadBytestream.h36
-rw-r--r--Swiften/FileTransfer/BytestreamException.h17
-rw-r--r--Swiften/FileTransfer/BytestreamsRequest.h24
-rw-r--r--Swiften/FileTransfer/FileReadBytestream.cpp40
-rw-r--r--Swiften/FileTransfer/FileReadBytestream.h27
-rw-r--r--Swiften/FileTransfer/FileTransferError.h27
-rw-r--r--Swiften/FileTransfer/IBBRequest.h24
-rw-r--r--Swiften/FileTransfer/IBBSendSession.cpp65
-rw-r--r--Swiften/FileTransfer/IBBSendSession.h49
-rw-r--r--Swiften/FileTransfer/OutgoingFileTransfer.cpp78
-rw-r--r--Swiften/FileTransfer/OutgoingFileTransfer.h52
-rw-r--r--Swiften/FileTransfer/ReadBytestream.cpp14
-rw-r--r--Swiften/FileTransfer/ReadBytestream.h18
-rw-r--r--Swiften/FileTransfer/SConscript13
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamRegistry.cpp32
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamRegistry.h29
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamServer.cpp51
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamServer.h49
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp118
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamServerSession.h54
-rw-r--r--Swiften/FileTransfer/StreamInitiationRequest.h24
-rw-r--r--Swiften/FileTransfer/UnitTest/IBBSendSessionTest.cpp164
-rw-r--r--Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp154
-rw-r--r--Swiften/Network/BoostConnection.cpp7
-rw-r--r--Swiften/Network/BoostConnection.h2
-rw-r--r--Swiften/Network/BoostConnectionServer.cpp9
-rw-r--r--Swiften/Network/BoostConnectionServer.h4
-rw-r--r--Swiften/Network/Connection.h6
-rw-r--r--Swiften/Network/ConnectionServer.h3
-rw-r--r--Swiften/Network/DummyConnection.h45
-rw-r--r--Swiften/Network/FakeConnection.h4
-rw-r--r--Swiften/Network/HostAddress.cpp61
-rw-r--r--Swiften/Network/HostAddress.h10
-rw-r--r--Swiften/Network/HostAddressPort.h19
-rw-r--r--Swiften/Network/UnitTest/ConnectorTest.cpp1
-rw-r--r--Swiften/Network/UnitTest/HostAddressTest.cpp24
-rw-r--r--Swiften/Parser/GenericPayloadParserFactory.h7
-rw-r--r--Swiften/Parser/PayloadParsers/BytestreamsParser.cpp48
-rw-r--r--Swiften/Parser/PayloadParsers/BytestreamsParser.h31
-rw-r--r--Swiften/Parser/PayloadParsers/BytestreamsParserFactory.h17
-rw-r--r--Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp6
-rw-r--r--Swiften/Parser/PayloadParsers/IBBParser.cpp77
-rw-r--r--Swiften/Parser/PayloadParsers/IBBParser.h31
-rw-r--r--Swiften/Parser/PayloadParsers/IBBParserFactory.h17
-rw-r--r--Swiften/Parser/PayloadParsers/StreamInitiationParser.cpp117
-rw-r--r--Swiften/Parser/PayloadParsers/StreamInitiationParser.h42
-rw-r--r--Swiften/Parser/PayloadParsers/StreamInitiationParserFactory.h17
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/IBBParserTest.cpp38
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/StreamInitiationParserTest.cpp75
-rw-r--r--Swiften/Parser/SConscript3
-rw-r--r--Swiften/QA/StorageTest/FileReadBytestreamTest.cpp73
-rw-r--r--Swiften/QA/StorageTest/SConscript1
-rw-r--r--Swiften/SConscript9
-rw-r--r--Swiften/Serializer/PayloadSerializers/BytestreamsSerializer.cpp41
-rw-r--r--Swiften/Serializer/PayloadSerializers/BytestreamsSerializer.h21
-rw-r--r--Swiften/Serializer/PayloadSerializers/FormSerializer.cpp4
-rw-r--r--Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp6
-rw-r--r--Swiften/Serializer/PayloadSerializers/IBBSerializer.cpp54
-rw-r--r--Swiften/Serializer/PayloadSerializers/IBBSerializer.h21
-rw-r--r--Swiften/Serializer/PayloadSerializers/StreamInitiationSerializer.cpp74
-rw-r--r--Swiften/Serializer/PayloadSerializers/StreamInitiationSerializer.h21
-rw-r--r--Swiften/Serializer/PayloadSerializers/UnitTest/FormSerializerTest.cpp4
-rw-r--r--Swiften/Serializer/PayloadSerializers/UnitTest/StreamInitiationSerializerTest.cpp69
76 files changed, 2637 insertions, 82 deletions
diff --git a/Swiften/Base/ByteArray.h b/Swiften/Base/ByteArray.h
index 21cfb87..09698ad 100644
--- a/Swiften/Base/ByteArray.h
+++ b/Swiften/Base/ByteArray.h
@@ -106,6 +106,10 @@ namespace Swift {
void readFromFile(const String& file);
+ void clear() {
+ data_.clear();
+ }
+
private:
std::vector<char> data_;
};
diff --git a/Swiften/Base/StartStopper.h b/Swiften/Base/StartStopper.h
new file mode 100644
index 0000000..7ea6049
--- /dev/null
+++ b/Swiften/Base/StartStopper.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+namespace Swift {
+ template<typename T> class StartStopper {
+ public:
+ StartStopper(T* target) : target(target) {
+ target->start();
+ }
+
+ ~StartStopper() {
+ target->stop();
+ }
+
+ private:
+ T* target;
+ };
+}
diff --git a/Swiften/Component/UnitTest/ComponentConnectorTest.cpp b/Swiften/Component/UnitTest/ComponentConnectorTest.cpp
index 7b8a4f8..4648365 100644
--- a/Swiften/Component/UnitTest/ComponentConnectorTest.cpp
+++ b/Swiften/Component/UnitTest/ComponentConnectorTest.cpp
@@ -177,6 +177,7 @@ class ComponentConnectorTest : public CppUnit::TestFixture {
void disconnect() { assert(false); }
void write(const ByteArray&) { assert(false); }
+ HostAddressPort getLocalAddress() const { return HostAddressPort(); }
boost::optional<HostAddressPort> hostAddressPort;
std::vector<HostAddressPort> failingPorts;
diff --git a/Swiften/Elements/Bytestreams.h b/Swiften/Elements/Bytestreams.h
new file mode 100644
index 0000000..323167c
--- /dev/null
+++ b/Swiften/Elements/Bytestreams.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <vector>
+#include <boost/optional.hpp>
+
+#include "Swiften/JID/JID.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/Base/Shared.h"
+#include "Swiften/Elements/Payload.h"
+
+namespace Swift {
+ class Bytestreams : public Payload, public Shared<Bytestreams> {
+ public:
+ struct StreamHost {
+ StreamHost(const String& host = "", const JID& jid = JID(), int port = -1) : host(host), jid(jid), port(port) {}
+
+ String host;
+ JID jid;
+ int port;
+ };
+
+ Bytestreams() {}
+
+ const String& getStreamID() const {
+ return id;
+ }
+
+ void setStreamID(const String& id) {
+ this->id = id;
+ }
+
+ const boost::optional<JID>& getUsedStreamHost() const {
+ return usedStreamHost;
+ }
+
+ void setUsedStreamHost(const JID& host) {
+ usedStreamHost = host;
+ }
+
+ const std::vector<StreamHost>& getStreamHosts() const {
+ return streamHosts;
+ }
+
+ void addStreamHost(const StreamHost& streamHost) {
+ streamHosts.push_back(streamHost);
+ }
+
+ private:
+ String id;
+ boost::optional<JID> usedStreamHost;
+ std::vector<StreamHost> streamHosts;
+ };
+}
diff --git a/Swiften/Elements/Form.cpp b/Swiften/Elements/Form.cpp
index 9420fb9..41014ba 100644
--- a/Swiften/Elements/Form.cpp
+++ b/Swiften/Elements/Form.cpp
@@ -10,13 +10,19 @@
namespace Swift {
String Form::getFormType() const {
+ FormField::ref field = getField("FORM_TYPE");
+ boost::shared_ptr<HiddenFormField> f = boost::dynamic_pointer_cast<HiddenFormField>(field);
+ return (f ? f->getValue() : "");
+}
+
+FormField::ref Form::getField(const String& name) const {
foreach(FormField::ref field, fields_) {
- boost::shared_ptr<HiddenFormField> f = boost::dynamic_pointer_cast<HiddenFormField>(field);
- if (f && f->getName() == "FORM_TYPE") {
- return f->getValue();
+ if (field->getName() == name) {
+ return field;
}
}
- return "";
+ return FormField::ref();
}
+
}
diff --git a/Swiften/Elements/Form.h b/Swiften/Elements/Form.h
index f5826a5..0eb6ef0 100644
--- a/Swiften/Elements/Form.h
+++ b/Swiften/Elements/Form.h
@@ -40,6 +40,8 @@ namespace Swift {
String getFormType() const;
+ FormField::ref getField(const String& name) const;
+
private:
std::vector<boost::shared_ptr<FormField> > fields_;
String title_;
diff --git a/Swiften/Elements/IBB.h b/Swiften/Elements/IBB.h
new file mode 100644
index 0000000..da9c18a
--- /dev/null
+++ b/Swiften/Elements/IBB.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Base/Shared.h"
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/Elements/Payload.h"
+
+namespace Swift {
+ class IBB : public Payload, public Shared<IBB> {
+ public:
+ enum Action {
+ Open,
+ Close,
+ Data,
+ };
+ enum StanzaType {
+ IQStanza,
+ MessageStanza,
+ };
+
+ IBB(Action action = Open, const String& streamID = "") : action(action), streamID(streamID), stanzaType(IQStanza), blockSize(-1), sequenceNumber(-1) {
+ }
+
+ static IBB::ref createIBBOpen(const String& streamID, int blockSize) {
+ IBB::ref result(new IBB(Open, streamID));
+ result->setBlockSize(blockSize);
+ return result;
+ }
+
+ static IBB::ref createIBBData(const String& streamID, int sequenceNumber, const ByteArray& data) {
+ IBB::ref result(new IBB(Data, streamID));
+ result->setSequenceNumber(sequenceNumber);
+ result->setData(data);
+ return result;
+ }
+
+ static IBB::ref createIBBClose(const String& streamID) {
+ return IBB::ref(new IBB(Close, streamID));
+ }
+
+ void setAction(Action action) {
+ this->action = action;
+ }
+
+ Action getAction() const {
+ return action;
+ }
+
+ void setStanzaType(StanzaType stanzaType) {
+ this->stanzaType = stanzaType;
+ }
+
+ StanzaType getStanzaType() const {
+ return stanzaType;
+ }
+
+ void setStreamID(const String& id) {
+ streamID = id;
+ }
+
+ const String& getStreamID() const {
+ return streamID;
+ }
+
+ const ByteArray& getData() const {
+ return data;
+ }
+
+ void setData(const ByteArray& data) {
+ this->data = data;
+ }
+
+ int getBlockSize() const {
+ return blockSize;
+ }
+
+ void setBlockSize(int blockSize) {
+ this->blockSize = blockSize;
+ }
+
+ int getSequenceNumber() const {
+ return sequenceNumber;
+ }
+
+ void setSequenceNumber(int i) {
+ sequenceNumber = i;
+ }
+
+ private:
+ Action action;
+ String streamID;
+ ByteArray data;
+ StanzaType stanzaType;
+ int blockSize;
+ int sequenceNumber;
+ };
+}
diff --git a/Swiften/Elements/StreamInitiation.h b/Swiften/Elements/StreamInitiation.h
new file mode 100644
index 0000000..fdf2399
--- /dev/null
+++ b/Swiften/Elements/StreamInitiation.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <vector>
+#include <boost/optional.hpp>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Base/Shared.h"
+#include "Swiften/Elements/Payload.h"
+
+namespace Swift {
+ class StreamInitiation : public Payload, public Shared<StreamInitiation> {
+ public:
+ struct FileInfo {
+ FileInfo(const String& name = "", const String& description = "", int size = -1) : name(name), description(description), size(size) {}
+
+ String name;
+ String description;
+ int size;
+ };
+
+ StreamInitiation() : isFileTransfer(true) {}
+
+ const String& getID() const {
+ return id;
+ }
+
+ void setID(const String& id) {
+ this->id = id;
+ }
+
+ const boost::optional<FileInfo>& getFileInfo() const {
+ return fileInfo;
+ }
+
+ void setFileInfo(const FileInfo& info) {
+ fileInfo = info;
+ }
+
+ const std::vector<String>& getProvidedMethods() const {
+ return providedMethods;
+ }
+
+ void addProvidedMethod(const String& method) {
+ providedMethods.push_back(method);
+ }
+
+ void setRequestedMethod(const String& method) {
+ requestedMethod = method;
+ }
+
+ const String& getRequestedMethod() const {
+ return requestedMethod;
+ }
+
+ bool getIsFileTransfer() const {
+ return isFileTransfer;
+ }
+
+ void setIsFileTransfer(bool b) {
+ isFileTransfer = b;
+ }
+
+ private:
+ bool isFileTransfer;
+ String id;
+ boost::optional<FileInfo> fileInfo;
+ std::vector<String> providedMethods;
+ String requestedMethod;
+ };
+}
diff --git a/Swiften/Elements/UnitTest/FormTest.cpp b/Swiften/Elements/UnitTest/FormTest.cpp
index 3852d98..715111b 100644
--- a/Swiften/Elements/UnitTest/FormTest.cpp
+++ b/Swiften/Elements/UnitTest/FormTest.cpp
@@ -15,6 +15,8 @@ using namespace Swift;
class FormTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(FormTest);
CPPUNIT_TEST(testGetFormType);
+ CPPUNIT_TEST(testGetFormType_InvalidFormType);
+ CPPUNIT_TEST(testGetFormType_NoFormType);
CPPUNIT_TEST_SUITE_END();
public:
@@ -31,6 +33,24 @@ class FormTest : public CppUnit::TestFixture {
CPPUNIT_ASSERT_EQUAL(String("jabber:bot"), form.getFormType());
}
+
+ void testGetFormType_InvalidFormType() {
+ Form form;
+
+ FormField::ref field = FixedFormField::create("jabber:bot");
+ field->setName("FORM_TYPE");
+ form.addField(field);
+
+ CPPUNIT_ASSERT_EQUAL(String(""), form.getFormType());
+ }
+
+ void testGetFormType_NoFormType() {
+ Form form;
+
+ form.addField(FixedFormField::create("Foo"));
+
+ CPPUNIT_ASSERT_EQUAL(String(""), form.getFormType());
+ }
};
CPPUNIT_TEST_SUITE_REGISTRATION(FormTest);
diff --git a/Swiften/Examples/SConscript b/Swiften/Examples/SConscript
index 61bedfb..7b9b491 100644
--- a/Swiften/Examples/SConscript
+++ b/Swiften/Examples/SConscript
@@ -4,6 +4,7 @@ myenv = swiften_env.Clone()
SConscript(dirs = [
"SendMessage",
+ "SendFile",
"ConnectivityTest",
"LinkLocalTool",
"ParserTester",
diff --git a/Swiften/Examples/SendFile/.gitignore b/Swiften/Examples/SendFile/.gitignore
new file mode 100644
index 0000000..f8b7625
--- /dev/null
+++ b/Swiften/Examples/SendFile/.gitignore
@@ -0,0 +1 @@
+SendFile
diff --git a/Swiften/Examples/SendFile/SConscript b/Swiften/Examples/SendFile/SConscript
new file mode 100644
index 0000000..50cbe40
--- /dev/null
+++ b/Swiften/Examples/SendFile/SConscript
@@ -0,0 +1,13 @@
+Import("env")
+
+myenv = env.Clone()
+myenv.MergeFlags(myenv["SWIFTEN_FLAGS"])
+myenv.MergeFlags(myenv["LIBIDN_FLAGS"])
+myenv.MergeFlags(myenv["BOOST_FLAGS"])
+myenv.MergeFlags(myenv["ZLIB_FLAGS"])
+myenv.MergeFlags(myenv["OPENSSL_FLAGS"])
+myenv.MergeFlags(myenv.get("SQLITE_FLAGS", {}))
+myenv.MergeFlags(myenv.get("LIBXML_FLAGS", ""))
+myenv.MergeFlags(myenv.get("EXPAT_FLAGS", ""))
+myenv.MergeFlags(myenv["PLATFORM_FLAGS"])
+tester = myenv.Program("SendFile", ["SendFile.cpp"])
diff --git a/Swiften/Examples/SendFile/SendFile.cpp b/Swiften/Examples/SendFile/SendFile.cpp
new file mode 100644
index 0000000..da0b2fe
--- /dev/null
+++ b/Swiften/Examples/SendFile/SendFile.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <boost/bind.hpp>
+#include <boost/filesystem.hpp>
+
+#include "Swiften/Client/Client.h"
+#include "Swiften/Network/BoostTimer.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+#include "Swiften/Client/ClientXMLTracer.h"
+#include "Swiften/EventLoop/SimpleEventLoop.h"
+#include "Swiften/Network/MainBoostIOServiceThread.h"
+#include "Swiften/FileTransfer/OutgoingFileTransfer.h"
+#include "Swiften/FileTransfer/FileReadBytestream.h"
+#include "Swiften/FileTransfer/SOCKS5BytestreamServer.h"
+#include "Swiften/Network/BoostConnectionServer.h"
+#include "Swiften/Network/BoostIOServiceThread.h"
+
+using namespace Swift;
+
+SimpleEventLoop eventLoop;
+int exitCode = 2;
+
+class FileSender {
+ public:
+ FileSender(const JID& jid, const String& password, const JID& recipient, const boost::filesystem::path& file, int port) : jid(jid), password(password), recipient(recipient), file(file), transfer(NULL) {
+ connectionServer = BoostConnectionServer::create(port, &MainBoostIOServiceThread::getInstance().getIOService());
+ socksBytestreamServer = new SOCKS5BytestreamServer(connectionServer);
+
+ client = new Swift::Client(jid, password);
+ client->onConnected.connect(boost::bind(&FileSender::handleConnected, this));
+ client->onError.connect(boost::bind(&FileSender::handleError, this, _1));
+ //tracer = new ClientXMLTracer(client);
+ }
+
+ ~FileSender() {
+ //delete tracer;
+ client->onError.disconnect(boost::bind(&FileSender::handleError, this, _1));
+ client->onConnected.disconnect(boost::bind(&FileSender::handleConnected, this));
+ delete client;
+ delete socksBytestreamServer;
+ }
+
+ void start() {
+ connectionServer->start();
+ socksBytestreamServer->start();
+ client->connect();
+ }
+
+ void stop() {
+ if (transfer) {
+ transfer->stop();
+ }
+ client->disconnect();
+ socksBytestreamServer->stop();
+ connectionServer->stop();
+ }
+
+ private:
+ void handleConnected() {
+ client->sendPresence(Presence::create());
+ transfer = new OutgoingFileTransfer("myid", client->getJID(), recipient, file.filename(), boost::filesystem::file_size(file), "A file", boost::shared_ptr<FileReadBytestream>(new FileReadBytestream(file)), client->getIQRouter(), socksBytestreamServer);
+ transfer->onFinished.connect(boost::bind(&FileSender::handleFileTransferFinished, this, _1));
+ transfer->start();
+ }
+
+ void handleError(const ClientError&) {
+ std::cerr << "Error!" << std::endl;
+ exit(-1);
+ }
+
+ void handleFileTransferFinished(const boost::optional<FileTransferError>& error) {
+ std::cout << "File transfer finished" << std::endl;
+ if (error) {
+ exit(-1);
+ }
+ else {
+ exit(0);
+ }
+ }
+
+ void exit(int code) {
+ exitCode = code;
+ stop();
+ eventLoop.stop();
+ }
+
+ private:
+ BoostConnectionServer::ref connectionServer;
+ SOCKS5BytestreamServer* socksBytestreamServer;
+ JID jid;
+ String password;
+ JID recipient;
+ boost::filesystem::path file;
+ Client* client;
+ ClientXMLTracer* tracer;
+ OutgoingFileTransfer* transfer;
+};
+
+
+int main(int argc, char* argv[]) {
+ if (argc != 5) {
+ std::cerr << "Usage: " << argv[0] << " <jid> <password> <recipient> <file>" << std::endl;
+ return -1;
+ }
+
+ JID sender(argv[1]);
+ JID recipient(argv[3]);
+ FileSender fileSender(sender, String(argv[2]), recipient, boost::filesystem::path(argv[4]), 8888);
+ fileSender.start();
+
+ {
+ /*BoostTimer::ref timer(BoostTimer::create(30000, &MainBoostIOServiceThread::getInstance().getIOService()));
+ timer->onTick.connect(boost::bind(&SimpleEventLoop::stop, &eventLoop));
+ timer->start();*/
+
+ eventLoop.run();
+ }
+
+ return exitCode;
+}
diff --git a/Swiften/FileTransfer/ByteArrayReadBytestream.h b/Swiften/FileTransfer/ByteArrayReadBytestream.h
new file mode 100644
index 0000000..d459658
--- /dev/null
+++ b/Swiften/FileTransfer/ByteArrayReadBytestream.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include "Swiften/FileTransfer/ReadBytestream.h"
+#include "Swiften/Base/ByteArray.h"
+
+namespace Swift {
+ class ByteArrayReadBytestream : public ReadBytestream {
+ public:
+ ByteArrayReadBytestream(const ByteArray& data) : data(data), position(0) {
+ }
+
+ virtual ByteArray read(size_t size) {
+ size_t readSize = size;
+ if (position + readSize > data.getSize()) {
+ readSize = data.getSize() - position;
+ }
+ ByteArray result(data.getData() + position, readSize);
+ position += readSize;
+ return result;
+ }
+
+ virtual bool isFinished() const {
+ return position >= data.getSize();
+ }
+
+ private:
+ ByteArray data;
+ size_t position;
+ };
+}
diff --git a/Swiften/FileTransfer/BytestreamException.h b/Swiften/FileTransfer/BytestreamException.h
new file mode 100644
index 0000000..f38ef86
--- /dev/null
+++ b/Swiften/FileTransfer/BytestreamException.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <stdexcept>
+
+namespace Swift {
+ class BytestreamException : public std::exception {
+ public:
+ BytestreamException() {
+ }
+ };
+}
diff --git a/Swiften/FileTransfer/BytestreamsRequest.h b/Swiften/FileTransfer/BytestreamsRequest.h
new file mode 100644
index 0000000..71b93ec
--- /dev/null
+++ b/Swiften/FileTransfer/BytestreamsRequest.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include "Swiften/Queries/GenericRequest.h"
+#include "Swiften/Elements/Bytestreams.h"
+#include "Swiften/Base/Shared.h"
+
+namespace Swift {
+ class BytestreamsRequest : public GenericRequest<Bytestreams>, public Shared<BytestreamsRequest> {
+ public:
+ static ref create(const JID& jid, boost::shared_ptr<Bytestreams> payload, IQRouter* router) {
+ return ref(new BytestreamsRequest(jid, payload, router));
+ }
+
+ private:
+ BytestreamsRequest(const JID& jid, boost::shared_ptr<Bytestreams> payload, IQRouter* router) : GenericRequest<Bytestreams>(IQ::Set, jid, payload, router) {
+ }
+ };
+}
diff --git a/Swiften/FileTransfer/FileReadBytestream.cpp b/Swiften/FileTransfer/FileReadBytestream.cpp
new file mode 100644
index 0000000..7ed33af
--- /dev/null
+++ b/Swiften/FileTransfer/FileReadBytestream.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <boost/filesystem/fstream.hpp>
+#include <cassert>
+
+#include "Swiften/FileTransfer/FileReadBytestream.h"
+
+namespace Swift {
+
+FileReadBytestream::FileReadBytestream(const boost::filesystem::path& file) : file(file), stream(NULL) {
+}
+
+FileReadBytestream::~FileReadBytestream() {
+ if (stream) {
+ stream->close();
+ stream = NULL;
+ }
+}
+
+ByteArray FileReadBytestream::read(size_t size) {
+ if (!stream) {
+ stream = new boost::filesystem::ifstream(file, std::ios_base::in|std::ios_base::binary);
+ }
+ ByteArray result;
+ result.resize(size);
+ assert(stream->good());
+ stream->read(result.getData(), size);
+ result.resize(stream->gcount());
+ return result;
+}
+
+bool FileReadBytestream::isFinished() const {
+ return stream && !stream->good();
+}
+
+}
diff --git a/Swiften/FileTransfer/FileReadBytestream.h b/Swiften/FileTransfer/FileReadBytestream.h
new file mode 100644
index 0000000..055e194
--- /dev/null
+++ b/Swiften/FileTransfer/FileReadBytestream.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/filesystem.hpp>
+#include <boost/filesystem/fstream.hpp>
+
+#include "Swiften/FileTransfer/ReadBytestream.h"
+
+namespace Swift {
+ class FileReadBytestream : public ReadBytestream {
+ public:
+ FileReadBytestream(const boost::filesystem::path& file);
+ ~FileReadBytestream();
+
+ virtual ByteArray read(size_t size) ;
+ virtual bool isFinished() const;
+
+ private:
+ boost::filesystem::path file;
+ boost::filesystem::ifstream* stream;
+ };
+}
diff --git a/Swiften/FileTransfer/FileTransferError.h b/Swiften/FileTransfer/FileTransferError.h
new file mode 100644
index 0000000..b718927
--- /dev/null
+++ b/Swiften/FileTransfer/FileTransferError.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+namespace Swift {
+ class FileTransferError {
+ public:
+ enum Type {
+ UnknownError,
+ PeerError,
+ ReadError,
+ };
+
+ FileTransferError(Type type = UnknownError) : type(type) {}
+
+ Type getType() const {
+ return type;
+ }
+
+ private:
+ Type type;
+ };
+}
diff --git a/Swiften/FileTransfer/IBBRequest.h b/Swiften/FileTransfer/IBBRequest.h
new file mode 100644
index 0000000..7269362
--- /dev/null
+++ b/Swiften/FileTransfer/IBBRequest.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include "Swiften/Queries/GenericRequest.h"
+#include "Swiften/Elements/IBB.h"
+#include "Swiften/Base/Shared.h"
+
+namespace Swift {
+ class IBBRequest : public GenericRequest<IBB>, public Shared<IBBRequest> {
+ public:
+ static ref create(const JID& jid, boost::shared_ptr<IBB> payload, IQRouter* router) {
+ return ref(new IBBRequest(jid, payload, router));
+ }
+
+ private:
+ IBBRequest(const JID& jid, boost::shared_ptr<IBB> payload, IQRouter* router) : GenericRequest<IBB>(IQ::Set, jid, payload, router) {
+ }
+ };
+}
diff --git a/Swiften/FileTransfer/IBBSendSession.cpp b/Swiften/FileTransfer/IBBSendSession.cpp
new file mode 100644
index 0000000..7246187
--- /dev/null
+++ b/Swiften/FileTransfer/IBBSendSession.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Swiften/FileTransfer/IBBSendSession.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Queries/IQRouter.h"
+#include "Swiften/FileTransfer/IBBRequest.h"
+#include "Swiften/FileTransfer/BytestreamException.h"
+
+namespace Swift {
+
+IBBSendSession::IBBSendSession(const String& id, const JID& to, boost::shared_ptr<ReadBytestream> bytestream, IQRouter* router) : id(id), to(to), bytestream(bytestream), router(router), blockSize(4096), sequenceNumber(0), active(false) {
+}
+
+IBBSendSession::~IBBSendSession() {
+}
+
+void IBBSendSession::start() {
+ IBBRequest::ref request = IBBRequest::create(to, IBB::createIBBOpen(id, blockSize), router);
+ request->onResponse.connect(boost::bind(&IBBSendSession::handleIBBResponse, this, _1, _2));
+ active = true;
+ request->send();
+}
+
+void IBBSendSession::stop() {
+ if (active && router->isAvailable()) {
+ IBBRequest::create(to, IBB::createIBBClose(id), router)->send();
+ }
+ finish(boost::optional<FileTransferError>());
+}
+
+void IBBSendSession::handleIBBResponse(IBB::ref, const boost::optional<ErrorPayload>& error) {
+ if (!error) {
+ if (!bytestream->isFinished()) {
+ try {
+ ByteArray data = bytestream->read(blockSize);
+ IBBRequest::ref request = IBBRequest::create(to, IBB::createIBBData(id, sequenceNumber, data), router);
+ sequenceNumber++;
+ request->onResponse.connect(boost::bind(&IBBSendSession::handleIBBResponse, this, _1, _2));
+ request->send();
+ }
+ catch (const BytestreamException& e) {
+ finish(FileTransferError(FileTransferError::ReadError));
+ }
+ }
+ else {
+ finish(boost::optional<FileTransferError>());
+ }
+ }
+ else {
+ finish(FileTransferError(FileTransferError::PeerError));
+ }
+}
+
+void IBBSendSession::finish(boost::optional<FileTransferError> error) {
+ active = false;
+ onFinished(error);
+}
+
+}
diff --git a/Swiften/FileTransfer/IBBSendSession.h b/Swiften/FileTransfer/IBBSendSession.h
new file mode 100644
index 0000000..102bae2
--- /dev/null
+++ b/Swiften/FileTransfer/IBBSendSession.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
+
+#include "Swiften/Base/boost_bsignals.h"
+#include "Swiften/FileTransfer/ReadBytestream.h"
+#include "Swiften/JID/JID.h"
+#include "Swiften/Elements/IBB.h"
+#include "Swiften/Elements/ErrorPayload.h"
+#include "Swiften/FileTransfer/FileTransferError.h"
+
+namespace Swift {
+ class IQRouter;
+
+ class IBBSendSession {
+ public:
+ IBBSendSession(const String& id, const JID& to, boost::shared_ptr<ReadBytestream> bytestream, IQRouter* router);
+ ~IBBSendSession();
+
+ void start();
+ void stop();
+
+ void setBlockSize(int blockSize) {
+ this->blockSize = blockSize;
+ }
+
+ boost::signal<void (boost::optional<FileTransferError>)> onFinished;
+
+ private:
+ void handleIBBResponse(IBB::ref, const boost::optional<ErrorPayload>&);
+ void finish(boost::optional<FileTransferError>);
+
+ private:
+ String id;
+ JID to;
+ boost::shared_ptr<ReadBytestream> bytestream;
+ IQRouter* router;
+ int blockSize;
+ int sequenceNumber;
+ bool active;
+ };
+}
diff --git a/Swiften/FileTransfer/OutgoingFileTransfer.cpp b/Swiften/FileTransfer/OutgoingFileTransfer.cpp
new file mode 100644
index 0000000..9f1dd1b
--- /dev/null
+++ b/Swiften/FileTransfer/OutgoingFileTransfer.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Swiften/FileTransfer/OutgoingFileTransfer.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/FileTransfer/StreamInitiationRequest.h"
+#include "Swiften/FileTransfer/BytestreamsRequest.h"
+#include "Swiften/FileTransfer/SOCKS5BytestreamServer.h"
+#include "Swiften/FileTransfer/IBBSendSession.h"
+
+namespace Swift {
+
+OutgoingFileTransfer::OutgoingFileTransfer(const String& id, const JID& from, const JID& to, const String& name, int size, const String& description, boost::shared_ptr<ReadBytestream> bytestream, IQRouter* iqRouter, SOCKS5BytestreamServer* socksServer) : id(id), from(from), to(to), name(name), size(size), description(description), bytestream(bytestream), iqRouter(iqRouter), socksServer(socksServer) {
+}
+
+void OutgoingFileTransfer::start() {
+ StreamInitiation::ref streamInitiation(new StreamInitiation());
+ streamInitiation->setID(id);
+ streamInitiation->setFileInfo(StreamInitiation::FileInfo(name, description, size));
+ //streamInitiation->addProvidedMethod("http://jabber.org/protocol/bytestreams");
+ streamInitiation->addProvidedMethod("http://jabber.org/protocol/ibb");
+ StreamInitiationRequest::ref request = StreamInitiationRequest::create(to, streamInitiation, iqRouter);
+ request->onResponse.connect(boost::bind(&OutgoingFileTransfer::handleStreamInitiationRequestResponse, this, _1, _2));
+ request->send();
+}
+
+void OutgoingFileTransfer::stop() {
+}
+
+void OutgoingFileTransfer::handleStreamInitiationRequestResponse(StreamInitiation::ref response, const boost::optional<ErrorPayload>& error) {
+ if (error) {
+ finish(FileTransferError());
+ }
+ else {
+ if (response->getRequestedMethod() == "http://jabber.org/protocol/bytestreams") {
+ socksServer->addBytestream(id, from, to, bytestream);
+ Bytestreams::ref bytestreams(new Bytestreams());
+ bytestreams->setStreamID(id);
+ HostAddressPort addressPort = socksServer->getAddressPort();
+ bytestreams->addStreamHost(Bytestreams::StreamHost(addressPort.getAddress().toString(), from, addressPort.getPort()));
+ BytestreamsRequest::ref request = BytestreamsRequest::create(to, bytestreams, iqRouter);
+ request->onResponse.connect(boost::bind(&OutgoingFileTransfer::handleBytestreamsRequestResponse, this, _1, _2));
+ request->send();
+ }
+ else if (response->getRequestedMethod() == "http://jabber.org/protocol/ibb") {
+ ibbSession = boost::shared_ptr<IBBSendSession>(new IBBSendSession(id, to, bytestream, iqRouter));
+ ibbSession->onFinished.connect(boost::bind(&OutgoingFileTransfer::handleIBBSessionFinished, this, _1));
+ ibbSession->start();
+ }
+ }
+}
+
+void OutgoingFileTransfer::handleBytestreamsRequestResponse(Bytestreams::ref, const boost::optional<ErrorPayload>& error) {
+ if (error) {
+ finish(FileTransferError());
+ }
+ //socksServer->onTransferFinished.connect();
+}
+
+void OutgoingFileTransfer::finish(boost::optional<FileTransferError> error) {
+ if (ibbSession) {
+ ibbSession->onFinished.disconnect(boost::bind(&OutgoingFileTransfer::handleIBBSessionFinished, this, _1));
+ ibbSession.reset();
+ }
+ socksServer->removeBytestream(id, from, to);
+ onFinished(error);
+}
+
+void OutgoingFileTransfer::handleIBBSessionFinished(boost::optional<FileTransferError> error) {
+ finish(error);
+}
+
+}
diff --git a/Swiften/FileTransfer/OutgoingFileTransfer.h b/Swiften/FileTransfer/OutgoingFileTransfer.h
new file mode 100644
index 0000000..cfbfe21
--- /dev/null
+++ b/Swiften/FileTransfer/OutgoingFileTransfer.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/FileTransfer/ReadBytestream.h"
+#include "Swiften/Base/boost_bsignals.h"
+#include "Swiften/FileTransfer/FileTransferError.h"
+#include "Swiften/FileTransfer/SOCKS5BytestreamServer.h"
+#include "Swiften/JID/JID.h"
+#include "Swiften/Elements/StreamInitiation.h"
+#include "Swiften/Elements/Bytestreams.h"
+#include "Swiften/Elements/ErrorPayload.h"
+#include "Swiften/FileTransfer/IBBSendSession.h"
+
+namespace Swift {
+ class IQRouter;
+ class SOCKS5BytestreamServer;
+
+ class OutgoingFileTransfer {
+ public:
+ OutgoingFileTransfer(const String& id, const JID& from, const JID& to, const String& name, int size, const String& description, boost::shared_ptr<ReadBytestream> bytestream, IQRouter* iqRouter, SOCKS5BytestreamServer* socksServer);
+
+ void start();
+ void stop();
+
+ boost::signal<void (const boost::optional<FileTransferError>&)> onFinished;
+
+ private:
+ void handleStreamInitiationRequestResponse(StreamInitiation::ref, const boost::optional<ErrorPayload>&);
+ void handleBytestreamsRequestResponse(Bytestreams::ref, const boost::optional<ErrorPayload>&);
+ void finish(boost::optional<FileTransferError> error);
+ void handleIBBSessionFinished(boost::optional<FileTransferError> error);
+
+ private:
+ String id;
+ JID from;
+ JID to;
+ String name;
+ int size;
+ String description;
+ boost::shared_ptr<ReadBytestream> bytestream;
+ IQRouter* iqRouter;
+ SOCKS5BytestreamServer* socksServer;
+ boost::shared_ptr<IBBSendSession> ibbSession;
+ };
+}
diff --git a/Swiften/FileTransfer/ReadBytestream.cpp b/Swiften/FileTransfer/ReadBytestream.cpp
new file mode 100644
index 0000000..705906c
--- /dev/null
+++ b/Swiften/FileTransfer/ReadBytestream.cpp
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Swiften/FileTransfer/ReadBytestream.h"
+
+namespace Swift {
+
+ReadBytestream::~ReadBytestream() {
+}
+
+}
diff --git a/Swiften/FileTransfer/ReadBytestream.h b/Swiften/FileTransfer/ReadBytestream.h
new file mode 100644
index 0000000..4da2bc2
--- /dev/null
+++ b/Swiften/FileTransfer/ReadBytestream.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include "Swiften/Base/ByteArray.h"
+
+namespace Swift {
+ class ReadBytestream {
+ public:
+ virtual ~ReadBytestream();
+ virtual ByteArray read(size_t size) = 0;
+ virtual bool isFinished() const = 0;
+ };
+}
diff --git a/Swiften/FileTransfer/SConscript b/Swiften/FileTransfer/SConscript
new file mode 100644
index 0000000..f42dd88
--- /dev/null
+++ b/Swiften/FileTransfer/SConscript
@@ -0,0 +1,13 @@
+Import("swiften_env")
+
+sources = [
+ "OutgoingFileTransfer.cpp",
+ "ReadBytestream.cpp",
+ "FileReadBytestream.cpp",
+ "SOCKS5BytestreamServer.cpp",
+ "SOCKS5BytestreamServerSession.cpp",
+ "SOCKS5BytestreamRegistry.cpp",
+ "IBBSendSession.cpp",
+ ]
+
+swiften_env.Append(SWIFTEN_OBJECTS = swiften_env.StaticObject(sources))
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamRegistry.cpp b/Swiften/FileTransfer/SOCKS5BytestreamRegistry.cpp
new file mode 100644
index 0000000..a4715a0
--- /dev/null
+++ b/Swiften/FileTransfer/SOCKS5BytestreamRegistry.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Swiften/FileTransfer/SOCKS5BytestreamRegistry.h"
+
+namespace Swift {
+
+SOCKS5BytestreamRegistry::SOCKS5BytestreamRegistry() {
+}
+
+void SOCKS5BytestreamRegistry::addBytestream(const String& destination, boost::shared_ptr<ReadBytestream> byteStream) {
+ byteStreams[destination] = byteStream;
+}
+
+void SOCKS5BytestreamRegistry::removeBytestream(const String& destination) {
+ byteStreams.erase(destination);
+}
+
+boost::shared_ptr<ReadBytestream> SOCKS5BytestreamRegistry::getBytestream(const String& destination) const {
+ BytestreamMap::const_iterator i = byteStreams.find(destination);
+ if (i != byteStreams.end()) {
+ return i->second;
+ }
+ return boost::shared_ptr<ReadBytestream>();
+}
+
+
+}
+
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamRegistry.h b/Swiften/FileTransfer/SOCKS5BytestreamRegistry.h
new file mode 100644
index 0000000..1afd03f
--- /dev/null
+++ b/Swiften/FileTransfer/SOCKS5BytestreamRegistry.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <map>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/FileTransfer/ReadBytestream.h"
+
+namespace Swift {
+ class SOCKS5BytestreamRegistry {
+ public:
+ SOCKS5BytestreamRegistry();
+
+ boost::shared_ptr<ReadBytestream> getBytestream(const String& destination) const;
+ void addBytestream(const String& destination, boost::shared_ptr<ReadBytestream> byteStream);
+ void removeBytestream(const String& destination);
+
+ private:
+ typedef std::map<String, boost::shared_ptr<ReadBytestream> > BytestreamMap;
+ BytestreamMap byteStreams;
+ };
+}
+
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServer.cpp b/Swiften/FileTransfer/SOCKS5BytestreamServer.cpp
new file mode 100644
index 0000000..58506f3
--- /dev/null
+++ b/Swiften/FileTransfer/SOCKS5BytestreamServer.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Swiften/FileTransfer/SOCKS5BytestreamServer.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/StringCodecs/Hexify.h"
+#include "Swiften/StringCodecs/SHA1.h"
+#include "Swiften/FileTransfer/SOCKS5BytestreamServerSession.h"
+
+namespace Swift {
+
+SOCKS5BytestreamServer::SOCKS5BytestreamServer(boost::shared_ptr<ConnectionServer> connectionServer) : connectionServer(connectionServer) {
+}
+
+void SOCKS5BytestreamServer::start() {
+ connectionServer->onNewConnection.connect(boost::bind(&SOCKS5BytestreamServer::handleNewConnection, this, _1));
+}
+
+void SOCKS5BytestreamServer::stop() {
+ connectionServer->onNewConnection.disconnect(boost::bind(&SOCKS5BytestreamServer::handleNewConnection, this, _1));
+}
+
+void SOCKS5BytestreamServer::addBytestream(const String& id, const JID& from, const JID& to, boost::shared_ptr<ReadBytestream> byteStream) {
+ bytestreams.addBytestream(getSOCKSDestinationAddress(id, from, to), byteStream);
+}
+
+void SOCKS5BytestreamServer::removeBytestream(const String& id, const JID& from, const JID& to) {
+ bytestreams.removeBytestream(getSOCKSDestinationAddress(id, from, to));
+}
+
+String SOCKS5BytestreamServer::getSOCKSDestinationAddress(const String& id, const JID& from, const JID& to) {
+ return Hexify::hexify(SHA1::getHash(ByteArray(id + from.toString() + to.toString())));
+}
+
+void SOCKS5BytestreamServer::handleNewConnection(boost::shared_ptr<Connection> connection) {
+ boost::shared_ptr<SOCKS5BytestreamServerSession> session(new SOCKS5BytestreamServerSession(connection, &bytestreams));
+ sessions.push_back(session);
+ session->start();
+}
+
+HostAddressPort SOCKS5BytestreamServer::getAddressPort() const {
+ return connectionServer->getAddressPort();
+}
+
+}
+
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServer.h b/Swiften/FileTransfer/SOCKS5BytestreamServer.h
new file mode 100644
index 0000000..35a8d4f
--- /dev/null
+++ b/Swiften/FileTransfer/SOCKS5BytestreamServer.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <map>
+
+#include "Swiften/Network/ConnectionServer.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/JID/JID.h"
+#include "Swiften/FileTransfer/ReadBytestream.h"
+#include "Swiften/FileTransfer/SOCKS5BytestreamRegistry.h"
+
+namespace Swift {
+ class SOCKS5BytestreamServerSession;
+
+ class SOCKS5BytestreamServer {
+ public:
+ SOCKS5BytestreamServer(boost::shared_ptr<ConnectionServer> connectionServer);
+
+ HostAddressPort getAddressPort() const;
+
+ void start();
+ void stop();
+
+ void addBytestream(const String& id, const JID& from, const JID& to, boost::shared_ptr<ReadBytestream> byteStream);
+ void removeBytestream(const String& id, const JID& from, const JID& to);
+
+ /*protected:
+ boost::shared_ptr<ReadBytestream> getBytestream(const String& dest);*/
+
+ private:
+ void handleNewConnection(boost::shared_ptr<Connection> connection);
+
+ static String getSOCKSDestinationAddress(const String& id, const JID& from, const JID& to);
+
+ private:
+ friend class SOCKS5BytestreamServerSession;
+
+ boost::shared_ptr<ConnectionServer> connectionServer;
+ SOCKS5BytestreamRegistry bytestreams;
+ std::vector<boost::shared_ptr<SOCKS5BytestreamServerSession> > sessions;
+ };
+}
+
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp b/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp
new file mode 100644
index 0000000..d22845a
--- /dev/null
+++ b/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Swiften/FileTransfer/SOCKS5BytestreamServerSession.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/FileTransfer/SOCKS5BytestreamRegistry.h"
+#include "Swiften/FileTransfer/BytestreamException.h"
+
+namespace Swift {
+
+SOCKS5BytestreamServerSession::SOCKS5BytestreamServerSession(boost::shared_ptr<Connection> connection, SOCKS5BytestreamRegistry* bytestreams) : connection(connection), bytestreams(bytestreams), state(Initial), chunkSize(4096) {
+}
+
+SOCKS5BytestreamServerSession::~SOCKS5BytestreamServerSession() {
+ if (state != Finished && state != Initial) {
+ std::cerr << "Warning: SOCKS5BytestremServerSession unfinished" << std::endl;
+ finish(false);
+ }
+}
+
+void SOCKS5BytestreamServerSession::start() {
+ connection->onDataRead.connect(boost::bind(&SOCKS5BytestreamServerSession::handleDataRead, this, _1));
+ state = WaitingForAuthentication;
+}
+
+void SOCKS5BytestreamServerSession::stop() {
+ finish(false);
+}
+
+void SOCKS5BytestreamServerSession::handleDataRead(const ByteArray& data) {
+ unprocessedData += data;
+ process();
+}
+
+void SOCKS5BytestreamServerSession::process() {
+ if (state == WaitingForAuthentication) {
+ if (unprocessedData.getSize() >= 2) {
+ int authCount = unprocessedData[1];
+ int i = 2;
+ while (i < 2 + authCount && i < unprocessedData.getSize()) {
+ // Skip authentication mechanism
+ ++i;
+ }
+ if (i == 2 + authCount) {
+ // Authentication message is complete
+ if (i != unprocessedData.getSize()) {
+ std::cerr << "SOCKS5BytestreamServerSession: Junk after authentication mechanism";
+ }
+ unprocessedData.clear();
+ connection->write(ByteArray("\x05\x00", 2));
+ state = WaitingForRequest;
+ }
+ }
+ }
+ else if (state == WaitingForRequest) {
+ if (unprocessedData.getSize() >= 5) {
+ ByteArray requestID;
+ int i = 5;
+ int hostnameSize = unprocessedData[4];
+ while (i < 5 + hostnameSize && i < unprocessedData.getSize()) {
+ requestID += unprocessedData[i];
+ ++i;
+ }
+ // Skip the port:
+ i += 2;
+ if (i >= unprocessedData.getSize()) {
+ if (i != unprocessedData.getSize()) {
+ std::cerr << "SOCKS5BytestreamServerSession: Junk after authentication mechanism";
+ }
+ bytestream = bytestreams->getBytestream(requestID.toString());
+ ByteArray result("\x05", 1);
+ result += bytestream ? 0x0 : 0x4;
+ result += ByteArray("\x00\x03", 2);
+ result += static_cast<char>(requestID.getSize());
+ result += requestID + ByteArray("\x00\x00", 2);
+ if (!bytestream) {
+ connection->write(result);
+ finish(true);
+ }
+ else {
+ state = SendingData;
+ connection->onDataWritten.connect(boost::bind(&SOCKS5BytestreamServerSession::sendData, this));
+ connection->write(result);
+ }
+ }
+ }
+ }
+}
+
+void SOCKS5BytestreamServerSession::sendData() {
+ if (!bytestream->isFinished()) {
+ try {
+ connection->write(bytestream->read(chunkSize));
+ }
+ catch (const BytestreamException& e) {
+ finish(true);
+ }
+ }
+ else {
+ finish(false);
+ }
+}
+
+void SOCKS5BytestreamServerSession::finish(bool error) {
+ connection->onDataWritten.disconnect(boost::bind(&SOCKS5BytestreamServerSession::sendData, this));
+ connection->onDataRead.disconnect(boost::bind(&SOCKS5BytestreamServerSession::handleDataRead, this, _1));
+ bytestream.reset();
+ state = Finished;
+ onFinished(error);
+}
+
+}
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServerSession.h b/Swiften/FileTransfer/SOCKS5BytestreamServerSession.h
new file mode 100644
index 0000000..f430f5d
--- /dev/null
+++ b/Swiften/FileTransfer/SOCKS5BytestreamServerSession.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Base/boost_bsignals.h"
+#include "Swiften/Network/Connection.h"
+#include "Swiften/FileTransfer/ReadBytestream.h"
+
+namespace Swift {
+ class SOCKS5BytestreamRegistry;
+
+ class SOCKS5BytestreamServerSession {
+ public:
+ enum State {
+ Initial,
+ WaitingForAuthentication,
+ WaitingForRequest,
+ SendingData,
+ Finished,
+ };
+
+ SOCKS5BytestreamServerSession(boost::shared_ptr<Connection> connection, SOCKS5BytestreamRegistry* registry);
+ ~SOCKS5BytestreamServerSession();
+
+ void setChunkSize(int chunkSize) {
+ this->chunkSize = chunkSize;
+ }
+
+ void start();
+ void stop();
+
+ boost::signal<void (bool /* error */)> onFinished;
+
+ private:
+ void finish(bool error);
+ void process();
+ void handleDataRead(const ByteArray&);
+ void sendData();
+
+ private:
+ boost::shared_ptr<Connection> connection;
+ SOCKS5BytestreamRegistry* bytestreams;
+ ByteArray unprocessedData;
+ State state;
+ int chunkSize;
+ boost::shared_ptr<ReadBytestream> bytestream;
+ };
+}
diff --git a/Swiften/FileTransfer/StreamInitiationRequest.h b/Swiften/FileTransfer/StreamInitiationRequest.h
new file mode 100644
index 0000000..64f51f2
--- /dev/null
+++ b/Swiften/FileTransfer/StreamInitiationRequest.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include "Swiften/Queries/GenericRequest.h"
+#include "Swiften/Elements/StreamInitiation.h"
+#include "Swiften/Base/Shared.h"
+
+namespace Swift {
+ class StreamInitiationRequest : public GenericRequest<StreamInitiation>, public Shared<StreamInitiationRequest> {
+ public:
+ static ref create(const JID& jid, boost::shared_ptr<StreamInitiation> payload, IQRouter* router) {
+ return ref(new StreamInitiationRequest(jid, payload, router));
+ }
+
+ private:
+ StreamInitiationRequest(const JID& jid, boost::shared_ptr<StreamInitiation> payload, IQRouter* router) : GenericRequest<StreamInitiation>(IQ::Set, jid, payload, router) {
+ }
+ };
+}
diff --git a/Swiften/FileTransfer/UnitTest/IBBSendSessionTest.cpp b/Swiften/FileTransfer/UnitTest/IBBSendSessionTest.cpp
new file mode 100644
index 0000000..9052439
--- /dev/null
+++ b/Swiften/FileTransfer/UnitTest/IBBSendSessionTest.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <vector>
+#include <boost/bind.hpp>
+
+#include "Swiften/FileTransfer/IBBSendSession.h"
+#include "Swiften/FileTransfer/ByteArrayReadBytestream.h"
+#include "Swiften/Queries/IQRouter.h"
+#include "Swiften/Client/DummyStanzaChannel.h"
+
+using namespace Swift;
+
+class IBBSendSessionTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(IBBSendSessionTest);
+ CPPUNIT_TEST(testStart);
+ CPPUNIT_TEST(testStart_ResponseStartsSending);
+ CPPUNIT_TEST(testResponseContinuesSending);
+ CPPUNIT_TEST(testRespondToAllFinishes);
+ CPPUNIT_TEST(testErrorResponseFinishesWithError);
+ CPPUNIT_TEST(testStopDuringSessionCloses);
+ CPPUNIT_TEST(testStopAfterFinishedDoesNotClose);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void setUp() {
+ stanzaChannel = new DummyStanzaChannel();
+ iqRouter = new IQRouter(stanzaChannel);
+ bytestream = boost::shared_ptr<ByteArrayReadBytestream>(new ByteArrayReadBytestream(ByteArray("abcdefg")));
+ }
+
+ void tearDown() {
+ delete iqRouter;
+ delete stanzaChannel;
+ }
+
+ void testStart() {
+ std::auto_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz");
+ testling->setBlockSize(1234);
+
+ testling->start();
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(stanzaChannel->sentStanzas.size()));
+ CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<IBB>(0, JID("foo@bar.com/baz"), IQ::Set));
+ IBB::ref ibb = stanzaChannel->sentStanzas[0]->getPayload<IBB>();
+ CPPUNIT_ASSERT_EQUAL(IBB::Open, ibb->getAction());
+ CPPUNIT_ASSERT_EQUAL(1234, ibb->getBlockSize());
+ CPPUNIT_ASSERT_EQUAL(String("myid"), ibb->getStreamID());
+ }
+
+ void testStart_ResponseStartsSending() {
+ std::auto_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz");
+ testling->setBlockSize(3);
+ testling->start();
+
+ stanzaChannel->onIQReceived(createIBBResult());
+
+ CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(stanzaChannel->sentStanzas.size()));
+ CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<IBB>(1, JID("foo@bar.com/baz"), IQ::Set));
+ IBB::ref ibb = stanzaChannel->sentStanzas[1]->getPayload<IBB>();
+ CPPUNIT_ASSERT_EQUAL(IBB::Data, ibb->getAction());
+ CPPUNIT_ASSERT_EQUAL(ByteArray("abc"), ibb->getData());
+ CPPUNIT_ASSERT_EQUAL(0, ibb->getSequenceNumber());
+ CPPUNIT_ASSERT_EQUAL(String("myid"), ibb->getStreamID());
+ }
+
+ void testResponseContinuesSending() {
+ std::auto_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz");
+ testling->setBlockSize(3);
+ testling->start();
+ stanzaChannel->onIQReceived(createIBBResult());
+ stanzaChannel->onIQReceived(createIBBResult());
+
+ CPPUNIT_ASSERT_EQUAL(3, static_cast<int>(stanzaChannel->sentStanzas.size()));
+ CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<IBB>(2, JID("foo@bar.com/baz"), IQ::Set));
+ IBB::ref ibb = stanzaChannel->sentStanzas[2]->getPayload<IBB>();
+ CPPUNIT_ASSERT_EQUAL(IBB::Data, ibb->getAction());
+ CPPUNIT_ASSERT_EQUAL(ByteArray("def"), ibb->getData());
+ CPPUNIT_ASSERT_EQUAL(1, ibb->getSequenceNumber());
+ CPPUNIT_ASSERT_EQUAL(String("myid"), ibb->getStreamID());
+ }
+
+ void testRespondToAllFinishes() {
+ std::auto_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz");
+ testling->setBlockSize(3);
+ testling->start();
+ stanzaChannel->onIQReceived(createIBBResult());
+ stanzaChannel->onIQReceived(createIBBResult());
+ stanzaChannel->onIQReceived(createIBBResult());
+ stanzaChannel->onIQReceived(createIBBResult());
+
+ CPPUNIT_ASSERT(finished);
+ CPPUNIT_ASSERT(!error);
+ }
+
+ void testErrorResponseFinishesWithError() {
+ std::auto_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz");
+ testling->setBlockSize(3);
+ testling->start();
+ stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getID()));
+
+ CPPUNIT_ASSERT(finished);
+ CPPUNIT_ASSERT(error);
+ }
+
+ void testStopDuringSessionCloses() {
+ std::auto_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz");
+ testling->setBlockSize(3);
+ testling->start();
+ testling->stop();
+
+ CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(stanzaChannel->sentStanzas.size()));
+ CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<IBB>(1, JID("foo@bar.com/baz"), IQ::Set));
+ IBB::ref ibb = stanzaChannel->sentStanzas[1]->getPayload<IBB>();
+ CPPUNIT_ASSERT_EQUAL(IBB::Close, ibb->getAction());
+ CPPUNIT_ASSERT_EQUAL(String("myid"), ibb->getStreamID());
+ CPPUNIT_ASSERT(finished);
+ CPPUNIT_ASSERT(!error);
+ }
+
+ void testStopAfterFinishedDoesNotClose() {
+ std::auto_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz");
+ testling->setBlockSize(16);
+ testling->start();
+ stanzaChannel->onIQReceived(createIBBResult());
+ stanzaChannel->onIQReceived(createIBBResult());
+ CPPUNIT_ASSERT(finished);
+
+ testling->stop();
+
+ CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(stanzaChannel->sentStanzas.size()));
+ }
+
+ private:
+ IQ::ref createIBBResult() {
+ return IQ::createResult(JID("baz@fum.com/dum"), stanzaChannel->sentStanzas[stanzaChannel->sentStanzas.size()-1]->getID(), boost::shared_ptr<IBB>());
+ }
+
+ private:
+ std::auto_ptr<IBBSendSession> createSession(const String& to) {
+ std::auto_ptr<IBBSendSession> session(new IBBSendSession("myid", JID(to), bytestream, iqRouter));
+ session->onFinished.connect(boost::bind(&IBBSendSessionTest::handleFinished, this, _1));
+ return session;
+ }
+
+ void handleFinished(boost::optional<FileTransferError> error) {
+ finished = true;
+ this->error = error;
+ }
+
+ private:
+ DummyStanzaChannel* stanzaChannel;
+ IQRouter* iqRouter;
+ bool finished;
+ boost::optional<FileTransferError> error;
+ boost::shared_ptr<ByteArrayReadBytestream> bytestream;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(IBBSendSessionTest);
diff --git a/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp b/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp
new file mode 100644
index 0000000..126f971
--- /dev/null
+++ b/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/bind.hpp>
+
+#include "Swiften/FileTransfer/SOCKS5BytestreamServerSession.h"
+#include "Swiften/FileTransfer/ByteArrayReadBytestream.h"
+#include "Swiften/FileTransfer/SOCKS5BytestreamRegistry.h"
+#include "Swiften/Network/DummyConnection.h"
+#include "Swiften/EventLoop/DummyEventLoop.h"
+#include "Swiften/Base/StartStopper.h"
+
+using namespace Swift;
+
+class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(SOCKS5BytestreamServerSessionTest);
+ CPPUNIT_TEST(testAuthenticate);
+ CPPUNIT_TEST(testAuthenticate_Chunked);
+ CPPUNIT_TEST(testRequest);
+ CPPUNIT_TEST(testRequest_UnknownBytestream);
+ CPPUNIT_TEST(testReceiveData);
+ CPPUNIT_TEST(testReceiveData_Chunked);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void setUp() {
+ receivedDataChunks = 0;
+ eventLoop = new DummyEventLoop();
+ connection = boost::shared_ptr<DummyConnection>(new DummyConnection());
+ connection->onDataSent.connect(boost::bind(&SOCKS5BytestreamServerSessionTest::handleDataWritten, this, _1));
+ stream1 = boost::shared_ptr<ByteArrayReadBytestream>(new ByteArrayReadBytestream(ByteArray("abcdefg")));
+ }
+
+ void tearDown() {
+ connection.reset();
+ delete eventLoop;
+ }
+
+ void testAuthenticate() {
+ std::auto_ptr<SOCKS5BytestreamServerSession> testling(createSession());
+ StartStopper<SOCKS5BytestreamServerSession> stopper(testling.get());
+
+ receive(ByteArray("\x05\x02\x01\x02"));
+
+ CPPUNIT_ASSERT_EQUAL(ByteArray("\x05\x00", 2), receivedData);
+ }
+
+ void testAuthenticate_Chunked() {
+ std::auto_ptr<SOCKS5BytestreamServerSession> testling(createSession());
+ StartStopper<SOCKS5BytestreamServerSession> stopper(testling.get());
+
+ receive(ByteArray("\x05\x02\x01"));
+
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(receivedData.getSize()));
+ receive(ByteArray("\x01"));
+ CPPUNIT_ASSERT_EQUAL(ByteArray("\x05\x00", 2), receivedData);
+ }
+
+ void testRequest() {
+ std::auto_ptr<SOCKS5BytestreamServerSession> testling(createSession());
+ StartStopper<SOCKS5BytestreamServerSession> stopper(testling.get());
+ bytestreams.addBytestream("abcdef", stream1);
+ authenticate();
+
+ ByteArray hostname("abcdef");
+ receive(ByteArray("\x05\x01\x00\x03", 4) + hostname.getSize() + hostname + ByteArray("\x00\x00", 2));
+ CPPUNIT_ASSERT_EQUAL(ByteArray("\x05\x00\x00\x03\x06\x61\x62\x63\x64\x65\x66\x00\x00", 13), ByteArray(receivedData.getData(), 13));
+ }
+
+ void testRequest_UnknownBytestream() {
+ std::auto_ptr<SOCKS5BytestreamServerSession> testling(createSession());
+ StartStopper<SOCKS5BytestreamServerSession> stopper(testling.get());
+ authenticate();
+
+ ByteArray hostname("abcdef");
+ receive(ByteArray("\x05\x01\x00\x03", 4) + hostname.getSize() + hostname + ByteArray("\x00\x00", 2));
+ CPPUNIT_ASSERT_EQUAL(ByteArray("\x05\x04\x00\x03\x06\x61\x62\x63\x64\x65\x66\x00\x00", 13), receivedData);
+ }
+
+ void testReceiveData() {
+ std::auto_ptr<SOCKS5BytestreamServerSession> testling(createSession());
+ StartStopper<SOCKS5BytestreamServerSession> stopper(testling.get());
+ bytestreams.addBytestream("abcdef", stream1);
+ authenticate();
+ request("abcdef");
+ eventLoop->processEvents();
+ skipHeader("abcdef");
+
+ CPPUNIT_ASSERT_EQUAL(ByteArray("abcdefg"), receivedData);
+ CPPUNIT_ASSERT_EQUAL(2, receivedDataChunks);
+ }
+
+ void testReceiveData_Chunked() {
+ std::auto_ptr<SOCKS5BytestreamServerSession> testling(createSession());
+ testling->setChunkSize(3);
+ StartStopper<SOCKS5BytestreamServerSession> stopper(testling.get());
+ bytestreams.addBytestream("abcdef", stream1);
+ authenticate();
+ request("abcdef");
+ eventLoop->processEvents();
+
+ skipHeader("abcdef");
+ CPPUNIT_ASSERT_EQUAL(ByteArray("abcdefg"), receivedData);
+ CPPUNIT_ASSERT_EQUAL(4, receivedDataChunks);
+ }
+
+ private:
+ void receive(const ByteArray& data) {
+ connection->receive(data);
+ eventLoop->processEvents();
+ }
+
+ void authenticate() {
+ receive(ByteArray("\x05\x02\x01\x02"));
+ receivedData.clear();
+ receivedDataChunks = 0;
+ }
+
+ void request(const String& hostname) {
+ receive(ByteArray("\x05\x01\x00\x03", 4) + hostname.getUTF8Size() + hostname + ByteArray("\x00\x00", 2));
+ }
+
+ void skipHeader(const String& hostname) {
+ int headerSize = 7 + hostname.getUTF8Size();
+ receivedData = ByteArray(receivedData.getData() + headerSize, receivedData.getSize() - headerSize);
+ }
+
+
+ void handleDataWritten(const ByteArray& data) {
+ receivedData += data;
+ receivedDataChunks++;
+ }
+
+ private:
+ SOCKS5BytestreamServerSession* createSession() {
+ SOCKS5BytestreamServerSession* session = new SOCKS5BytestreamServerSession(connection, &bytestreams);
+ return session;
+ }
+
+ private:
+ DummyEventLoop* eventLoop;
+ SOCKS5BytestreamRegistry bytestreams;
+ boost::shared_ptr<DummyConnection> connection;
+ ByteArray receivedData;
+ int receivedDataChunks;
+ boost::shared_ptr<ByteArrayReadBytestream> stream1;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(SOCKS5BytestreamServerSessionTest);
diff --git a/Swiften/Network/BoostConnection.cpp b/Swiften/Network/BoostConnection.cpp
index ccaae8a..8cd19f2 100644
--- a/Swiften/Network/BoostConnection.cpp
+++ b/Swiften/Network/BoostConnection.cpp
@@ -102,7 +102,7 @@ void BoostConnection::handleSocketRead(const boost::system::error_code& error, s
void BoostConnection::handleDataWritten(const boost::system::error_code& error) {
if (!error) {
- return;
+ MainEventLoop::postEvent(boost::ref(onDataWritten), shared_from_this());
}
if (error == boost::asio::error::eof) {
MainEventLoop::postEvent(boost::bind(boost::ref(onDisconnected), boost::optional<Error>()), shared_from_this());
@@ -112,4 +112,9 @@ void BoostConnection::handleDataWritten(const boost::system::error_code& error)
}
}
+HostAddressPort BoostConnection::getLocalAddress() const {
+ return HostAddressPort(socket_.local_endpoint());
+}
+
+
}
diff --git a/Swiften/Network/BoostConnection.h b/Swiften/Network/BoostConnection.h
index 8d3d444..4f5352f 100644
--- a/Swiften/Network/BoostConnection.h
+++ b/Swiften/Network/BoostConnection.h
@@ -39,6 +39,8 @@ namespace Swift {
return socket_;
}
+ HostAddressPort getLocalAddress() const;
+
private:
BoostConnection(boost::asio::io_service* ioService);
diff --git a/Swiften/Network/BoostConnectionServer.cpp b/Swiften/Network/BoostConnectionServer.cpp
index febe6c9..03ae19c 100644
--- a/Swiften/Network/BoostConnectionServer.cpp
+++ b/Swiften/Network/BoostConnectionServer.cpp
@@ -71,4 +71,13 @@ void BoostConnectionServer::handleAccept(boost::shared_ptr<BoostConnection> newC
}
}
+HostAddressPort BoostConnectionServer::getAddressPort() const {
+ if (acceptor_) {
+ return HostAddressPort(acceptor_->local_endpoint());
+ }
+ else {
+ return HostAddressPort();
+ }
+}
+
}
diff --git a/Swiften/Network/BoostConnectionServer.h b/Swiften/Network/BoostConnectionServer.h
index 3a3c096..abcb3af 100644
--- a/Swiften/Network/BoostConnectionServer.h
+++ b/Swiften/Network/BoostConnectionServer.h
@@ -28,10 +28,12 @@ namespace Swift {
static ref create(int port, boost::asio::io_service* ioService) {
return ref(new BoostConnectionServer(port, ioService));
}
-
+
void start();
void stop();
+ virtual HostAddressPort getAddressPort() const;
+
boost::signal<void (boost::optional<Error>)> onStopped;
private:
diff --git a/Swiften/Network/Connection.h b/Swiften/Network/Connection.h
index 34f3ade..8c30ad7 100644
--- a/Swiften/Network/Connection.h
+++ b/Swiften/Network/Connection.h
@@ -10,10 +10,9 @@
#include "Swiften/Base/ByteArray.h"
#include "Swiften/Base/String.h"
+#include "Swiften/Network/HostAddressPort.h"
namespace Swift {
- class HostAddressPort;
-
class Connection {
public:
enum Error {
@@ -29,9 +28,12 @@ namespace Swift {
virtual void disconnect() = 0;
virtual void write(const ByteArray& data) = 0;
+ virtual HostAddressPort getLocalAddress() const = 0;
+
public:
boost::signal<void (bool /* error */)> onConnectFinished;
boost::signal<void (const boost::optional<Error>&)> onDisconnected;
boost::signal<void (const ByteArray&)> onDataRead;
+ boost::signal<void ()> onDataWritten;
};
}
diff --git a/Swiften/Network/ConnectionServer.h b/Swiften/Network/ConnectionServer.h
index 5597d1b..8129372 100644
--- a/Swiften/Network/ConnectionServer.h
+++ b/Swiften/Network/ConnectionServer.h
@@ -10,12 +10,15 @@
#include "Swiften/Base/boost_bsignals.h"
#include "Swiften/Network/Connection.h"
+#include "Swiften/Network/HostAddressPort.h"
namespace Swift {
class ConnectionServer {
public:
virtual ~ConnectionServer();
+ virtual HostAddressPort getAddressPort() const = 0;
+
boost::signal<void (boost::shared_ptr<Connection>)> onNewConnection;
};
}
diff --git a/Swiften/Network/DummyConnection.h b/Swiften/Network/DummyConnection.h
index d1657d5..576965f 100644
--- a/Swiften/Network/DummyConnection.h
+++ b/Swiften/Network/DummyConnection.h
@@ -15,32 +15,35 @@
#include "Swiften/EventLoop/EventOwner.h"
namespace Swift {
- class DummyConnection :
- public Connection,
- public EventOwner,
- public boost::enable_shared_from_this<DummyConnection> {
+ class DummyConnection : public Connection, public EventOwner, public boost::enable_shared_from_this<DummyConnection> {
+ public:
+ void listen() {
+ assert(false);
+ }
- void listen() {
- assert(false);
- }
+ void connect(const HostAddressPort&) {
+ assert(false);
+ }
- void connect(const HostAddressPort&) {
- assert(false);
- }
+ void disconnect() {
+ //assert(false);
+ }
- void disconnect() {
- assert(false);
- }
+ void write(const ByteArray& data) {
+ MainEventLoop::postEvent(boost::ref(onDataWritten), shared_from_this());
+ onDataSent(data);
+ }
- void write(const ByteArray& data) {
- onDataWritten(data);
- }
+ void receive(const ByteArray& data) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onDataRead), ByteArray(data)), shared_from_this());
+ }
- void receive(const ByteArray& data) {
- MainEventLoop::postEvent(boost::bind(
- boost::ref(onDataRead), ByteArray(data)), shared_from_this());
- }
+ HostAddressPort getLocalAddress() const {
+ return localAddress;
+ }
- boost::signal<void (const ByteArray&)> onDataWritten;
+ boost::signal<void (const ByteArray&)> onDataSent;
+
+ HostAddressPort localAddress;
};
}
diff --git a/Swiften/Network/FakeConnection.h b/Swiften/Network/FakeConnection.h
index 60b8c94..a89466f 100644
--- a/Swiften/Network/FakeConnection.h
+++ b/Swiften/Network/FakeConnection.h
@@ -36,6 +36,10 @@ namespace Swift {
assert(false);
}
+ virtual HostAddressPort getLocalAddress() const {
+ return HostAddressPort();
+ }
+
void setError(const Error& e) {
error = boost::optional<Error>(e);
state = DisconnectedWithError;
diff --git a/Swiften/Network/HostAddress.cpp b/Swiften/Network/HostAddress.cpp
index e299429..b3876c0 100644
--- a/Swiften/Network/HostAddress.cpp
+++ b/Swiften/Network/HostAddress.cpp
@@ -9,8 +9,8 @@
#include <boost/numeric/conversion/cast.hpp>
#include <boost/lexical_cast.hpp>
#include <cassert>
-#include <sstream>
-#include <iomanip>
+#include <stdexcept>
+#include <boost/array.hpp>
#include "Swiften/Base/foreach.h"
#include "Swiften/Base/String.h"
@@ -18,50 +18,43 @@
namespace Swift {
HostAddress::HostAddress() {
- for (int i = 0; i < 4; ++i) {
- address_.push_back(0);
- }
}
HostAddress::HostAddress(const String& address) {
- std::vector<String> components = address.split('.');
- assert(components.size() == 4);
- foreach(const String& component, components) {
- address_.push_back(boost::lexical_cast<int>(component.getUTF8String()));
+ try {
+ address_ = boost::asio::ip::address::from_string(address.getUTF8String());
+ }
+ catch (const std::exception& t) {
}
}
HostAddress::HostAddress(const unsigned char* address, int length) {
assert(length == 4 || length == 16);
- address_.reserve(length);
- for (int i = 0; i < length; ++i) {
- address_.push_back(address[i]);
- }
-}
-
-std::string HostAddress::toString() const {
- if (address_.size() == 4) {
- std::ostringstream result;
- for (size_t i = 0; i < address_.size() - 1; ++i) {
- result << boost::numeric_cast<unsigned int>(address_[i]) << ".";
- }
- result << boost::numeric_cast<unsigned int>(address_[address_.size() - 1]);
- return result.str();
- }
- else if (address_.size() == 16) {
- std::ostringstream result;
- result << std::hex;
- result.fill('0');
- for (size_t i = 0; i < (address_.size() / 2) - 1; ++i) {
- result << std::setw(2) << boost::numeric_cast<unsigned int>(address_[2*i]) << std::setw(2) << boost::numeric_cast<unsigned int>(address_[(2*i)+1]) << ":";
+ if (length == 4) {
+ boost::array<unsigned char, 4> data;
+ for (int i = 0; i < length; ++i) {
+ data[i] = address[i];
}
- result << std::setw(2) << boost::numeric_cast<unsigned int>(address_[address_.size() - 2]) << std::setw(2) << boost::numeric_cast<unsigned int>(address_[address_.size() - 1]);
- return result.str();
+ address_ = boost::asio::ip::address(boost::asio::ip::address_v4(data));
}
else {
- assert(false);
- return "";
+ boost::array<unsigned char, 16> data;
+ for (int i = 0; i < length; ++i) {
+ data[i] = address[i];
+ }
+ address_ = boost::asio::ip::address(boost::asio::ip::address_v6(data));
}
}
+HostAddress::HostAddress(const boost::asio::ip::address& address) : address_(address) {
+}
+
+std::string HostAddress::toString() const {
+ return address_.to_string();
+}
+
+bool HostAddress::isValid() const {
+ return !(address_.is_v4() && address_.to_v4().to_ulong() == 0);
+}
+
}
diff --git a/Swiften/Network/HostAddress.h b/Swiften/Network/HostAddress.h
index cf2db07..6e2bde0 100644
--- a/Swiften/Network/HostAddress.h
+++ b/Swiften/Network/HostAddress.h
@@ -8,6 +8,7 @@
#include <string>
#include <vector>
+#include <boost/asio.hpp>
namespace Swift {
class String;
@@ -17,10 +18,7 @@ namespace Swift {
HostAddress();
HostAddress(const String&);
HostAddress(const unsigned char* address, int length);
-
- const std::vector<unsigned char>& getRawAddress() const {
- return address_;
- }
+ HostAddress(const boost::asio::ip::address& address);
std::string toString() const;
@@ -28,7 +26,9 @@ namespace Swift {
return address_ == o.address_;
}
+ bool isValid() const;
+
private:
- std::vector<unsigned char> address_;
+ boost::asio::ip::address address_;
};
}
diff --git a/Swiften/Network/HostAddressPort.h b/Swiften/Network/HostAddressPort.h
index cf24a26..6883380 100644
--- a/Swiften/Network/HostAddressPort.h
+++ b/Swiften/Network/HostAddressPort.h
@@ -4,17 +4,24 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#ifndef SWIFTEN_HostAddressPort_H
-#define SWIFTEN_HostAddressPort_H
+#pragma once
+
+#include <boost/asio.hpp>
#include "Swiften/Network/HostAddress.h"
namespace Swift {
class HostAddressPort {
public:
- HostAddressPort(const HostAddress& address, int port) : address_(address), port_(port) {
+ HostAddressPort(const HostAddress& address = HostAddress(), int port = -1) : address_(address), port_(port) {
+ }
+
+ HostAddressPort(const boost::asio::ip::tcp::endpoint& endpoint) {
+ address_ = HostAddress(endpoint.address());
+ port_ = endpoint.port();
}
+
const HostAddress& getAddress() const {
return address_;
}
@@ -27,10 +34,12 @@ namespace Swift {
return address_ == o.address_ && port_ == o.port_;
}
+ bool isValid() const {
+ return address_.isValid() && port_ > 0;
+ }
+
private:
HostAddress address_;
int port_;
};
}
-
-#endif
diff --git a/Swiften/Network/UnitTest/ConnectorTest.cpp b/Swiften/Network/UnitTest/ConnectorTest.cpp
index 2e396b3..07e520c 100644
--- a/Swiften/Network/UnitTest/ConnectorTest.cpp
+++ b/Swiften/Network/UnitTest/ConnectorTest.cpp
@@ -268,6 +268,7 @@ class ConnectorTest : public CppUnit::TestFixture {
}
}
+ HostAddressPort getLocalAddress() const { return HostAddressPort(); }
void disconnect() { assert(false); }
void write(const ByteArray&) { assert(false); }
diff --git a/Swiften/Network/UnitTest/HostAddressTest.cpp b/Swiften/Network/UnitTest/HostAddressTest.cpp
index 1cc31ad..45793fa 100644
--- a/Swiften/Network/UnitTest/HostAddressTest.cpp
+++ b/Swiften/Network/UnitTest/HostAddressTest.cpp
@@ -15,8 +15,11 @@ using namespace Swift;
class HostAddressTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(HostAddressTest);
CPPUNIT_TEST(testConstructor);
+ CPPUNIT_TEST(testConstructor_Invalid);
+ CPPUNIT_TEST(testConstructor_InvalidString);
CPPUNIT_TEST(testToString);
CPPUNIT_TEST(testToString_IPv6);
+ CPPUNIT_TEST(testToString_Invalid);
CPPUNIT_TEST_SUITE_END();
public:
@@ -24,6 +27,19 @@ class HostAddressTest : public CppUnit::TestFixture {
HostAddress testling("192.168.1.254");
CPPUNIT_ASSERT_EQUAL(std::string("192.168.1.254"), testling.toString());
+ CPPUNIT_ASSERT(testling.isValid());
+ }
+
+ void testConstructor_Invalid() {
+ HostAddress testling;
+
+ CPPUNIT_ASSERT(!testling.isValid());
+ }
+
+ void testConstructor_InvalidString() {
+ HostAddress testling("invalid");
+
+ CPPUNIT_ASSERT(!testling.isValid());
}
void testToString() {
@@ -37,7 +53,13 @@ class HostAddressTest : public CppUnit::TestFixture {
unsigned char address[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17};
HostAddress testling(address, 16);
- CPPUNIT_ASSERT_EQUAL(std::string("0102:0304:0506:0708:090a:0b0c:0d0e:0f11"), testling.toString());
+ CPPUNIT_ASSERT_EQUAL(std::string("102:304:506:708:90a:b0c:d0e:f11"), testling.toString());
+ }
+
+ void testToString_Invalid() {
+ HostAddress testling;
+
+ CPPUNIT_ASSERT_EQUAL(std::string("0.0.0.0"), testling.toString());
}
};
diff --git a/Swiften/Parser/GenericPayloadParserFactory.h b/Swiften/Parser/GenericPayloadParserFactory.h
index e7e070d..05b996a 100644
--- a/Swiften/Parser/GenericPayloadParserFactory.h
+++ b/Swiften/Parser/GenericPayloadParserFactory.h
@@ -4,8 +4,7 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#ifndef SWIFTEN_GENERICPAYLOADPARSERFACTORY_H
-#define SWIFTEN_GENERICPAYLOADPARSERFACTORY_H
+#pragma once
#include "Swiften/Parser/PayloadParserFactory.h"
#include "Swiften/Base/String.h"
@@ -18,7 +17,7 @@ namespace Swift {
GenericPayloadParserFactory(const String& tag, const String& xmlns = "") : tag_(tag), xmlns_(xmlns) {}
virtual bool canParse(const String& element, const String& ns, const AttributeMap&) const {
- return element == tag_ && (xmlns_.isEmpty() ? true : xmlns_ == ns);
+ return (tag_.isEmpty() ? true : element == tag_) && (xmlns_.isEmpty() ? true : xmlns_ == ns);
}
virtual PayloadParser* createPayloadParser() {
@@ -30,5 +29,3 @@ namespace Swift {
String xmlns_;
};
}
-
-#endif
diff --git a/Swiften/Parser/PayloadParsers/BytestreamsParser.cpp b/Swiften/Parser/PayloadParsers/BytestreamsParser.cpp
new file mode 100644
index 0000000..154a925
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/BytestreamsParser.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Swiften/Parser/PayloadParsers/BytestreamsParser.h"
+
+#include <boost/lexical_cast.hpp>
+
+#include "Swiften/Base/foreach.h"
+
+namespace Swift {
+
+BytestreamsParser::BytestreamsParser() : level(TopLevel) {
+}
+
+BytestreamsParser::~BytestreamsParser() {
+}
+
+void BytestreamsParser::handleStartElement(const String& element, const String&, const AttributeMap& attributes) {
+ if (level == TopLevel) {
+ getPayloadInternal()->setStreamID(attributes.getAttribute("sid"));
+ }
+ else if (level == PayloadLevel) {
+ if (element == "streamhost") {
+ try {
+ getPayloadInternal()->addStreamHost(Bytestreams::StreamHost(attributes.getAttribute("host"), JID(attributes.getAttribute("jid")), boost::lexical_cast<int>(attributes.getAttribute("port"))));
+ }
+ catch (boost::bad_lexical_cast& e) {
+ }
+ }
+ else if (element == "streamhost-used") {
+ getPayloadInternal()->setUsedStreamHost(JID(attributes.getAttribute("jid")));
+ }
+ }
+ ++level;
+}
+
+void BytestreamsParser::handleEndElement(const String&, const String&) {
+ --level;
+}
+
+void BytestreamsParser::handleCharacterData(const String&) {
+}
+
+
+}
diff --git a/Swiften/Parser/PayloadParsers/BytestreamsParser.h b/Swiften/Parser/PayloadParsers/BytestreamsParser.h
new file mode 100644
index 0000000..a45baa4
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/BytestreamsParser.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/optional.hpp>
+
+#include "Swiften/Elements/Bytestreams.h"
+#include "Swiften/Parser/GenericPayloadParser.h"
+
+namespace Swift {
+ class BytestreamsParser : public GenericPayloadParser<Bytestreams> {
+ public:
+ BytestreamsParser();
+ ~BytestreamsParser();
+
+ virtual void handleStartElement(const String& element, const String&, const AttributeMap& attributes);
+ virtual void handleEndElement(const String& element, const String&);
+ virtual void handleCharacterData(const String& data);
+
+ private:
+ enum Level {
+ TopLevel = 0,
+ PayloadLevel = 1,
+ };
+ int level;
+ };
+}
diff --git a/Swiften/Parser/PayloadParsers/BytestreamsParserFactory.h b/Swiften/Parser/PayloadParsers/BytestreamsParserFactory.h
new file mode 100644
index 0000000..8defd45
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/BytestreamsParserFactory.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include "Swiften/Parser/GenericPayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/BytestreamsParser.h"
+
+namespace Swift {
+ class BytestreamsParserFactory : public GenericPayloadParserFactory<BytestreamsParser> {
+ public:
+ BytestreamsParserFactory() : GenericPayloadParserFactory<BytestreamsParser>("query", "http://jabber.org/protocol/bytestreams") {}
+ };
+}
diff --git a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
index de0e71c..90e9038 100644
--- a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
+++ b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
@@ -27,6 +27,9 @@
#include "Swiften/Parser/PayloadParsers/SecurityLabelsCatalogParserFactory.h"
#include "Swiften/Parser/PayloadParsers/FormParserFactory.h"
#include "Swiften/Parser/PayloadParsers/CommandParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/StreamInitiationParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/BytestreamsParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/IBBParserFactory.h"
#include "Swiften/Parser/PayloadParsers/VCardUpdateParserFactory.h"
#include "Swiften/Parser/PayloadParsers/VCardParserFactory.h"
#include "Swiften/Parser/PayloadParsers/RawXMLPayloadParserFactory.h"
@@ -40,6 +43,7 @@ using namespace boost;
namespace Swift {
FullPayloadParserFactoryCollection::FullPayloadParserFactoryCollection() {
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new IBBParserFactory()));
factories_.push_back(shared_ptr<PayloadParserFactory>(new StatusParserFactory()));
factories_.push_back(shared_ptr<PayloadParserFactory>(new StatusShowParserFactory()));
factories_.push_back(shared_ptr<PayloadParserFactory>(new BodyParserFactory()));
@@ -58,6 +62,8 @@ FullPayloadParserFactoryCollection::FullPayloadParserFactoryCollection() {
factories_.push_back(shared_ptr<PayloadParserFactory>(new SecurityLabelsCatalogParserFactory()));
factories_.push_back(shared_ptr<PayloadParserFactory>(new FormParserFactory()));
factories_.push_back(shared_ptr<PayloadParserFactory>(new CommandParserFactory()));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new StreamInitiationParserFactory()));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new BytestreamsParserFactory()));
factories_.push_back(shared_ptr<PayloadParserFactory>(new VCardUpdateParserFactory()));
factories_.push_back(shared_ptr<PayloadParserFactory>(new VCardParserFactory()));
factories_.push_back(shared_ptr<PayloadParserFactory>(new PrivateStorageParserFactory(this)));
diff --git a/Swiften/Parser/PayloadParsers/IBBParser.cpp b/Swiften/Parser/PayloadParsers/IBBParser.cpp
new file mode 100644
index 0000000..b2b4929
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/IBBParser.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Swiften/Parser/PayloadParsers/IBBParser.h"
+
+#include <boost/lexical_cast.hpp>
+
+#include "Swiften/Base/foreach.h"
+#include "Swiften/StringCodecs/Base64.h"
+
+namespace Swift {
+
+IBBParser::IBBParser() : level(TopLevel) {
+}
+
+IBBParser::~IBBParser() {
+}
+
+void IBBParser::handleStartElement(const String& element, const String&, const AttributeMap& attributes) {
+ if (level == TopLevel) {
+ if (element == "data") {
+ getPayloadInternal()->setAction(IBB::Data);
+ getPayloadInternal()->setStreamID(attributes.getAttribute("sid"));
+ try {
+ getPayloadInternal()->setSequenceNumber(boost::lexical_cast<int>(attributes.getAttribute("seq")));
+ }
+ catch (boost::bad_lexical_cast& e) {
+ }
+ }
+ else if (element == "open") {
+ getPayloadInternal()->setAction(IBB::Open);
+ getPayloadInternal()->setStreamID(attributes.getAttribute("sid"));
+ if (attributes.getAttribute("stanza") == "message") {
+ getPayloadInternal()->setStanzaType(IBB::MessageStanza);
+ }
+ else {
+ getPayloadInternal()->setStanzaType(IBB::IQStanza);
+ }
+ try {
+ getPayloadInternal()->setBlockSize(boost::lexical_cast<int>(attributes.getAttribute("block-size")));
+ }
+ catch (boost::bad_lexical_cast& e) {
+ }
+ }
+ else if (element == "close") {
+ getPayloadInternal()->setAction(IBB::Close);
+ getPayloadInternal()->setStreamID(attributes.getAttribute("sid"));
+ }
+ }
+ ++level;
+}
+
+void IBBParser::handleEndElement(const String& element, const String&) {
+ --level;
+ if (level == TopLevel) {
+ if (element == "data") {
+ std::vector<char> data;
+ for (size_t i = 0; i < currentText.getUTF8Size(); ++i) {
+ char c = currentText[i];
+ if (c >= 48 && c <= 122) {
+ data.push_back(c);
+ }
+ }
+ getPayloadInternal()->setData(Base64::decode(String(&data[0], data.size())));
+ }
+ }
+}
+
+void IBBParser::handleCharacterData(const String& data) {
+ currentText += data;
+}
+
+
+}
diff --git a/Swiften/Parser/PayloadParsers/IBBParser.h b/Swiften/Parser/PayloadParsers/IBBParser.h
new file mode 100644
index 0000000..1fc062f
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/IBBParser.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/optional.hpp>
+
+#include "Swiften/Elements/IBB.h"
+#include "Swiften/Parser/GenericPayloadParser.h"
+
+namespace Swift {
+ class IBBParser : public GenericPayloadParser<IBB> {
+ public:
+ IBBParser();
+ ~IBBParser();
+
+ virtual void handleStartElement(const String& element, const String&, const AttributeMap& attributes);
+ virtual void handleEndElement(const String& element, const String&);
+ virtual void handleCharacterData(const String& data);
+
+ private:
+ enum Level {
+ TopLevel = 0,
+ };
+ int level;
+ String currentText;
+ };
+}
diff --git a/Swiften/Parser/PayloadParsers/IBBParserFactory.h b/Swiften/Parser/PayloadParsers/IBBParserFactory.h
new file mode 100644
index 0000000..aa323f7
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/IBBParserFactory.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include "Swiften/Parser/GenericPayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/IBBParser.h"
+
+namespace Swift {
+ class IBBParserFactory : public GenericPayloadParserFactory<IBBParser> {
+ public:
+ IBBParserFactory() : GenericPayloadParserFactory<IBBParser>("", "http://jabber.org/protocol/ibb") {}
+ };
+}
diff --git a/Swiften/Parser/PayloadParsers/StreamInitiationParser.cpp b/Swiften/Parser/PayloadParsers/StreamInitiationParser.cpp
new file mode 100644
index 0000000..76925af
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/StreamInitiationParser.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Swiften/Parser/PayloadParsers/StreamInitiationParser.h"
+
+#include <boost/lexical_cast.hpp>
+
+#include "Swiften/Parser/PayloadParsers/FormParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/FormParser.h"
+#include "Swiften/Base/foreach.h"
+
+
+#define FILE_TRANSFER_NS "http://jabber.org/protocol/si/profile/file-transfer"
+#define FEATURE_NEG_NS "http://jabber.org/protocol/feature-neg"
+
+namespace Swift {
+
+StreamInitiationParser::StreamInitiationParser() : level(TopLevel), formParser(0), inFile(false), inFeature(false) {
+ formParserFactory = new FormParserFactory();
+}
+
+StreamInitiationParser::~StreamInitiationParser() {
+ delete formParserFactory;
+}
+
+void StreamInitiationParser::handleStartElement(const String& element, const String& ns, const AttributeMap& attributes) {
+ if (level == TopLevel) {
+ getPayloadInternal()->setID(attributes.getAttribute("id"));
+ if (!attributes.getAttribute("profile").isEmpty()) {
+ getPayloadInternal()->setIsFileTransfer(attributes.getAttribute("profile") == FILE_TRANSFER_NS);
+ }
+ }
+ else if (level == PayloadLevel) {
+ if (element == "file") {
+ inFile = true;
+ currentFile = StreamInitiation::FileInfo();
+ currentFile.name = attributes.getAttribute("name");
+ try {
+ currentFile.size = boost::lexical_cast<int>(attributes.getAttribute("size"));
+ }
+ catch (boost::bad_lexical_cast& e) {
+ }
+ }
+ else if (element == "feature" && ns == FEATURE_NEG_NS) {
+ inFeature = true;
+ }
+ }
+ else if (level == FileOrFeatureLevel) {
+ if (inFile && element == "desc") {
+ currentText.clear();
+ }
+ else if (inFeature && formParserFactory->canParse(element, ns, attributes)) {
+ formParser = dynamic_cast<FormParser*>(formParserFactory->createPayloadParser());
+ }
+ }
+
+ if (formParser) {
+ formParser->handleStartElement(element, ns, attributes);
+ }
+ ++level;
+}
+
+void StreamInitiationParser::handleEndElement(const String& element, const String& ns) {
+ --level;
+ if (formParser) {
+ formParser->handleEndElement(element, ns);
+ }
+ if (level == TopLevel) {
+ }
+ else if (level == PayloadLevel) {
+ if (element == "file") {
+ getPayloadInternal()->setFileInfo(currentFile);
+ inFile = false;
+ }
+ else if (element == "feature" && ns == FEATURE_NEG_NS) {
+ inFeature = false;
+ }
+ }
+ else if (level == FileOrFeatureLevel) {
+ if (inFile && element == "desc") {
+ currentFile.description = currentText;
+ }
+ else if (formParser) {
+ Form::ref form = formParser->getPayloadInternal();
+ if (form) {
+ FormField::ref field = boost::dynamic_pointer_cast<FormField>(form->getField("stream-method"));
+ if (field) {
+ if (form->getType() == Form::FormType) {
+ foreach (const FormField::Option& option, field->getOptions()) {
+ getPayloadInternal()->addProvidedMethod(option.value);
+ }
+ }
+ else if (form->getType() == Form::SubmitType) {
+ if (field->getRawValues().size() > 0) {
+ getPayloadInternal()->setRequestedMethod(field->getRawValues()[0]);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void StreamInitiationParser::handleCharacterData(const String& data) {
+ if (formParser) {
+ formParser->handleCharacterData(data);
+ }
+ else {
+ currentText += data;
+ }
+}
+
+
+}
diff --git a/Swiften/Parser/PayloadParsers/StreamInitiationParser.h b/Swiften/Parser/PayloadParsers/StreamInitiationParser.h
new file mode 100644
index 0000000..f01567e
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/StreamInitiationParser.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/optional.hpp>
+
+#include "Swiften/Elements/StreamInitiation.h"
+#include "Swiften/Parser/GenericPayloadParser.h"
+
+namespace Swift {
+ class FormParserFactory;
+ class FormParser;
+
+ class StreamInitiationParser : public GenericPayloadParser<StreamInitiation> {
+ public:
+ StreamInitiationParser();
+ ~StreamInitiationParser();
+
+ virtual void handleStartElement(const String& element, const String&, const AttributeMap& attributes);
+ virtual void handleEndElement(const String& element, const String&);
+ virtual void handleCharacterData(const String& data);
+
+ private:
+ enum Level {
+ TopLevel = 0,
+ PayloadLevel = 1,
+ FileOrFeatureLevel = 2,
+ FormOrDescriptionLevel = 3,
+ };
+ int level;
+ FormParserFactory* formParserFactory;
+ FormParser* formParser;
+ bool inFile;
+ bool inFeature;
+ StreamInitiation::FileInfo currentFile;
+ String currentText;
+ };
+}
diff --git a/Swiften/Parser/PayloadParsers/StreamInitiationParserFactory.h b/Swiften/Parser/PayloadParsers/StreamInitiationParserFactory.h
new file mode 100644
index 0000000..ee5ed09
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/StreamInitiationParserFactory.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include "Swiften/Parser/GenericPayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/StreamInitiationParser.h"
+
+namespace Swift {
+ class StreamInitiationParserFactory : public GenericPayloadParserFactory<StreamInitiationParser> {
+ public:
+ StreamInitiationParserFactory() : GenericPayloadParserFactory<StreamInitiationParser>("si", "http://jabber.org/protocol/si") {}
+ };
+}
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/IBBParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/IBBParserTest.cpp
new file mode 100644
index 0000000..f892deb
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/IBBParserTest.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h"
+#include "Swiften/Elements/IBB.h"
+
+using namespace Swift;
+
+class IBBParserTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(IBBParserTest);
+ CPPUNIT_TEST(testParse_Data);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void testParse_Data() {
+ PayloadsParserTester parser;
+
+ CPPUNIT_ASSERT(parser.parse(
+ "<data xmlns='http://jabber.org/protocol/ibb' seq='4'>\n"
+ "\t YWJjZGVmZ2loamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjEyMzQ1\n"
+ "\t Njc4OTAK\n"
+ "</data>"
+ ));
+
+ IBB::ref ibb = parser.getPayload<IBB>();
+ CPPUNIT_ASSERT(ibb->getAction() == IBB::Data);
+ CPPUNIT_ASSERT_EQUAL(ByteArray("abcdefgihjklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890\x0a"), ibb->getData());
+ CPPUNIT_ASSERT_EQUAL(4, ibb->getSequenceNumber());
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(IBBParserTest);
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/StreamInitiationParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/StreamInitiationParserTest.cpp
new file mode 100644
index 0000000..ca8e353
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/StreamInitiationParserTest.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h"
+#include "Swiften/Elements/StreamInitiation.h"
+
+using namespace Swift;
+
+class StreamInitiationParserTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(StreamInitiationParserTest);
+ CPPUNIT_TEST(testParse_Request);
+ CPPUNIT_TEST(testParse_Response);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void testParse_Request() {
+ PayloadsParserTester parser;
+
+ CPPUNIT_ASSERT(parser.parse(
+ "<si xmlns='http://jabber.org/protocol/si' id='a0' mime-type='text/plain' profile='http://jabber.org/protocol/si/profile/file-transfer'>"
+ "<file xmlns='http://jabber.org/protocol/si/profile/file-transfer' name='test.txt' size='1022'>"
+ "<desc>This is info about the file.</desc>"
+ "</file>"
+ "<feature xmlns='http://jabber.org/protocol/feature-neg'>"
+ "<x xmlns='jabber:x:data' type='form'>"
+ "<field var='stream-method' type='list-single'>"
+ "<option><value>http://jabber.org/protocol/bytestreams</value></option>"
+ "<option><value>jabber:iq:oob</value></option>"
+ "<option><value>http://jabber.org/protocol/ibb</value></option>"
+ "</field>"
+ "</x>"
+ "</feature>"
+ "</si>"
+ ));
+
+ StreamInitiation::ref si = parser.getPayload<StreamInitiation>();
+ CPPUNIT_ASSERT(si->getIsFileTransfer());
+ CPPUNIT_ASSERT(si->getFileInfo());
+ CPPUNIT_ASSERT_EQUAL(String("test.txt"), si->getFileInfo()->name);
+ CPPUNIT_ASSERT_EQUAL(1022, si->getFileInfo()->size);
+ CPPUNIT_ASSERT_EQUAL(String("This is info about the file."), si->getFileInfo()->description);
+ CPPUNIT_ASSERT_EQUAL(3, static_cast<int>(si->getProvidedMethods().size()));
+ CPPUNIT_ASSERT_EQUAL(String("http://jabber.org/protocol/bytestreams"), si->getProvidedMethods()[0]);
+ CPPUNIT_ASSERT_EQUAL(String("jabber:iq:oob"), si->getProvidedMethods()[1]);
+ CPPUNIT_ASSERT_EQUAL(String("http://jabber.org/protocol/ibb"), si->getProvidedMethods()[2]);
+ }
+
+ void testParse_Response() {
+ PayloadsParserTester parser;
+
+ CPPUNIT_ASSERT(parser.parse(
+ "<si xmlns='http://jabber.org/protocol/si'>"
+ "<feature xmlns='http://jabber.org/protocol/feature-neg'>"
+ "<x xmlns='jabber:x:data' type='submit'>"
+ "<field var='stream-method'>"
+ "<value>http://jabber.org/protocol/bytestreams</value>"
+ "</field>"
+ "</x>"
+ "</feature>"
+ "</si>"
+ ));
+
+ StreamInitiation::ref si = parser.getPayload<StreamInitiation>();
+ CPPUNIT_ASSERT(si->getIsFileTransfer());
+ CPPUNIT_ASSERT_EQUAL(String("http://jabber.org/protocol/bytestreams"), si->getRequestedMethod());
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(StreamInitiationParserTest);
diff --git a/Swiften/Parser/SConscript b/Swiften/Parser/SConscript
index 0256cbf..1eeb266 100644
--- a/Swiften/Parser/SConscript
+++ b/Swiften/Parser/SConscript
@@ -27,6 +27,7 @@ sources = [
"PayloadParsers/DiscoItemsParser.cpp",
"PayloadParsers/ErrorParser.cpp",
"PayloadParsers/FormParser.cpp",
+ "PayloadParsers/IBBParser.cpp",
"PayloadParsers/CommandParser.cpp",
"PayloadParsers/FullPayloadParserFactoryCollection.cpp",
"PayloadParsers/PriorityParser.cpp",
@@ -40,6 +41,8 @@ sources = [
"PayloadParsers/StorageParser.cpp",
"PayloadParsers/StatusParser.cpp",
"PayloadParsers/StatusShowParser.cpp",
+ "PayloadParsers/StreamInitiationParser.cpp",
+ "PayloadParsers/BytestreamsParser.cpp",
"PayloadParsers/VCardParser.cpp",
"PayloadParsers/VCardUpdateParser.cpp",
"PayloadParsers/DelayParser.cpp",
diff --git a/Swiften/QA/StorageTest/FileReadBytestreamTest.cpp b/Swiften/QA/StorageTest/FileReadBytestreamTest.cpp
new file mode 100644
index 0000000..b8e9fd4
--- /dev/null
+++ b/Swiften/QA/StorageTest/FileReadBytestreamTest.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/FileTransfer/FileReadBytestream.h"
+#include "SwifTools/Application/PlatformApplicationPathProvider.h"
+
+using namespace Swift;
+
+class FileReadBytestreamTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(FileReadBytestreamTest);
+ CPPUNIT_TEST(testRead);
+ CPPUNIT_TEST(testRead_Twice);
+ CPPUNIT_TEST(testIsFinished_NotFinished);
+ CPPUNIT_TEST(testIsFinished_IsFinished);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void setUp() {
+ pathProvider = new PlatformApplicationPathProvider("FileReadBytestreamTest");
+ }
+
+ void tearDown() {
+ delete pathProvider;
+ }
+
+ void testRead() {
+ std::auto_ptr<FileReadBytestream> testling(createTestling());
+
+ ByteArray result = testling->read(10);
+
+ CPPUNIT_ASSERT_EQUAL(String("/*\n * Copy"), result.toString());
+ }
+
+ void testRead_Twice() {
+ std::auto_ptr<FileReadBytestream> testling(createTestling());
+
+ testling->read(10);
+ ByteArray result = testling->read(10);
+
+ CPPUNIT_ASSERT_EQUAL(String("right (c) "), result.toString());
+ }
+
+ void testIsFinished_NotFinished() {
+ std::auto_ptr<FileReadBytestream> testling(createTestling());
+
+ testling->read(10);
+
+ CPPUNIT_ASSERT(!testling->isFinished());
+ }
+
+ void testIsFinished_IsFinished() {
+ std::auto_ptr<FileReadBytestream> testling(createTestling());
+
+ testling->read(4096);
+
+ CPPUNIT_ASSERT(testling->isFinished());
+ }
+
+ private:
+ FileReadBytestream* createTestling() {
+ return new FileReadBytestream(pathProvider->getExecutableDir() / "FileReadBytestreamTest.cpp");
+ }
+
+ PlatformApplicationPathProvider* pathProvider;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(FileReadBytestreamTest);
diff --git a/Swiften/QA/StorageTest/SConscript b/Swiften/QA/StorageTest/SConscript
index bb8646e..4d0a197 100644
--- a/Swiften/QA/StorageTest/SConscript
+++ b/Swiften/QA/StorageTest/SConscript
@@ -15,5 +15,6 @@ if env["TEST"] :
tester = myenv.Program("StorageTest", [
"VCardFileStorageTest.cpp",
+ "FileReadBytestreamTest.cpp",
])
myenv.Test(tester, "system", is_checker = True)
diff --git a/Swiften/SConscript b/Swiften/SConscript
index fb79963..b371826 100644
--- a/Swiften/SConscript
+++ b/Swiften/SConscript
@@ -72,6 +72,7 @@ if env["SCONS_STAGE"] == "build" :
"Serializer/ComponentHandshakeSerializer.cpp",
"Serializer/PayloadSerializer.cpp",
"Serializer/PayloadSerializerCollection.cpp",
+ "Serializer/PayloadSerializers/IBBSerializer.cpp",
"Serializer/PayloadSerializers/CapsInfoSerializer.cpp",
"Serializer/PayloadSerializers/ChatStateSerializer.cpp",
"Serializer/PayloadSerializers/DiscoInfoSerializer.cpp",
@@ -86,6 +87,8 @@ if env["SCONS_STAGE"] == "build" :
"Serializer/PayloadSerializers/SecurityLabelSerializer.cpp",
"Serializer/PayloadSerializers/SecurityLabelsCatalogSerializer.cpp",
"Serializer/PayloadSerializers/SoftwareVersionSerializer.cpp",
+ "Serializer/PayloadSerializers/StreamInitiationSerializer.cpp",
+ "Serializer/PayloadSerializers/BytestreamsSerializer.cpp",
"Serializer/PayloadSerializers/VCardSerializer.cpp",
"Serializer/PayloadSerializers/VCardUpdateSerializer.cpp",
"Serializer/PayloadSerializers/StorageSerializer.cpp",
@@ -130,6 +133,7 @@ if env["SCONS_STAGE"] == "build" :
"Disco",
"VCards",
"Network",
+ "FileTransfer",
"History",
"StreamStack",
"LinkLocal",
@@ -169,6 +173,8 @@ if env["SCONS_STAGE"] == "build" :
File("Elements/UnitTest/StanzasTest.cpp"),
File("EventLoop/UnitTest/EventLoopTest.cpp"),
File("EventLoop/UnitTest/SimpleEventLoopTest.cpp"),
+ File("FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp"),
+ File("FileTransfer/UnitTest/IBBSendSessionTest.cpp"),
# File("History/UnitTest/SQLiteHistoryManagerTest.cpp"),
File("JID/UnitTest/JIDTest.cpp"),
File("LinkLocal/UnitTest/LinkLocalConnectorTest.cpp"),
@@ -186,11 +192,13 @@ if env["SCONS_STAGE"] == "build" :
File("Parser/PayloadParsers/UnitTest/RawXMLPayloadParserTest.cpp"),
File("Parser/PayloadParsers/UnitTest/ResourceBindParserTest.cpp"),
File("Parser/PayloadParsers/UnitTest/RosterParserTest.cpp"),
+ File("Parser/PayloadParsers/UnitTest/IBBParserTest.cpp"),
File("Parser/PayloadParsers/UnitTest/SecurityLabelParserTest.cpp"),
File("Parser/PayloadParsers/UnitTest/SecurityLabelsCatalogParserTest.cpp"),
File("Parser/PayloadParsers/UnitTest/SoftwareVersionParserTest.cpp"),
File("Parser/PayloadParsers/UnitTest/StatusParserTest.cpp"),
File("Parser/PayloadParsers/UnitTest/StatusShowParserTest.cpp"),
+ File("Parser/PayloadParsers/UnitTest/StreamInitiationParserTest.cpp"),
File("Parser/PayloadParsers/UnitTest/VCardParserTest.cpp"),
File("Parser/PayloadParsers/UnitTest/StorageParserTest.cpp"),
File("Parser/PayloadParsers/UnitTest/PrivateStorageParserTest.cpp"),
@@ -230,6 +238,7 @@ if env["SCONS_STAGE"] == "build" :
File("Serializer/PayloadSerializers/UnitTest/SoftwareVersionSerializerTest.cpp"),
File("Serializer/PayloadSerializers/UnitTest/StatusSerializerTest.cpp"),
File("Serializer/PayloadSerializers/UnitTest/StatusShowSerializerTest.cpp"),
+ File("Serializer/PayloadSerializers/UnitTest/StreamInitiationSerializerTest.cpp"),
File("Serializer/PayloadSerializers/UnitTest/VCardUpdateSerializerTest.cpp"),
File("Serializer/PayloadSerializers/UnitTest/VCardSerializerTest.cpp"),
File("Serializer/PayloadSerializers/UnitTest/StorageSerializerTest.cpp"),
diff --git a/Swiften/Serializer/PayloadSerializers/BytestreamsSerializer.cpp b/Swiften/Serializer/PayloadSerializers/BytestreamsSerializer.cpp
new file mode 100644
index 0000000..9acabee
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/BytestreamsSerializer.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Swiften/Serializer/PayloadSerializers/BytestreamsSerializer.h"
+
+#include <boost/shared_ptr.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Serializer/XML/XMLElement.h"
+#include "Swiften/Serializer/PayloadSerializerCollection.h"
+
+
+namespace Swift {
+
+BytestreamsSerializer::BytestreamsSerializer() {
+}
+
+String BytestreamsSerializer::serializePayload(boost::shared_ptr<Bytestreams> bytestreams) const {
+ XMLElement queryElement("query", "http://jabber.org/protocol/bytestreams");
+ queryElement.setAttribute("sid", bytestreams->getStreamID());
+ foreach(const Bytestreams::StreamHost& streamHost, bytestreams->getStreamHosts()) {
+ boost::shared_ptr<XMLElement> streamHostElement(new XMLElement("streamhost"));
+ streamHostElement->setAttribute("host", streamHost.host);
+ streamHostElement->setAttribute("jid", streamHost.jid.toString());
+ streamHostElement->setAttribute("port", boost::lexical_cast<std::string>(streamHost.port));
+ queryElement.addNode(streamHostElement);
+ }
+
+ if (bytestreams->getUsedStreamHost()) {
+ boost::shared_ptr<XMLElement> streamHostElement(new XMLElement("streamhost-used"));
+ streamHostElement->setAttribute("jid", *bytestreams->getUsedStreamHost());
+ queryElement.addNode(streamHostElement);
+ }
+ return queryElement.serialize();
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/BytestreamsSerializer.h b/Swiften/Serializer/PayloadSerializers/BytestreamsSerializer.h
new file mode 100644
index 0000000..50d58c2
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/BytestreamsSerializer.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include "Swiften/Serializer/GenericPayloadSerializer.h"
+#include "Swiften/Elements/Bytestreams.h"
+
+namespace Swift {
+ class PayloadSerializerCollection;
+
+ class BytestreamsSerializer : public GenericPayloadSerializer<Bytestreams> {
+ public:
+ BytestreamsSerializer();
+
+ virtual String serializePayload(boost::shared_ptr<Bytestreams>) const;
+ };
+}
diff --git a/Swiften/Serializer/PayloadSerializers/FormSerializer.cpp b/Swiften/Serializer/PayloadSerializers/FormSerializer.cpp
index ece8fd8..77c2fd4 100644
--- a/Swiften/Serializer/PayloadSerializers/FormSerializer.cpp
+++ b/Swiften/Serializer/PayloadSerializers/FormSerializer.cpp
@@ -145,7 +145,9 @@ boost::shared_ptr<XMLElement> FormSerializer::fieldToXML(boost::shared_ptr<FormF
foreach (const FormField::Option& option, field->getOptions()) {
boost::shared_ptr<XMLElement> optionElement(new XMLElement("option"));
- optionElement->setAttribute("label", option.label);
+ if (!option.label.isEmpty()) {
+ optionElement->setAttribute("label", option.label);
+ }
boost::shared_ptr<XMLElement> valueElement(new XMLElement("value"));
valueElement->addNode(XMLTextNode::create(option.value));
diff --git a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
index 8598f17..1d04456 100644
--- a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
+++ b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
@@ -7,6 +7,7 @@
#include "Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h"
#include "Swiften/Base/foreach.h"
#include "Swiften/Serializer/PayloadSerializer.h"
+#include "Swiften/Serializer/PayloadSerializers/IBBSerializer.h"
#include "Swiften/Serializer/PayloadSerializers/BodySerializer.h"
#include "Swiften/Serializer/PayloadSerializers/SubjectSerializer.h"
#include "Swiften/Serializer/PayloadSerializers/ChatStateSerializer.h"
@@ -26,6 +27,8 @@
#include "Swiften/Serializer/PayloadSerializers/StartSessionSerializer.h"
#include "Swiften/Serializer/PayloadSerializers/SecurityLabelSerializer.h"
#include "Swiften/Serializer/PayloadSerializers/SecurityLabelsCatalogSerializer.h"
+#include "Swiften/Serializer/PayloadSerializers/StreamInitiationSerializer.h"
+#include "Swiften/Serializer/PayloadSerializers/ByteStreamsSerializer.h"
#include "Swiften/Serializer/PayloadSerializers/VCardSerializer.h"
#include "Swiften/Serializer/PayloadSerializers/VCardUpdateSerializer.h"
#include "Swiften/Serializer/PayloadSerializers/RawXMLPayloadSerializer.h"
@@ -39,6 +42,7 @@
namespace Swift {
FullPayloadSerializerCollection::FullPayloadSerializerCollection() {
+ serializers_.push_back(new IBBSerializer());
serializers_.push_back(new BodySerializer());
serializers_.push_back(new SubjectSerializer());
serializers_.push_back(new ChatStateSerializer());
@@ -58,6 +62,8 @@ FullPayloadSerializerCollection::FullPayloadSerializerCollection() {
serializers_.push_back(new StartSessionSerializer());
serializers_.push_back(new SecurityLabelSerializer());
serializers_.push_back(new SecurityLabelsCatalogSerializer());
+ serializers_.push_back(new StreamInitiationSerializer());
+ serializers_.push_back(new BytestreamsSerializer());
serializers_.push_back(new VCardSerializer());
serializers_.push_back(new VCardUpdateSerializer());
serializers_.push_back(new RawXMLPayloadSerializer());
diff --git a/Swiften/Serializer/PayloadSerializers/IBBSerializer.cpp b/Swiften/Serializer/PayloadSerializers/IBBSerializer.cpp
new file mode 100644
index 0000000..5e52145
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/IBBSerializer.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Swiften/Serializer/PayloadSerializers/IBBSerializer.h"
+
+#include <boost/shared_ptr.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Serializer/XML/XMLElement.h"
+#include "Swiften/Serializer/XML/XMLTextNode.h"
+#include "Swiften/Serializer/PayloadSerializerCollection.h"
+#include "Swiften/StringCodecs/Base64.h"
+
+namespace Swift {
+
+IBBSerializer::IBBSerializer() {
+}
+
+String IBBSerializer::serializePayload(boost::shared_ptr<IBB> ibb) const {
+ switch(ibb->getAction()) {
+ case IBB::Data: {
+ XMLElement ibbElement("data", "http://jabber.org/protocol/ibb");
+ ibbElement.setAttribute("sid", ibb->getStreamID());
+ if (ibb->getSequenceNumber() >= 0) {
+ ibbElement.setAttribute("seq", boost::lexical_cast<std::string>(ibb->getSequenceNumber()));
+ }
+ ibbElement.addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(Base64::encode(ibb->getData()))));
+ return ibbElement.serialize();
+ }
+ case IBB::Open: {
+ XMLElement ibbElement("open", "http://jabber.org/protocol/ibb");
+ ibbElement.setAttribute("sid", ibb->getStreamID());
+ switch (ibb->getStanzaType()) {
+ case IBB::IQStanza: ibbElement.setAttribute("stanza", "iq"); break;
+ case IBB::MessageStanza: ibbElement.setAttribute("stanza", "message"); break;
+ }
+ assert(ibb->getBlockSize() > 0);
+ ibbElement.setAttribute("block-size", boost::lexical_cast<std::string>(ibb->getBlockSize()));
+ return ibbElement.serialize();
+ }
+ case IBB::Close: {
+ XMLElement ibbElement("close", "http://jabber.org/protocol/ibb");
+ ibbElement.setAttribute("sid", ibb->getStreamID());
+ return ibbElement.serialize();
+ }
+ }
+ return "";
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/IBBSerializer.h b/Swiften/Serializer/PayloadSerializers/IBBSerializer.h
new file mode 100644
index 0000000..71b1c80
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/IBBSerializer.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include "Swiften/Serializer/GenericPayloadSerializer.h"
+#include "Swiften/Elements/IBB.h"
+
+namespace Swift {
+ class PayloadSerializerCollection;
+
+ class IBBSerializer : public GenericPayloadSerializer<IBB> {
+ public:
+ IBBSerializer();
+
+ virtual String serializePayload(boost::shared_ptr<IBB>) const;
+ };
+}
diff --git a/Swiften/Serializer/PayloadSerializers/StreamInitiationSerializer.cpp b/Swiften/Serializer/PayloadSerializers/StreamInitiationSerializer.cpp
new file mode 100644
index 0000000..71702d0
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/StreamInitiationSerializer.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Swiften/Serializer/PayloadSerializers/StreamInitiationSerializer.h"
+
+#include <boost/shared_ptr.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Serializer/XML/XMLElement.h"
+#include "Swiften/Serializer/XML/XMLTextNode.h"
+#include "Swiften/Serializer/XML/XMLRawTextNode.h"
+#include "Swiften/Serializer/PayloadSerializerCollection.h"
+#include "Swiften/Serializer/PayloadSerializers/FormSerializer.h"
+
+
+#define FILE_TRANSFER_NS "http://jabber.org/protocol/si/profile/file-transfer"
+#define FEATURE_NEG_NS "http://jabber.org/protocol/feature-neg"
+
+namespace Swift {
+
+StreamInitiationSerializer::StreamInitiationSerializer() {
+}
+
+String StreamInitiationSerializer::serializePayload(boost::shared_ptr<StreamInitiation> streamInitiation) const {
+ assert(streamInitiation->getIsFileTransfer());
+
+ XMLElement siElement("si", "http://jabber.org/protocol/si");
+ if (!streamInitiation->getID().isEmpty()) {
+ siElement.setAttribute("id", streamInitiation->getID());
+ }
+ siElement.setAttribute("profile", FILE_TRANSFER_NS);
+
+ if (streamInitiation->getFileInfo()) {
+ StreamInitiation::FileInfo file = *streamInitiation->getFileInfo();
+ boost::shared_ptr<XMLElement> fileElement(new XMLElement("file", "http://jabber.org/protocol/si/profile/file-transfer"));
+ fileElement->setAttribute("name", file.name);
+ if (file.size != -1) {
+ fileElement->setAttribute("size", boost::lexical_cast<std::string>(file.size));
+ }
+ if (!file.description.isEmpty()) {
+ boost::shared_ptr<XMLElement> descElement(new XMLElement("desc"));
+ descElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(file.description)));
+ fileElement->addNode(descElement);
+ }
+ siElement.addNode(fileElement);
+ }
+
+ boost::shared_ptr<XMLElement> featureElement(new XMLElement("feature", "http://jabber.org/protocol/feature-neg"));
+ if (streamInitiation->getProvidedMethods().size() > 0) {
+ Form::ref form(new Form(Form::FormType));
+ ListSingleFormField::ref field = ListSingleFormField::create();
+ field->setName("stream-method");
+ foreach(const String& method, streamInitiation->getProvidedMethods()) {
+ field->addOption(FormField::Option("", method));
+ }
+ form->addField(field);
+ featureElement->addNode(boost::shared_ptr<XMLRawTextNode>(new XMLRawTextNode(FormSerializer().serialize(form))));
+ }
+ else if (!streamInitiation->getRequestedMethod().isEmpty()) {
+ Form::ref form(new Form(Form::SubmitType));
+ ListSingleFormField::ref field = ListSingleFormField::create(streamInitiation->getRequestedMethod());
+ field->setName("stream-method");
+ form->addField(field);
+ featureElement->addNode(boost::shared_ptr<XMLRawTextNode>(new XMLRawTextNode(FormSerializer().serialize(form))));
+ }
+ siElement.addNode(featureElement);
+ return siElement.serialize();
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/StreamInitiationSerializer.h b/Swiften/Serializer/PayloadSerializers/StreamInitiationSerializer.h
new file mode 100644
index 0000000..35c71b9
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/StreamInitiationSerializer.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include "Swiften/Serializer/GenericPayloadSerializer.h"
+#include "Swiften/Elements/StreamInitiation.h"
+
+namespace Swift {
+ class PayloadSerializerCollection;
+
+ class StreamInitiationSerializer : public GenericPayloadSerializer<StreamInitiation> {
+ public:
+ StreamInitiationSerializer();
+
+ virtual String serializePayload(boost::shared_ptr<StreamInitiation>) const;
+ };
+}
diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/FormSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/FormSerializerTest.cpp
index ebc8664..4ed3ba9 100644
--- a/Swiften/Serializer/PayloadSerializers/UnitTest/FormSerializerTest.cpp
+++ b/Swiften/Serializer/PayloadSerializers/UnitTest/FormSerializerTest.cpp
@@ -84,7 +84,7 @@ class FormSerializerTest : public CppUnit::TestFixture {
field->addOption(FormField::Option("30", "30"));
field->addOption(FormField::Option("50", "50"));
field->addOption(FormField::Option("100", "100"));
- field->addOption(FormField::Option("None", "none"));
+ field->addOption(FormField::Option("", "none"));
form->addField(field);
std::vector<JID> jids;
@@ -132,7 +132,7 @@ class FormSerializerTest : public CppUnit::TestFixture {
"<option label=\"30\"><value>30</value></option>"
"<option label=\"50\"><value>50</value></option>"
"<option label=\"100\"><value>100</value></option>"
- "<option label=\"None\"><value>none</value></option>"
+ "<option><value>none</value></option>"
"</field>"
"<field label=\"People to invite\" type=\"jid-multi\" var=\"invitelist\">"
"<desc>Tell all your friends about your new bot!</desc>"
diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/StreamInitiationSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/StreamInitiationSerializerTest.cpp
new file mode 100644
index 0000000..12c8485
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/UnitTest/StreamInitiationSerializerTest.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Serializer/PayloadSerializers/StreamInitiationSerializer.h"
+
+using namespace Swift;
+
+class StreamInitiationSerializerTest : public CppUnit::TestFixture{
+ CPPUNIT_TEST_SUITE(StreamInitiationSerializerTest);
+ CPPUNIT_TEST(testSerialize_Request);
+ CPPUNIT_TEST(testSerialize_Response);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void testSerialize_Request() {
+ StreamInitiationSerializer testling;
+ boost::shared_ptr<StreamInitiation> streamInitiation(new StreamInitiation());
+ StreamInitiation::FileInfo fileInfo("test.txt", "This is info about the file.", 1022);
+ streamInitiation->setID("a0");
+ streamInitiation->setFileInfo(fileInfo);
+ streamInitiation->addProvidedMethod("http://jabber.org/protocol/bytestreams");
+ streamInitiation->addProvidedMethod("jabber:iq:oob");
+ streamInitiation->addProvidedMethod("http://jabber.org/protocol/ibb");
+
+ CPPUNIT_ASSERT_EQUAL(String(
+ "<si id=\"a0\" profile=\"http://jabber.org/protocol/si/profile/file-transfer\" xmlns=\"http://jabber.org/protocol/si\">"
+ "<file name=\"test.txt\" size=\"1022\" xmlns=\"http://jabber.org/protocol/si/profile/file-transfer\">"
+ "<desc>This is info about the file.</desc>"
+ "</file>"
+ "<feature xmlns=\"http://jabber.org/protocol/feature-neg\">"
+ "<x type=\"form\" xmlns=\"jabber:x:data\">"
+ "<field type=\"list-single\" var=\"stream-method\">"
+ "<option><value>http://jabber.org/protocol/bytestreams</value></option>"
+ "<option><value>jabber:iq:oob</value></option>"
+ "<option><value>http://jabber.org/protocol/ibb</value></option>"
+ "</field>"
+ "</x>"
+ "</feature>"
+ "</si>"
+ ), testling.serialize(streamInitiation));
+ }
+
+ void testSerialize_Response() {
+ StreamInitiationSerializer testling;
+ boost::shared_ptr<StreamInitiation> streamInitiation(new StreamInitiation());
+ streamInitiation->setRequestedMethod("http://jabber.org/protocol/bytestreams");
+
+ CPPUNIT_ASSERT_EQUAL(String(
+ "<si profile=\"http://jabber.org/protocol/si/profile/file-transfer\" xmlns=\"http://jabber.org/protocol/si\">"
+ "<feature xmlns=\"http://jabber.org/protocol/feature-neg\">"
+ "<x type=\"submit\" xmlns=\"jabber:x:data\">"
+ "<field type=\"list-single\" var=\"stream-method\">"
+ "<value>http://jabber.org/protocol/bytestreams</value>"
+ "</field>"
+ "</x>"
+ "</feature>"
+ "</si>"
+ ), testling.serialize(streamInitiation));
+ }
+
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(StreamInitiationSerializerTest);