From 1b58ef2af54456004390a0888c3edf104e3baa99 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Sun, 17 Oct 2010 14:13:36 +0200
Subject: Added beginnings of outgoing file transfer to Swiften.


diff --git a/.cproject b/.cproject
index 9bcb7c5..6784073 100644
--- a/.cproject
+++ b/.cproject
@@ -1746,6 +1746,297 @@
 				</scannerConfigBuildInfo>
 			</storageModule>
 		</cconfiguration>
+		<cconfiguration id="0.980756260.1834106966.226646757.1415775202">
+			<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="0.980756260.1834106966.226646757.1415775202" moduleId="org.eclipse.cdt.core.settings" name="SendFile">
+				<externalSettings/>
+				<extensions>
+					<extension id="org.eclipse.cdt.core.MachO64" point="org.eclipse.cdt.core.BinaryParser"/>
+					<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
+					<extension id="org.eclipse.cdt.core.PE" point="org.eclipse.cdt.core.BinaryParser"/>
+					<extension id="org.eclipse.cdt.core.GNU_ELF" point="org.eclipse.cdt.core.BinaryParser"/>
+					<extension id="org.eclipse.cdt.core.VCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="be.el_tramo.ecppunit.CPPUnitErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+				</extensions>
+			</storageModule>
+			<storageModule moduleId="cdtBuildSystem" version="4.0.0">
+				<configuration artifactName="${ProjName}" buildProperties="" description="" errorParsers="org.eclipse.cdt.core.VCErrorParser;org.eclipse.cdt.core.GmakeErrorParser;org.eclipse.cdt.core.CWDLocator;org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GASErrorParser;org.eclipse.cdt.core.GLDErrorParser;be.el_tramo.ecppunit.CPPUnitErrorParser" id="0.980756260.1834106966.226646757.1415775202" name="SendFile" parent="org.eclipse.cdt.build.core.prefbase.cfg">
+					<folderInfo id="0.980756260.1834106966.226646757.1415775202." name="/" resourcePath="">
+						<toolChain errorParsers="" id="org.eclipse.cdt.build.core.prefbase.toolchain.1076959452" name="No ToolChain" resourceTypeBasedDiscovery="false" superClass="org.eclipse.cdt.build.core.prefbase.toolchain">
+							<targetPlatform binaryParser="org.eclipse.cdt.core.MachO64;org.eclipse.cdt.core.ELF;org.eclipse.cdt.core.PE;org.eclipse.cdt.core.GNU_ELF" id="org.eclipse.cdt.build.core.prefbase.toolchain.1076959452.1988775774" name=""/>
+							<builder arguments="${ProjDirPath}/3rdParty/SCons/scons.py" autoBuildTarget="Swiften/Examples/SendFile" buildPath="" cleanBuildTarget="-c" command="python" enableAutoBuild="true" errorParsers="org.eclipse.cdt.core.GmakeErrorParser;org.eclipse.cdt.core.CWDLocator" id="org.eclipse.cdt.build.core.settings.default.builder.1201609851" incrementalBuildTarget="Swiften/Examples/SendFile" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" superClass="org.eclipse.cdt.build.core.settings.default.builder"/>
+							<tool errorParsers="org.eclipse.cdt.core.VCErrorParser;org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GASErrorParser;org.eclipse.cdt.core.GLDErrorParser" id="org.eclipse.cdt.build.core.settings.holder.libs.323393126" name="holder for library settings" superClass="org.eclipse.cdt.build.core.settings.holder.libs"/>
+							<tool errorParsers="org.eclipse.cdt.core.VCErrorParser;org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GASErrorParser;org.eclipse.cdt.core.GLDErrorParser" id="org.eclipse.cdt.build.core.settings.holder.596341006" name="Assembly" superClass="org.eclipse.cdt.build.core.settings.holder">
+								<inputType id="org.eclipse.cdt.build.core.settings.holder.inType.639143750" languageId="org.eclipse.cdt.core.assembly" languageName="Assembly" sourceContentType="org.eclipse.cdt.core.asmSource" superClass="org.eclipse.cdt.build.core.settings.holder.inType"/>
+							</tool>
+							<tool errorParsers="org.eclipse.cdt.core.VCErrorParser;org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GASErrorParser;org.eclipse.cdt.core.GLDErrorParser" id="org.eclipse.cdt.build.core.settings.holder.915501366" name="GNU C++" superClass="org.eclipse.cdt.build.core.settings.holder">
+								<inputType id="org.eclipse.cdt.build.core.settings.holder.inType.980974316" languageId="org.eclipse.cdt.core.g++" languageName="GNU C++" sourceContentType="org.eclipse.cdt.core.cxxSource,org.eclipse.cdt.core.cxxHeader" superClass="org.eclipse.cdt.build.core.settings.holder.inType"/>
+							</tool>
+							<tool errorParsers="org.eclipse.cdt.core.VCErrorParser;org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GASErrorParser;org.eclipse.cdt.core.GLDErrorParser" id="org.eclipse.cdt.build.core.settings.holder.1564153861" name="GNU C" superClass="org.eclipse.cdt.build.core.settings.holder">
+								<inputType id="org.eclipse.cdt.build.core.settings.holder.inType.1573459519" languageId="org.eclipse.cdt.core.gcc" languageName="GNU C" sourceContentType="org.eclipse.cdt.core.cSource,org.eclipse.cdt.core.cHeader" superClass="org.eclipse.cdt.build.core.settings.holder.inType"/>
+							</tool>
+						</toolChain>
+					</folderInfo>
+				</configuration>
+			</storageModule>
+			<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
+			<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"/>
+			<storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings"/>
+			<storageModule moduleId="org.eclipse.cdt.core.language.mapping"/>
+			<storageModule moduleId="scannerConfiguration">
+				<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+				<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
+					<buildOutputProvider>
+						<openAction enabled="true" filePath=""/>
+						<parser enabled="true"/>
+					</buildOutputProvider>
+					<scannerInfoProvider id="specsFile">
+						<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+						<parser enabled="true"/>
+					</scannerInfoProvider>
+				</profile>
+				<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
+					<buildOutputProvider>
+						<openAction enabled="true" filePath=""/>
+						<parser enabled="true"/>
+					</buildOutputProvider>
+					<scannerInfoProvider id="makefileGenerator">
+						<runAction arguments="-E -P -v -dD" command="" useDefault="true"/>
+						<parser enabled="true"/>
+					</scannerInfoProvider>
+				</profile>
+				<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
+					<buildOutputProvider>
+						<openAction enabled="true" filePath=""/>
+						<parser enabled="true"/>
+					</buildOutputProvider>
+					<scannerInfoProvider id="specsFile">
+						<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+						<parser enabled="true"/>
+					</scannerInfoProvider>
+				</profile>
+				<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
+					<buildOutputProvider>
+						<openAction enabled="true" filePath=""/>
+						<parser enabled="true"/>
+					</buildOutputProvider>
+					<scannerInfoProvider id="specsFile">
+						<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+						<parser enabled="true"/>
+					</scannerInfoProvider>
+				</profile>
+				<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
+					<buildOutputProvider>
+						<openAction enabled="true" filePath=""/>
+						<parser enabled="true"/>
+					</buildOutputProvider>
+					<scannerInfoProvider id="specsFile">
+						<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+						<parser enabled="true"/>
+					</scannerInfoProvider>
+				</profile>
+				<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
+					<buildOutputProvider>
+						<openAction enabled="true" filePath=""/>
+						<parser enabled="true"/>
+					</buildOutputProvider>
+					<scannerInfoProvider id="specsFile">
+						<runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/${specs_file}&quot;'" command="sh" useDefault="true"/>
+						<parser enabled="true"/>
+					</scannerInfoProvider>
+				</profile>
+				<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
+					<buildOutputProvider>
+						<openAction enabled="true" filePath=""/>
+						<parser enabled="true"/>
+					</buildOutputProvider>
+					<scannerInfoProvider id="specsFile">
+						<runAction arguments="-c 'g++ -E -P -v -dD &quot;${plugin_state_location}/specs.cpp&quot;'" command="sh" useDefault="true"/>
+						<parser enabled="true"/>
+					</scannerInfoProvider>
+				</profile>
+				<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
+					<buildOutputProvider>
+						<openAction enabled="true" filePath=""/>
+						<parser enabled="true"/>
+					</buildOutputProvider>
+					<scannerInfoProvider id="specsFile">
+						<runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/specs.c&quot;'" command="sh" useDefault="true"/>
+						<parser enabled="true"/>
+					</scannerInfoProvider>
+				</profile>
+				<scannerConfigBuildInfo instanceId="0.980756260.1834106966.1269306596">
+					<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+					<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
+						<buildOutputProvider>
+							<openAction enabled="true" filePath=""/>
+							<parser enabled="true"/>
+						</buildOutputProvider>
+						<scannerInfoProvider id="specsFile">
+							<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+							<parser enabled="true"/>
+						</scannerInfoProvider>
+					</profile>
+					<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
+						<buildOutputProvider>
+							<openAction enabled="true" filePath=""/>
+							<parser enabled="true"/>
+						</buildOutputProvider>
+						<scannerInfoProvider id="makefileGenerator">
+							<runAction arguments="-E -P -v -dD" command="" useDefault="true"/>
+							<parser enabled="true"/>
+						</scannerInfoProvider>
+					</profile>
+					<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
+						<buildOutputProvider>
+							<openAction enabled="true" filePath=""/>
+							<parser enabled="true"/>
+						</buildOutputProvider>
+						<scannerInfoProvider id="specsFile">
+							<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+							<parser enabled="true"/>
+						</scannerInfoProvider>
+					</profile>
+					<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
+						<buildOutputProvider>
+							<openAction enabled="true" filePath=""/>
+							<parser enabled="true"/>
+						</buildOutputProvider>
+						<scannerInfoProvider id="specsFile">
+							<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+							<parser enabled="true"/>
+						</scannerInfoProvider>
+					</profile>
+					<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
+						<buildOutputProvider>
+							<openAction enabled="true" filePath=""/>
+							<parser enabled="true"/>
+						</buildOutputProvider>
+						<scannerInfoProvider id="specsFile">
+							<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+							<parser enabled="true"/>
+						</scannerInfoProvider>
+					</profile>
+					<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
+						<buildOutputProvider>
+							<openAction enabled="true" filePath=""/>
+							<parser enabled="true"/>
+						</buildOutputProvider>
+						<scannerInfoProvider id="specsFile">
+							<runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/${specs_file}&quot;'" command="sh" useDefault="true"/>
+							<parser enabled="true"/>
+						</scannerInfoProvider>
+					</profile>
+					<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
+						<buildOutputProvider>
+							<openAction enabled="true" filePath=""/>
+							<parser enabled="true"/>
+						</buildOutputProvider>
+						<scannerInfoProvider id="specsFile">
+							<runAction arguments="-c 'g++ -E -P -v -dD &quot;${plugin_state_location}/specs.cpp&quot;'" command="sh" useDefault="true"/>
+							<parser enabled="true"/>
+						</scannerInfoProvider>
+					</profile>
+					<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
+						<buildOutputProvider>
+							<openAction enabled="true" filePath=""/>
+							<parser enabled="true"/>
+						</buildOutputProvider>
+						<scannerInfoProvider id="specsFile">
+							<runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/specs.c&quot;'" command="sh" useDefault="true"/>
+							<parser enabled="true"/>
+						</scannerInfoProvider>
+					</profile>
+				</scannerConfigBuildInfo>
+				<scannerConfigBuildInfo instanceId="0.980756260">
+					<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+					<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
+						<buildOutputProvider>
+							<openAction enabled="true" filePath=""/>
+							<parser enabled="true"/>
+						</buildOutputProvider>
+						<scannerInfoProvider id="specsFile">
+							<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+							<parser enabled="true"/>
+						</scannerInfoProvider>
+					</profile>
+					<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
+						<buildOutputProvider>
+							<openAction enabled="true" filePath=""/>
+							<parser enabled="true"/>
+						</buildOutputProvider>
+						<scannerInfoProvider id="makefileGenerator">
+							<runAction arguments="-E -P -v -dD" command="" useDefault="true"/>
+							<parser enabled="true"/>
+						</scannerInfoProvider>
+					</profile>
+					<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
+						<buildOutputProvider>
+							<openAction enabled="true" filePath=""/>
+							<parser enabled="true"/>
+						</buildOutputProvider>
+						<scannerInfoProvider id="specsFile">
+							<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+							<parser enabled="true"/>
+						</scannerInfoProvider>
+					</profile>
+					<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
+						<buildOutputProvider>
+							<openAction enabled="true" filePath=""/>
+							<parser enabled="true"/>
+						</buildOutputProvider>
+						<scannerInfoProvider id="specsFile">
+							<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+							<parser enabled="true"/>
+						</scannerInfoProvider>
+					</profile>
+					<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
+						<buildOutputProvider>
+							<openAction enabled="true" filePath=""/>
+							<parser enabled="true"/>
+						</buildOutputProvider>
+						<scannerInfoProvider id="specsFile">
+							<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+							<parser enabled="true"/>
+						</scannerInfoProvider>
+					</profile>
+					<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
+						<buildOutputProvider>
+							<openAction enabled="true" filePath=""/>
+							<parser enabled="true"/>
+						</buildOutputProvider>
+						<scannerInfoProvider id="specsFile">
+							<runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/${specs_file}&quot;'" command="sh" useDefault="true"/>
+							<parser enabled="true"/>
+						</scannerInfoProvider>
+					</profile>
+					<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
+						<buildOutputProvider>
+							<openAction enabled="true" filePath=""/>
+							<parser enabled="true"/>
+						</buildOutputProvider>
+						<scannerInfoProvider id="specsFile">
+							<runAction arguments="-c 'g++ -E -P -v -dD &quot;${plugin_state_location}/specs.cpp&quot;'" command="sh" useDefault="true"/>
+							<parser enabled="true"/>
+						</scannerInfoProvider>
+					</profile>
+					<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
+						<buildOutputProvider>
+							<openAction enabled="true" filePath=""/>
+							<parser enabled="true"/>
+						</buildOutputProvider>
+						<scannerInfoProvider id="specsFile">
+							<runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/specs.c&quot;'" command="sh" useDefault="true"/>
+							<parser enabled="true"/>
+						</scannerInfoProvider>
+					</profile>
+				</scannerConfigBuildInfo>
+			</storageModule>
+		</cconfiguration>
 	</storageModule>
 	<storageModule moduleId="cdtBuildSystem" version="4.0.0">
 		<project id="swift.null.189117846" name="swift"/>
diff --git a/.project b/.project
index fcdfcdc..17363c7 100644
--- a/.project
+++ b/.project
@@ -18,7 +18,7 @@
 				</dictionary>
 				<dictionary>
 					<key>org.eclipse.cdt.make.core.autoBuildTarget</key>
-					<value></value>
+					<value>check=1 QA</value>
 				</dictionary>
 				<dictionary>
 					<key>org.eclipse.cdt.make.core.buildArguments</key>
@@ -29,6 +29,10 @@
 					<value>python</value>
 				</dictionary>
 				<dictionary>
+					<key>org.eclipse.cdt.make.core.buildLocation</key>
+					<value></value>
+				</dictionary>
+				<dictionary>
 					<key>org.eclipse.cdt.make.core.cleanBuildTarget</key>
 					<value>-c</value>
 				</dictionary>
@@ -50,7 +54,7 @@
 				</dictionary>
 				<dictionary>
 					<key>org.eclipse.cdt.make.core.fullBuildTarget</key>
-					<value></value>
+					<value>check=1 QA</value>
 				</dictionary>
 				<dictionary>
 					<key>org.eclipse.cdt.make.core.stopOnError</key>
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);
-- 
cgit v0.10.2-6-g49f6