Repository: SpectrumIM/spectrum2 (language: python)

Issue:
Telegram messages with images
I use libpurple backend for connect to telegram. When I send image (from conversation) to telegram contact, He get link to image. Image it's accessible by this link. So it  should be (link instead of image) ? Or am I setting something wrong ?

Server ejabberd with enabled http_upload module, max_size is 500 Mb. Sended image size is 200 Kb.


Discussion / hints:
First rule with libpurple plugins - test it with pidgin(finch). If behavior is the same then it is plugin issue, not spectrum
In pidgin image sending as file. Telegram user see thumbnail image and download button.
OK now I understand, spectrum does not re-upload received image to libpurple as "file", so yes it should be a link, until someone implemented proper file transfers
Tnx, for the to be image display as image in telegram (not a file and not a link) need to implement it in telegram-purple ? or for this also need to implement file transfers in spectrum side ?
If pidgin works correctly with telegram-purple then nothing to do on their sides, only in spectrum
i get this error when sending an image from conversations via telegram-purple. (It used to work before. Not sure since when it stopped working as I don't send a lot of images):
```
Mar  1 16:45:32 host spectrum2[5954]: 5954: 2021-03-01 16:45:32,721 INFO  Component.RAW: RAW DATA IN <iq type='set' to='foo@telegram.example.com/bot' from='bar@example.com/Conversations.IMtG' id='DykBqnBSlvwf'><jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='7APugYBBFX9plFbiyTnNUQ'><content creator='initiator' senders='initiator' name='NJU5ge9dRPHSQR9DPSAMpg'><description xmlns='urn:xmpp:jingle:apps:file-transfer:3'><offer><file><size>218996</size><name>fa0b64e0-1388-4b30-9197-154e85cc38bb.jpg</name></file></offer></description><transport block-size='8192' xmlns='urn:xmpp:jingle:transports:ibb:1' sid='26at+zU5chqyBIQanLXukQ'/></content></jingle></iq>
Mar  1 16:45:32 host spectrum2[5954]: 5954: 2021-03-01 16:45:32,722 INFO  Component.RAW: RAW DATA OUT <iq from="telegram.example.com" id="DykBqnBSlvwf" to="bar@example.com/Conversations.IMtG" type="error"><error type="cancel"><feature-not-implemented xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error></iq>
```
Spectrum never had jingle file-transfers support, and your client should detect this and should not try that

Fix patch:
diff --git a/backends/libpurple/main.cpp b/backends/libpurple/main.cpp
index 86aff002d..8fb4e894c 100644
--- a/backends/libpurple/main.cpp
+++ b/backends/libpurple/main.cpp
@@ -112,14 +112,6 @@ bool caching = true;
 
 static void *notify_user_info(PurpleConnection *gc, const char *who, PurpleNotifyUserInfo *user_info);
 
-static gboolean ft_ui_ready(void *data) {
-	PurpleXfer *xfer = (PurpleXfer *) data;
-	FTData *ftdata = (FTData *) xfer->ui_data;
-	ftdata->timer = 0;
-	purple_xfer_ui_ready_wrapped((PurpleXfer *) data);
-	return FALSE;
-}
-
 /*
 Authorization requests from buddies are cached for the duration of the session.
 To authorize or deny, call the cached callbacks.
@@ -898,44 +890,6 @@ class SpectrumNetworkPlugin : public NetworkPlugin {
 			purple_conversation_destroy_wrapped(conv);
 		}
 
-		void handleFTStartRequest(const std::string &user, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long ftID) {
-			PurpleXfer *xfer = m_unhandledXfers[user + fileName + buddyName];
-			if (xfer) {
-				m_unhandledXfers.erase(user + fileName + buddyName);
-				FTData *ftData = (FTData *) xfer->ui_data;
-
-				ftData->id = ftID;
-				m_xfers[ftID] = xfer;
-				purple_xfer_request_accepted_wrapped(xfer, fileName.c_str());
-				purple_xfer_ui_ready_wrapped(xfer);
-			}
-		}
-
-		void handleFTFinishRequest(const std::string &user, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long ftID) {
-			PurpleXfer *xfer = m_unhandledXfers[user + fileName + buddyName];
-			if (xfer) {
-				m_unhandledXfers.erase(user + fileName + buddyName);
-				purple_xfer_request_denied_wrapped(xfer);
-			}
-		}
-
-		void handleFTPauseRequest(unsigned long ftID) {
-			PurpleXfer *xfer = m_xfers[ftID];
-			if (!xfer)
-				return;
-			FTData *ftData = (FTData *) xfer->ui_data;
-			ftData->paused = true;
-		}
-
-		void handleFTContinueRequest(unsigned long ftID) {
-			PurpleXfer *xfer = m_xfers[ftID];
-			if (!xfer)
-				return;
-			FTData *ftData = (FTData *) xfer->ui_data;
-			ftData->paused = false;
-			purple_xfer_ui_ready_wrapped(xfer);
-		}
-
 		void sendData(const std::string &string) {
 #ifdef WIN32
 			::send(main_socket, string.c_str(), string.size(), 0);
@@ -946,30 +900,12 @@ class SpectrumNetworkPlugin : public NetworkPlugin {
 				writeInput = purple_input_add_wrapped(main_socket, PURPLE_INPUT_WRITE, &transportDataReceived, NULL);
 		}
 
-		void readyForData() {
-			if (m_waitingXfers.empty())
-				return;
-			std::vector<PurpleXfer *> tmp;
-			tmp.swap(m_waitingXfers);
-
-			for (std::vector<PurpleXfer *>::const_iterator it = tmp.begin(); it != tmp.end(); it++) {
-				FTData *ftData = (FTData *) (*it)->ui_data;
-				if (ftData->timer == 0) {
-					ftData->timer = purple_timeout_add_wrapped(1, ft_ui_ready, *it);
-				}
-// 				purple_xfer_ui_ready_wrapped(xfer);
-			}
-		}
-
 		std::map<std::string, PurpleAccount *> m_sessions;
 		std::map<PurpleAccount *, std::string> m_accounts;
 		std::map<std::string, unsigned int> m_vcards;
 		AuthRequestList m_authRequests;
 		std::map<std::string, inputRequest *> m_inputRequests;
 		std::map<std::string, std::list<std::string> > m_rooms;
-		std::map<unsigned long, PurpleXfer *> m_xfers;
-		std::map<std::string, PurpleXfer *> m_unhandledXfers;
-		std::vector<PurpleXfer *> m_waitingXfers;
 		std::string adminLegacyName;
 		std::string adminAlias;
 };
@@ -1487,7 +1423,7 @@ static bool conv_msg_to_image(const char* msg, std::string* xhtml_, std::string*
 
 
 static void conv_write_im(PurpleConversation *conv, const char *who, const char *msg, PurpleMessageFlags flags, time_t mtime) {
-	LOG4CXX_INFO(logger, "conv_write_im()");
+	LOG4CXX_DEBUG(logger, "conv_write_im(): msg='" << msg << "', flags=" << flags);
 	bool isCarbon = false;
 
 	if (purple_conversation_get_type_wrapped(conv) == PURPLE_CONV_TYPE_IM) {
@@ -1513,8 +1449,6 @@ static void conv_write_im(PurpleConversation *conv, const char *who, const char
 	std::string message_; //plain text
 	std::string xhtml_;   //enhanced xhtml, if available
 
-	LOG4CXX_DEBUG(logger, "conv_write_im(): msg='" << msg << "', flags=" << flags);
-
 	if (flags & PURPLE_MESSAGE_IMAGES) {
 		//Store image locally and adjust the message
 		if (!conv_msg_to_image(msg, &xhtml_, &message_))
@@ -1975,146 +1909,92 @@ static PurpleAccountUiOps accountUiOps =
 	NULL
 };
 
-static void XferCreated(PurpleXfer *xfer) {
-	if (!xfer) {
-		return;
-	}
-
- 	PurpleAccount *account = purple_xfer_get_account_wrapped(xfer);
-	if (np->m_accounts.find(account) != np->m_accounts.end()) {
-		const char * filenameData = purple_xfer_get_filename_wrapped(xfer);
-		std::string filename(filenameData ? filenameData : "");
-		np->handleFTStart(np->m_accounts[account], xfer->who ? xfer->who : "", filename, purple_xfer_get_size_wrapped(xfer));
-	}
-}
-
-static void XferDestroyed(PurpleXfer *xfer) {
-	std::remove(np->m_waitingXfers.begin(), np->m_waitingXfers.end(), xfer);
-	FTData *ftdata = (FTData *) xfer->ui_data;
-	if (ftdata && ftdata->timer) {
-		purple_timeout_remove_wrapped(ftdata->timer);
-	}
-	if (ftdata) {
-		np->m_xfers.erase(ftdata->id);
+static gboolean
+ensure_path_exists(const char *dir)
+{
+	if (!g_file_test(dir, G_FILE_TEST_IS_DIR))
+	{
+		if (g_mkdir_with_parents(dir, S_IRUSR | S_IWUSR | S_IXUSR)) {
+			return FALSE;
+		}
 	}
+	return TRUE;
 }
 
-static void xferCanceled(PurpleXfer *xfer) {
+static void newXfer(PurpleXfer *xfer) {
 	PurpleAccount *account = purple_xfer_get_account_wrapped(xfer);
-	std::string filename(xfer ? purple_xfer_get_filename_wrapped(xfer) : "");
+	std::string remote_filename(xfer ? purple_xfer_get_filename_wrapped(xfer) : "");
 	std::string w = xfer->who ? xfer->who : "";
 	size_t pos = w.find("/");
 	if (pos != std::string::npos)
 		w.erase((int) pos, w.length() - (int) pos);
-
-	FTData *ftdata = (FTData *) xfer->ui_data;
-
-	np->handleFTFinish(np->m_accounts[account], w, filename, purple_xfer_get_size_wrapped(xfer), ftdata ? ftdata->id : 0);
-	std::remove(np->m_waitingXfers.begin(), np->m_waitingXfers.end(), xfer);
-	if (ftdata && ftdata->timer) {
-		purple_timeout_remove_wrapped(ftdata->timer);
+	int count = 1;
+	const char *escape;
+	gchar **name_and_ext;
+	const gchar *name;
+	gchar *ext;
+	std::string web_dir = CONFIG_STRING(config, "service.web_directory");
+	std::string web_url = CONFIG_STRING(config, "service.web_url");
+	if (web_dir.empty() || web_url.empty()) {
+		LOG4CXX_DEBUG(logger, "Denied " << remote_filename << " from " << w << ",  web_directory is disabled.");
+		purple_xfer_request_denied_wrapped(xfer);
+		return;
 	}
-	purple_xfer_unref_wrapped(xfer);
-}
-
-static void fileSendStart(PurpleXfer *xfer) {
-// 	FiletransferRepeater *repeater = (FiletransferRepeater *) xfer->ui_data;
-// 	repeater->fileSendStart();
-}
-
-static void fileRecvStart(PurpleXfer *xfer) {
-// 	FiletransferRepeater *repeater = (FiletransferRepeater *) xfer->ui_data;
-// 	repeater->fileRecvStart();
-	FTData *ftData = (FTData *) xfer->ui_data;
-	if (ftData && ftData->timer == 0) {
-		ftData->timer = purple_timeout_add_wrapped(1, ft_ui_ready, xfer);
+	LOG4CXX_INFO(logger, "Accepting " << remote_filename << " from " << w);
+	gchar *dirname = g_build_filename(web_dir.c_str(), NULL);
+	gchar *filename = g_build_filename(dirname, remote_filename.c_str(), NULL);
+	// Uniqifying code taken from Pidgin autoaccept plugin
+	/* Split at the first dot, to avoid uniquifying "foo.tar.gz" to "foo.tar-2.gz" */
+	name_and_ext = g_strsplit(escape, ".", 2);
+	name = name_and_ext[0];
+	if (name == NULL)
+	{
+		g_strfreev(name_and_ext);
+		g_return_if_reached();
 	}
-}
-
-static void newXfer(PurpleXfer *xfer) {
-	PurpleAccount *account = purple_xfer_get_account_wrapped(xfer);
-	std::string filename(xfer ? purple_xfer_get_filename_wrapped(xfer) : "");
-	purple_xfer_ref_wrapped(xfer);
-	std::string w = xfer->who ? xfer->who : "";
-	size_t pos = w.find("/");
-	if (pos != std::string::npos)
-		w.erase((int) pos, w.length() - (int) pos);
-
-	FTData *ftdata = new FTData;
-	ftdata->paused = false;
-	ftdata->id = 0;
-	ftdata->timer = 0;
-	xfer->ui_data = (void *) ftdata;
-
-	np->m_unhandledXfers[np->m_accounts[account] + filename + w] = xfer;
-
-	np->handleFTStart(np->m_accounts[account], w, filename, purple_xfer_get_size_wrapped(xfer));
-}
-
-static void XferReceiveComplete(PurpleXfer *xfer) {
-// 	FiletransferRepeater *repeater = (FiletransferRepeater *) xfer->ui_data;
-// 	repeater->_tryToDeleteMe();
-// 	GlooxMessageHandler::instance()->ftManager->handleXferFileReceiveComplete(xfer);
-	std::remove(np->m_waitingXfers.begin(), np->m_waitingXfers.end(), xfer);
-	FTData *ftdata = (FTData *) xfer->ui_data;
-	if (ftdata && ftdata->timer) {
-		purple_timeout_remove_wrapped(ftdata->timer);
-	}
-	purple_xfer_unref_wrapped(xfer);
-}
-
-static void XferSendComplete(PurpleXfer *xfer) {
-// 	FiletransferRepeater *repeater = (FiletransferRepeater *) xfer->ui_data;
-// 	repeater->_tryToDeleteMe();
-	std::remove(np->m_waitingXfers.begin(), np->m_waitingXfers.end(), xfer);
-	FTData *ftdata = (FTData *) xfer->ui_data;
-	if (ftdata && ftdata->timer) {
-		purple_timeout_remove_wrapped(ftdata->timer);
+	if (name_and_ext[1] != NULL)
+	{
+		/* g_strsplit does not include the separator in each chunk. */
+		ext = g_strdup_printf(".%s", name_and_ext[1]);
 	}
-	purple_xfer_unref_wrapped(xfer);
-}
-
-static gssize XferWrite(PurpleXfer *xfer, const guchar *buffer, gssize size) {
-	FTData *ftData = (FTData *) xfer->ui_data;
-	std::string data((const char *) buffer, (size_t) size);
-// 	std::cout << "xferwrite\n";
-	if (!ftData->paused) {
-// 		std::cout << "adding xfer to waitingXfers queue\n";
-		np->m_waitingXfers.push_back(xfer);
+	else
+	{
+		ext = g_strdup("");
+	}
+	/* Make sure the file doesn't exist. Do we want some better checking than this? */
+	/* FIXME: There is a race here: if the newly uniquified file name gets created between
+	 *        this g_file_test and the transfer starting, the file created in the meantime
+	 *        will be clobbered. But it's not at all straightforward to fix.
+	 */
+	while (g_file_test(filename, G_FILE_TEST_EXISTS))
+	{
+		char *file = g_strdup_printf("%s-%d%s", name, count++, ext);
+		g_free(filename);
+		filename = g_build_filename(dirname, file, NULL);
+		g_free(file);
 	}
-	np->handleFTData(ftData->id, data);
-	return size;
-}
 
-static void XferNotSent(PurpleXfer *xfer, const guchar *buffer, gsize size) {
-// 	FiletransferRepeater *repeater = (FiletransferRepeater *) xfer->ui_data;
-// 	repeater->handleDataNotSent(buffer, size);
+	purple_xfer_request_accepted_wrapped(xfer, filename);
+	g_strfreev(name_and_ext);
+	g_free(ext);
+	g_free(dirname);
+	g_free(filename);
 }
 
-static gssize XferRead(PurpleXfer *xfer, guchar **buffer, gssize size) {
-// 	FiletransferRepeater *repeater = (FiletransferRepeater *) xfer->ui_data;
-// 	int data_size = repeater->getDataToSend(buffer, size);
-// 	if (data_size == 0)
-// 		return 0;
-//
-// 	return data_size;
-	return 0;
+static void XferReceiveComplete(PurpleXfer *xfer) {
+	std::string filename(xfer ? purple_xfer_get_local_filename_wrapped(xfer) : "");
+	std::string w = xfer->who ? xfer->who : "";
+	LOG4CXX_INFO(logger, "Received " << filename << " from " << w);
+	PurpleAccount *account = purple_xfer_get_account_wrapped(xfer);
+	std::string web_url = CONFIG_STRING(config, "service.web_url");
+	pbnetwork::Attachment attachment;
+	gchar *base_filename = g_path_get_basename(filename.c_str());
+	attachment.set_url(web_url + "/" + std::string(base_filename));
+	g_free(base_filename);
+	std::vector<pbnetwork::Attachment> attachments = { attachment };
+	np->handleMessage(np->m_accounts[account], w, "", "", "", xfer->end_time? std::to_string(xfer->end_time) : "", false, false, false, attachments);
 }
 
-static PurpleXferUiOps xferUiOps =
-{
-	XferCreated,
-	XferDestroyed,
-	NULL,
-	NULL,
-	xferCanceled,
-	xferCanceled,
-	XferWrite,
-	XferRead,
-	XferNotSent,
-	NULL
-};
-
 static void RoomlistProgress(PurpleRoomlist *list, gboolean in_progress)
 {
 	if (!in_progress) {
@@ -2219,7 +2099,6 @@ static void transport_core_ui_init(void)
 	purple_accounts_set_ui_ops_wrapped(&accountUiOps);
 	purple_notify_set_ui_ops_wrapped(&notifyUiOps);
 	purple_request_set_ui_ops_wrapped(&requestUiOps);
-	purple_xfers_set_ui_ops_wrapped(&xferUiOps);
 	purple_connections_set_ui_ops_wrapped(&conn_ui_ops);
 	purple_conversations_set_ui_ops_wrapped(&conversation_ui_ops);
 	purple_roomlist_set_ui_ops_wrapped(&roomlist_ui_ops);
@@ -2477,11 +2356,8 @@ static bool initPurple() {
 		purple_signal_connect_wrapped(purple_blist_get_handle_wrapped(), "blist-node-removed", &blist_handle,PURPLE_CALLBACK(NodeRemoved), NULL);
 		purple_signal_connect_wrapped(purple_conversations_get_handle_wrapped(), "chat-topic-changed", &conversation_handle, PURPLE_CALLBACK(conv_chat_topic_changed), NULL);
 		static int xfer_handle;
-		purple_signal_connect_wrapped(purple_xfers_get_handle_wrapped(), "file-send-start", &xfer_handle, PURPLE_CALLBACK(fileSendStart), NULL);
-		purple_signal_connect_wrapped(purple_xfers_get_handle_wrapped(), "file-recv-start", &xfer_handle, PURPLE_CALLBACK(fileRecvStart), NULL);
-		purple_signal_connect_wrapped(purple_xfers_get_handle_wrapped(), "file-recv-request", &xfer_handle, PURPLE_CALLBACK(newXfer), NULL);
-		purple_signal_connect_wrapped(purple_xfers_get_handle_wrapped(), "file-recv-complete", &xfer_handle, PURPLE_CALLBACK(XferReceiveComplete), NULL);
-		purple_signal_connect_wrapped(purple_xfers_get_handle_wrapped(), "file-send-complete", &xfer_handle, PURPLE_CALLBACK(XferSendComplete), NULL);
+		purple_signal_connect_wrapped(purple_xfers_get_handle_wrapped(), "file-recv-request", &xfer_handle, PURPLE_CALLBACK(newXfer), &xfer_handle);
+		purple_signal_connect_wrapped(purple_xfers_get_handle_wrapped(), "file-recv-complete", &xfer_handle, PURPLE_CALLBACK(XferReceiveComplete), &xfer_handle);
 //
 // 		purple_commands_init();
 
@@ -2546,7 +2422,6 @@ static void transportDataReceived(gpointer data, gint source, PurpleInputConditi
 			purple_input_remove_wrapped(writeInput);
 			writeInput = 0;
 		}
-		np->readyForData();
 	}
 }
 
diff --git a/backends/libpurple/purple_defs.cpp b/backends/libpurple/purple_defs.cpp
index bb5830124..6bf7766b9 100644
--- a/backends/libpurple/purple_defs.cpp
+++ b/backends/libpurple/purple_defs.cpp
@@ -98,6 +98,7 @@ purple_xfer_request_accepted_wrapped_fnc purple_xfer_request_accepted_wrapped =
 purple_xfer_request_denied_wrapped_fnc purple_xfer_request_denied_wrapped = NULL;
 purple_xfer_get_account_wrapped_fnc purple_xfer_get_account_wrapped = NULL;
 purple_xfer_get_filename_wrapped_fnc purple_xfer_get_filename_wrapped = NULL;
+purple_xfer_get_local_filename_wrapped_fnc purple_xfer_get_local_filename_wrapped = NULL;
 purple_xfer_get_size_wrapped_fnc purple_xfer_get_size_wrapped = NULL;
 purple_xfer_unref_wrapped_fnc purple_xfer_unref_wrapped = NULL;
 purple_xfer_ref_wrapped_fnc purple_xfer_ref_wrapped = NULL;
@@ -540,6 +541,10 @@ bool resolvePurpleFunctions() {
 	if (!purple_xfer_get_filename_wrapped)
 		return false;
 
+	purple_xfer_get_local_filename_wrapped = (purple_xfer_get_local_filename_wrapped_fnc)GetProcAddress(f_hPurple, "purple_xfer_get_local_filename");
+	if (!purple_xfer_get_local_filename_wrapped)
+		return false;
+
 	purple_xfer_get_size_wrapped = (purple_xfer_get_size_wrapped_fnc)GetProcAddress(f_hPurple, "purple_xfer_get_size");
 	if (!purple_xfer_get_size_wrapped)
 		return false;
diff --git a/backends/libpurple/purple_defs.h b/backends/libpurple/purple_defs.h
index c138a6a93..69d6cbbee 100644
--- a/backends/libpurple/purple_defs.h
+++ b/backends/libpurple/purple_defs.h
@@ -311,6 +311,9 @@ extern purple_xfer_get_account_wrapped_fnc purple_xfer_get_account_wrapped;
 typedef const char * (_cdecl * purple_xfer_get_filename_wrapped_fnc)(const PurpleXfer *xfer);
 extern purple_xfer_get_filename_wrapped_fnc purple_xfer_get_filename_wrapped;
 
+typedef const char * (_cdecl * purple_xfer_get_local_filename_wrapped_fnc)(const PurpleXfer *xfer);
+extern purple_xfer_get_local_filename_wrapped_fnc purple_xfer_get_local_filename_wrapped;
+
 typedef size_t  (_cdecl * purple_xfer_get_size_wrapped_fnc)(const PurpleXfer *xfer);
 extern purple_xfer_get_size_wrapped_fnc purple_xfer_get_size_wrapped;
 
@@ -580,6 +583,7 @@ extern wpurple_g_io_channel_win32_new_socket_wrapped_fnc wpurple_g_io_channel_wi
 #define purple_xfer_request_denied_wrapped purple_xfer_request_denied
 #define purple_xfer_get_account_wrapped purple_xfer_get_account
 #define purple_xfer_get_filename_wrapped purple_xfer_get_filename
+#define purple_xfer_get_local_filename_wrapped purple_xfer_get_local_filename
 #define purple_xfer_get_size_wrapped purple_xfer_get_size
 #define purple_xfer_unref_wrapped purple_xfer_unref
 #define purple_xfer_ref_wrapped purple_xfer_ref
diff --git a/include/Swiften/FileTransfer/CombinedOutgoingFileTransferManager.cpp b/include/Swiften/FileTransfer/CombinedOutgoingFileTransferManager.cpp
deleted file mode 100644
index f344b5871..000000000
--- a/include/Swiften/FileTransfer/CombinedOutgoingFileTransferManager.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (c) 2011 Tobias Markmann
- * Licensed under the simplified BSD license.
- * See Documentation/Licenses/BSD-simplified.txt for more information.
- */
-
-#include "CombinedOutgoingFileTransferManager.h"
-
-#include <boost/foreach.hpp>
-#include <boost/smart_ptr/make_shared.hpp>
-
-#include <Swiften/JID/JID.h>
-#include "Swiften/Disco/EntityCapsProvider.h"
-#include <Swiften/Jingle/JingleSessionManager.h>
-#include <Swiften/Jingle/JingleSessionImpl.h>
-#include <Swiften/Jingle/JingleContentID.h>
-#include <Swiften/FileTransfer/OutgoingJingleFileTransfer.h>
-#include <Swiften/FileTransfer/MyOutgoingSIFileTransfer.h>
-#include <Swiften/FileTransfer/SOCKS5BytestreamServer.h>
-#include <Swiften/Base/IDGenerator.h>
-#include <Swiften/Elements/Presence.h>
-
-
-namespace Swift {
-
-CombinedOutgoingFileTransferManager::CombinedOutgoingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, Transport::PresenceOracle *presOracle, SOCKS5BytestreamServer *bytestreamServer) : jsManager(jingleSessionManager), iqRouter(router), capsProvider(capsProvider), remoteFactory(remoteFactory), localFactory(localFactory), bytestreamRegistry(bytestreamRegistry), bytestreamProxy(bytestreamProxy), presenceOracle(presOracle), bytestreamServer(bytestreamServer) {
-	idGenerator = new IDGenerator();
-}
-
-CombinedOutgoingFileTransferManager::~CombinedOutgoingFileTransferManager() {
-	delete idGenerator;
-}
-
-std::shared_ptr<OutgoingFileTransfer> CombinedOutgoingFileTransferManager::createOutgoingFileTransfer(const JID& from, const JID& receipient, std::shared_ptr<ReadBytestream> readBytestream, const StreamInitiationFileInfo& fileInfo) {
-	// check if receipient support Jingle FT
-	boost::optional<JID> fullJID = highestPriorityJIDSupportingJingle(receipient);
-	if (!fullJID.is_initialized()) {
-		fullJID = highestPriorityJIDSupportingSI(receipient);
-	}
-	else {
-		JingleSessionImpl::ref jingleSession = std::make_shared<JingleSessionImpl>(from, receipient, idGenerator->generateID(), iqRouter);
-
-		//jsManager->getSession(receipient, idGenerator->generateID());
-		assert(jingleSession);
-		jsManager->registerOutgoingSession(from, jingleSession);
-	}
-
-	if (!fullJID.is_initialized()) {
-		return std::shared_ptr<OutgoingFileTransfer>();
-	}
-	
-	// otherwise try SI
-	std::shared_ptr<MyOutgoingSIFileTransfer> jingleFT =  std::shared_ptr<MyOutgoingSIFileTransfer>(new MyOutgoingSIFileTransfer(idGenerator->generateID(), from, fullJID.get(), fileInfo.getName(), fileInfo.getSize(), fileInfo.getDescription(), readBytestream, iqRouter, bytestreamServer, bytestreamRegistry));
-	// else fail
-	
-	return jingleFT;
-}
-
-boost::optional<JID> CombinedOutgoingFileTransferManager::highestPriorityJIDSupportingJingle(const JID& bareJID) {
-	JID fullReceipientJID;
-	int priority = INT_MIN;
-	
-	//getAllPresence(bareJID) gives you all presences for the bare JID (i.e. all resources) Remko Tronçon @ 11:11
-	std::vector<Presence::ref> presences = presenceOracle->getAllPresence(bareJID);
-
-	//iterate over them
-	BOOST_FOREACH(Presence::ref pres, presences) {
-		if (pres->getPriority() > priority) {
-			// look up caps from the jid
-			DiscoInfo::ref info = capsProvider->getCaps(pres->getFrom());
-			if (info && info->hasFeature(DiscoInfo::JingleFeature) && info->hasFeature(DiscoInfo::JingleFTFeature) &&
-				info->hasFeature(DiscoInfo::JingleTransportsIBBFeature)) {
-			
-				priority = pres->getPriority();
-				fullReceipientJID = pres->getFrom();
-			}
-		}
-	}
-	
-	return fullReceipientJID.isValid() ? boost::optional<JID>(fullReceipientJID) : boost::optional<JID>();
-}
-
-boost::optional<JID> CombinedOutgoingFileTransferManager::highestPriorityJIDSupportingSI(const JID& bareJID) {
-	JID fullReceipientJID;
-	int priority = INT_MIN;
-	
-	//getAllPresence(bareJID) gives you all presences for the bare JID (i.e. all resources) Remko Tronçon @ 11:11
-	std::vector<Presence::ref> presences = presenceOracle->getAllPresence(bareJID);
-
-	//iterate over them
-	BOOST_FOREACH(Presence::ref pres, presences) {
-		if (pres->getPriority() > priority) {
-			// look up caps from the jid
-			DiscoInfo::ref info = capsProvider->getCaps(pres->getFrom());
-			if (info && info->hasFeature("http://jabber.org/protocol/si/profile/file-transfer")) {
-			
-				priority = pres->getPriority();
-				fullReceipientJID = pres->getFrom();
-			}
-		}
-	}
-	
-	return fullReceipientJID.isValid() ? boost::optional<JID>(fullReceipientJID) : boost::optional<JID>();
-}
-
-}
diff --git a/include/Swiften/FileTransfer/CombinedOutgoingFileTransferManager.h b/include/Swiften/FileTransfer/CombinedOutgoingFileTransferManager.h
deleted file mode 100644
index ae00753de..000000000
--- a/include/Swiften/FileTransfer/CombinedOutgoingFileTransferManager.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (c) 2011 Tobias Markmann
- * Licensed under the simplified BSD license.
- * See Documentation/Licenses/BSD-simplified.txt for more information.
- */
-
-#pragma once
-
-#include <boost/shared_ptr.hpp>
-#include <boost/optional.hpp>
-
-#include <Swiften/JID/JID.h>
-
-#include "transport/PresenceOracle.h"
-
-#include <Swiften/FileTransfer/OutgoingFileTransfer.h>
-
-namespace Swift {
-
-class JingleSessionManager;
-class IQRouter;
-class EntityCapsProvider;
-class RemoteJingleTransportCandidateSelectorFactory;
-class LocalJingleTransportCandidateGeneratorFactory;
-class OutgoingFileTransfer;
-class JID;
-class IDGenerator;
-class ReadBytestream;
-class StreamInitiationFileInfo;
-class SOCKS5BytestreamRegistry;
-class SOCKS5BytestreamProxy;
-class SOCKS5BytestreamServer;
-class PresenceOracle;
-
-class CombinedOutgoingFileTransferManager {
-public:
-	CombinedOutgoingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, Transport::PresenceOracle* presOracle, SOCKS5BytestreamServer *server);
-	~CombinedOutgoingFileTransferManager();
-	
-	std::shared_ptr<OutgoingFileTransfer> createOutgoingFileTransfer(const JID& from, const JID& to, std::shared_ptr<ReadBytestream>, const StreamInitiationFileInfo&);
-
-private:
-	boost::optional<JID> highestPriorityJIDSupportingJingle(const JID& bareJID);
-	boost::optional<JID> highestPriorityJIDSupportingSI(const JID& bareJID);
-	JingleSessionManager* jsManager;
-	IQRouter* iqRouter;
-	EntityCapsProvider* capsProvider;
-	RemoteJingleTransportCandidateSelectorFactory* remoteFactory;
-	LocalJingleTransportCandidateGeneratorFactory* localFactory;
-	IDGenerator *idGenerator;
-	SOCKS5BytestreamRegistry* bytestreamRegistry;
-	SOCKS5BytestreamProxy* bytestreamProxy;
-	Transport::PresenceOracle* presenceOracle;
-	SOCKS5BytestreamServer *bytestreamServer;
-};
-
-}
diff --git a/include/Swiften/FileTransfer/MyOutgoingSIFileTransfer.cpp b/include/Swiften/FileTransfer/MyOutgoingSIFileTransfer.cpp
deleted file mode 100644
index eebc8fd3b..000000000
--- a/include/Swiften/FileTransfer/MyOutgoingSIFileTransfer.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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/MyOutgoingSIFileTransfer.h>
-
-#include <boost/bind.hpp>
-
-#include <Swiften/FileTransfer/StreamInitiationRequest.h>
-#include <Swiften/FileTransfer/BytestreamsRequest.h>
-#include <Swiften/FileTransfer/SOCKS5BytestreamServer.h>
-#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
-#include <Swiften/FileTransfer/IBBSendSession.h>
-
-namespace Swift {
-
-MyOutgoingSIFileTransfer::MyOutgoingSIFileTransfer(const std::string& id, const JID& from, const JID& to, const std::string& name, int size, const std::string& description, std::shared_ptr<ReadBytestream> bytestream, IQRouter* iqRouter, SOCKS5BytestreamServer* socksServer, SOCKS5BytestreamRegistry* registry) : id(id), from(from), to(to), name(name), size(size), description(description), bytestream(bytestream), iqRouter(iqRouter), socksServer(socksServer), registry(registry) {
-}
-
-void MyOutgoingSIFileTransfer::start() {
-	StreamInitiation::ref streamInitiation(new StreamInitiation());
-	streamInitiation->setID(id);
-	streamInitiation->setFileInfo(StreamInitiationFileInfo(name, description, size));
-	streamInitiation->addProvidedMethod("http://jabber.org/protocol/bytestreams");
-	streamInitiation->addProvidedMethod("http://jabber.org/protocol/ibb");
-	StreamInitiationRequest::ref request = StreamInitiationRequest::create(from, to, streamInitiation, iqRouter);
-	request->onResponse.connect(boost::bind(&MyOutgoingSIFileTransfer::handleStreamInitiationRequestResponse, this, _1, _2));
-	request->send();
-}
-
-void MyOutgoingSIFileTransfer::stop() {
-}
-
-void MyOutgoingSIFileTransfer::cancel() {
-	// TODO
-// 	session->sendTerminate(JinglePayload::Reason::Cancel);
-
-	if (ibbSession) {
-		ibbSession->stop();
-	}
-}
-
-void MyOutgoingSIFileTransfer::handleStreamInitiationRequestResponse(StreamInitiation::ref response, ErrorPayload::ref error) {
-	if (error) {
-		finish(FileTransferError());
-	}
-	else {
-		if (response->getRequestedMethod() == "http://jabber.org/protocol/bytestreams") {
-			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(from, to, bytestreams, iqRouter);
-			request->onResponse.connect(boost::bind(&MyOutgoingSIFileTransfer::handleBytestreamsRequestResponse, this, _1, _2));
-			request->send();
-		}
-		else if (response->getRequestedMethod() == "http://jabber.org/protocol/ibb") {
-			ibbSession = std::shared_ptr<IBBSendSession>(new IBBSendSession(id, from, to, bytestream, iqRouter));
-			ibbSession->onFinished.connect(boost::bind(&MyOutgoingSIFileTransfer::handleIBBSessionFinished, this, _1));
-			ibbSession->start();
-		}
-	}
-}
-
-void MyOutgoingSIFileTransfer::handleBytestreamsRequestResponse(Bytestreams::ref, ErrorPayload::ref error) {
-	if (error) {
-		finish(FileTransferError());
-		return;
-	}
-	//socksServer->onTransferFinished.connect();
-}
-
-void MyOutgoingSIFileTransfer::finish(boost::optional<FileTransferError> error) {
-	if (ibbSession) {
-		ibbSession->onFinished.disconnect(boost::bind(&MyOutgoingSIFileTransfer::handleIBBSessionFinished, this, _1));
-		ibbSession.reset();
-	}
-	onFinished(error);
-}
-
-void MyOutgoingSIFileTransfer::handleIBBSessionFinished(boost::optional<FileTransferError> error) {
-	finish(error);
-}
-
-}
diff --git a/include/Swiften/FileTransfer/MyOutgoingSIFileTransfer.h b/include/Swiften/FileTransfer/MyOutgoingSIFileTransfer.h
deleted file mode 100644
index b04ec2944..000000000
--- a/include/Swiften/FileTransfer/MyOutgoingSIFileTransfer.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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/signals2.hpp>
-
-#include <Swiften/FileTransfer/OutgoingFileTransfer.h>
-#include <Swiften/FileTransfer/ReadBytestream.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>
-#include <Swiften/Version.h>
-
-namespace Swift {
-	class IQRouter;
-	class SOCKS5BytestreamServer;
-	class SOCKS5BytestreamRegistry;
-
-	class MyOutgoingSIFileTransfer : public OutgoingFileTransfer {
-		public:
-			MyOutgoingSIFileTransfer(const std::string& id, const JID& from, const JID& to, const std::string& name, int size, const std::string& description, std::shared_ptr<ReadBytestream> bytestream, IQRouter* iqRouter, SOCKS5BytestreamServer* socksServer, SOCKS5BytestreamRegistry* registry);
-
-			virtual void start();
-			virtual void stop();
-			virtual void cancel();
-
-			boost::signals2::signal<void (const boost::optional<FileTransferError>&)> onFinished;
-
-		private:
-			void handleStreamInitiationRequestResponse(StreamInitiation::ref, ErrorPayload::ref);
-			void handleBytestreamsRequestResponse(Bytestreams::ref, ErrorPayload::ref);
-			void finish(boost::optional<FileTransferError> error);
-			void handleIBBSessionFinished(boost::optional<FileTransferError> error);
-
-		private:
-			std::string id;
-			JID from;
-			JID to;
-			std::string name;
-			int size;
-			std::string description;
-			std::shared_ptr<ReadBytestream> bytestream;
-			IQRouter* iqRouter;
-			SOCKS5BytestreamServer* socksServer;
-			std::shared_ptr<IBBSendSession> ibbSession;
-			SOCKS5BytestreamRegistry *registry;
-	};
-}
diff --git a/include/transport/FileTransferManager.h b/include/transport/FileTransferManager.h
deleted file mode 100644
index bee5b9904..000000000
--- a/include/transport/FileTransferManager.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/**
- * libtransport -- C++ library for easy XMPP Transports development
- *
- * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-
-#pragma once
-
-#include <Swiften/Elements/StreamInitiationFileInfo.h>
-#include <Swiften/FileTransfer/CombinedOutgoingFileTransferManager.h>
-#include <Swiften/FileTransfer/IncomingFileTransferManager.h>
-#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
-#include <Swiften/FileTransfer/SOCKS5BytestreamServer.h>
-
-
-#include <Swiften/FileTransfer/SOCKS5BytestreamProxiesManager.h>
-#include <Swiften/FileTransfer/SOCKS5BytestreamServerManager.h>
-
-namespace Transport {
-
-class UserManager;
-class User;
-class Component;
-class Buddy;
-
-class FileTransferManager {
-	public:
-		typedef struct Transfer {
-			std::shared_ptr<Swift::OutgoingFileTransfer> ft;
-			Swift::JID from;
-			Swift::JID to;
-			std::shared_ptr<Swift::ReadBytestream> readByteStream;
-		} Transfer;
-
-		FileTransferManager(Component *component, UserManager *userManager);
-		virtual ~FileTransferManager();
-		
-		FileTransferManager::Transfer sendFile(User *user, Buddy *buddy, std::shared_ptr<Swift::ReadBytestream> byteStream, const Swift::StreamInitiationFileInfo &info);
-
-	private:
-		Component *m_component;
-		UserManager *m_userManager;
-		Swift::CombinedOutgoingFileTransferManager* m_outgoingFTManager;
-		Swift::RemoteJingleTransportCandidateSelectorFactory* m_remoteCandidateSelectorFactory;
-		Swift::LocalJingleTransportCandidateGeneratorFactory* m_localCandidateGeneratorFactory;
-		Swift::JingleSessionManager *m_jingleSessionManager;
-		Swift::SOCKS5BytestreamRegistry* m_bytestreamRegistry;
-		Swift::SOCKS5BytestreamServerManager* m_proxyServerManager;
-		Swift::SOCKS5BytestreamProxiesManager *m_proxyManager;
-};
-
-}
diff --git a/include/transport/NetworkPlugin.h b/include/transport/NetworkPlugin.h
index f8fd37700..8be3ad13e 100644
--- a/include/transport/NetworkPlugin.h
+++ b/include/transport/NetworkPlugin.h
@@ -136,7 +136,9 @@ class NetworkPlugin {
 		/// \param nickname Nickname of buddy in room. Empty if it's normal chat message.
 		/// \param xhtml XHTML message.
 		/// \param carbon If set, the message is a carbon copy of our own message, sent in a different legacy network client. The message should be treated as sent FROM us, not TO us.
-		void handleMessage(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &nickname = "", const std::string &xhtml = "", const std::string &timestamp = "", bool headline = false, bool pm = false, bool carbon = false);
+		void handleMessage(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &nickname = "",
+						   const std::string &xhtml = "", const std::string &timestamp = "", bool headline = false, bool pm = false, bool carbon = false,
+						   const std::vector<pbnetwork::Attachment> &attachments = {});
 
 		void handleMessageAck(const std::string &user, const std::string &legacyName, const std::string &id);
 
@@ -188,11 +190,6 @@ class NetworkPlugin {
 		/// \param message Message.
 		void handleAttention(const std::string &user, const std::string &buddyName, const std::string &message);
 
-		void handleFTStart(const std::string &user, const std::string &buddyName, const std::string fileName, unsigned long size);
-		void handleFTFinish(const std::string &user, const std::string &buddyName, const std::string fileName, unsigned long size, unsigned long ftid);
-
-		void handleFTData(unsigned long ftID, const std::string &data);
-
 		void handleRoomList(const std::string &user, const std::list<std::string> &rooms, const std::list<std::string> &names);
 
 		/// Called when XMPP user wants to connect legacy network.
@@ -263,11 +260,6 @@ class NetworkPlugin {
 		virtual void handleStoppedTypingRequest(const std::string &/*user*/, const std::string &/*buddyName*/) {}
 		virtual void handleAttentionRequest(const std::string &/*user*/, const std::string &/*buddyName*/, const std::string &/*message*/) {}
 
-		virtual void handleFTStartRequest(const std::string &/*user*/, const std::string &/*buddyName*/, const std::string &/*fileName*/, unsigned long size, unsigned long ftID) {}
-		virtual void handleFTFinishRequest(const std::string &/*user*/, const std::string &/*buddyName*/, const std::string &/*fileName*/, unsigned long size, unsigned long ftID) {}
-		virtual void handleFTPauseRequest(unsigned long ftID) {}
-		virtual void handleFTContinueRequest(unsigned long ftID) {}
-
 		virtual void handleRawXML(const std::string &xml) {}
 
 		virtual void handleMemoryUsage(double &res, double &shared) {res = 0; shared = 0;}
@@ -290,10 +282,6 @@ class NetworkPlugin {
 		void handleBuddyRemovedPayload(const std::string &payload);
 		void handleChatStatePayload(const std::string &payload, int type);
 		void handleAttentionPayload(const std::string &payload);
-		void handleFTStartPayload(const std::string &payload);
-		void handleFTFinishPayload(const std::string &payload);
-		void handleFTPausePayload(const std::string &payload);
-		void handleFTContinuePayload(const std::string &payload);
 		void handleRoomSubjectChangedPayload(const std::string &payload);
 
 		void send(const std::string &data);
diff --git a/include/transport/NetworkPluginServer.h b/include/transport/NetworkPluginServer.h
index 233e54522..51d0f4d8d 100644
--- a/include/transport/NetworkPluginServer.h
+++ b/include/transport/NetworkPluginServer.h
@@ -20,11 +20,8 @@
 
 #pragma once
 
-#include "transport/FileTransferManager.h"
-
 #include <time.h>
 #include <vector>
-#include "Swiften/Presence/PresenceOracle.h"
 #include "Swiften/Disco/EntityCapsManager.h"
 #include "Swiften/Network/BoostConnectionServer.h"
 #include "Swiften/Network/Connection.h"
@@ -40,7 +37,6 @@
 #include "Swiften/Parser/XMPPParser.h"
 #include "Swiften/Parser/XMPPParserClient.h"
 #include "Swiften/Serializer/XMPPSerializer.h"
-#include <Swiften/FileTransfer/FileTransfer.h>
 #include "transport/protocol.pb.h"
 
 #define NETWORK_PLUGIN_API_VERSION (1)
@@ -59,8 +55,6 @@ class RosterResponder;
 class BlockResponder;
 class DummyReadBytestream;
 class AdminInterface;
-class FileTransferManager;
-class FileTransfer;
 
 class NetworkPluginServer : Swift::XMPPParserClient {
 	public:
@@ -78,7 +72,7 @@ class NetworkPluginServer : Swift::XMPPParserClient {
 			std::string id;
 		};
 
-		NetworkPluginServer(Component *component, Config *config, UserManager *userManager, FileTransferManager *ftManager);
+		NetworkPluginServer(Component *component, Config *config, UserManager *userManager);
 
 		virtual ~NetworkPluginServer();
 
@@ -125,9 +119,6 @@ class NetworkPluginServer : Swift::XMPPParserClient {
 		void handleAuthorizationPayload(const std::string &payload);
 		void handleAttentionPayload(const std::string &payload);
 		void handleStatsPayload(Backend *c, const std::string &payload);
-		void handleFTStartPayload(const std::string &payload);
-		void handleFTFinishPayload(const std::string &payload);
-		void handleFTDataPayload(Backend *b, const std::string &payload);
 		void handleQueryPayload(Backend *b, const std::string &payload);
 		void handleBackendConfigPayload(const std::string &payload);
 		void handleRoomListPayload(const std::string &payload);
@@ -151,11 +142,6 @@ class NetworkPluginServer : Swift::XMPPParserClient {
 		void handleVCardUpdated(User *user, std::shared_ptr<Swift::VCard> vcard);
 		void handleVCardRequired(User *user, const std::string &name, unsigned int id);
 
-		void handleFTStateChanged(Swift::FileTransfer::State state, const std::string &userName, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long id);
-		void handleFTAccepted(User *user, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long ftID);
-		void handleFTRejected(User *user, const std::string &buddyName, const std::string &fileName, unsigned long size);
-		void handleFTDataNeeded(Backend *b, unsigned long ftid);
-
 		void handlePIDTerminated(unsigned long pid);
 
 		std::vector<std::shared_ptr<Swift::Message> > wrapIncomingMedia(std::shared_ptr<Swift::Message>& msg);
@@ -175,6 +161,7 @@ class NetworkPluginServer : Swift::XMPPParserClient {
 		void handleStreamStart(const Swift::ProtocolHeader&) {}
 		void handleElement(std::shared_ptr<Swift::ToplevelElement> element);
 		void handleStreamEnd() {}
+		void addOobPayload(Swift::Message::ref message, const std::string &url);
 
 		UserManager *m_userManager;
 		VCardResponder *m_vcardResponder;
@@ -190,8 +177,6 @@ class NetworkPluginServer : Swift::XMPPParserClient {
 		Component *m_component;
 		std::list<User *> m_waitingUsers;
 		bool m_isNextLongRun;
-		std::map<unsigned long, FileTransferManager::Transfer> m_filetransfers;
-		FileTransferManager *m_ftManager;
 		std::vector<std::string> m_crashedBackends;
 		AdminInterface *m_adminInterface;
 		bool m_startingBackend;
diff --git a/include/transport/User.h b/include/transport/User.h
index e5f6d7ce7..f688a36d5 100644
--- a/include/transport/User.h
+++ b/include/transport/User.h
@@ -25,7 +25,6 @@
 #include <boost/signals2.hpp>
 
 #include "transport/StorageBackend.h"
-#include <Swiften/FileTransfer/OutgoingFileTransfer.h>
 #include "Swiften/Elements/SpectrumErrorPayload.h"
 #include "Swiften/JID/JID.h"
 #include "Swiften/Elements/Presence.h"
@@ -175,7 +174,6 @@ class User {
 		std::shared_ptr<Swift::Connection> connection;
 		time_t m_lastActivity;
 		std::map<Swift::JID, Swift::DiscoInfo::ref> m_legacyCaps;
-		std::vector<std::shared_ptr<Swift::OutgoingFileTransfer> > m_filetransfers;
 		int m_resources;
 		int m_reconnectCounter;
 		std::list<Swift::Presence::ref> m_joinedRooms;
diff --git a/include/transport/protocol.proto b/include/transport/protocol.proto
index 9710eece5..749c7e7a8 100644
--- a/include/transport/protocol.proto
+++ b/include/transport/protocol.proto
@@ -81,6 +81,7 @@ message ConversationMessage {
 	optional string id = 8;
 	optional bool pm = 9;
 	optional bool carbon = 10;
+	repeated Attachment attachment = 11;
 }
 
 message Room {
@@ -141,17 +142,10 @@ message Stats {
 	required string id = 4;
 }
 
-message File {
-	required string userName = 1;
-	required string buddyName = 2;
-	required string fileName = 3;
-	required int32 size = 4;
-	optional int32 ftID = 5;
-}
-
-message FileTransferData {
-	required int32 ftID = 1;
-	required bytes data = 2;
+message Attachment {
+	required string url = 1;
+	optional string mimeType = 2;
+	optional string description = 3;
 }
 
 message BackendConfig {
diff --git a/libtransport/FileTransferManager.cpp b/libtransport/FileTransferManager.cpp
deleted file mode 100644
index 8792fcac8..000000000
--- a/libtransport/FileTransferManager.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
-/**
- * libtransport -- C++ library for easy XMPP Transports development
- *
- * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-
-#include "transport/FileTransferManager.h"
-#include "transport/Transport.h"
-#include "transport/UserManager.h"
-#include "transport/User.h"
-#include "transport/Buddy.h"
-#include "transport/Logging.h"
-#include "Swiften/Network/ConnectionServerFactory.h"
-
-namespace Transport {
-
-DEFINE_LOGGER(logger, "FileTransferManager");
-
-FileTransferManager::FileTransferManager(Component *component, UserManager *userManager) {
-// 	m_component = component;
-// 	m_userManager = userManager;
-// 
-// 	m_jingleSessionManager = new Swift::JingleSessionManager(m_component->getIQRouter());
-// #if !HAVE_SWIFTEN_3
-// 	m_connectivityManager = new Swift::ConnectivityManager(m_component->getNetworkFactories()->getNATTraverser());
-// #endif
-// 	m_bytestreamRegistry = new Swift::SOCKS5BytestreamRegistry();
-// #if !HAVE_SWIFTEN_3
-// 	m_bytestreamProxy = new Swift::SOCKS5BytestreamProxy(m_component->getNetworkFactories()->getConnectionFactory(), m_component->getNetworkFactories()->getTimerFactory());
-// 	m_localCandidateGeneratorFactory = new Swift::DefaultLocalJingleTransportCandidateGeneratorFactory(m_connectivityManager, m_bytestreamRegistry, m_bytestreamProxy, "thishouldnotbeused");
-// 	m_remoteCandidateSelectorFactory = new Swift::DefaultRemoteJingleTransportCandidateSelectorFactory(m_component->getNetworkFactories()->getConnectionFactory(), m_component->getNetworkFactories()->getTimerFactory());
-// #else
-// 	m_proxyManager = new Swift::SOCKS5BytestreamProxiesManager(m_component->getNetworkFactories()->getConnectionFactory(), m_component->getNetworkFactories()->getTimerFactory(), m_component->getNetworkFactories()->getDomainNameResolver(), m_component->getIQRouter(), "bar.com");
-// #endif
-// 	std::shared_ptr<Swift::ConnectionServer> server = m_component->getNetworkFactories()->getConnectionServerFactory()->createConnectionServer(19645);
-// 	server->start();
-// #if HAVE_SWIFTEN_3
-// 	m_proxyServerManager = new Swift::SOCKS5BytestreamServerManager(m_bytestreamRegistry, m_component->getNetworkFactories()->getConnectionServerFactory(), m_component->getNetworkFactories()->getNetworkEnvironment(), m_component->getNetworkFactories()->getNATTraverser());
-// #else
-// 	m_bytestreamServer = new Swift::SOCKS5BytestreamServer(server, m_bytestreamRegistry);
-// 	m_bytestreamServer->start();
-// 	m_outgoingFTManager = new Swift::CombinedOutgoingFileTransferManager(m_jingleSessionManager, m_component->getIQRouter(),
-// 		m_userManager, m_remoteCandidateSelectorFactory,
-// 		m_localCandidateGeneratorFactory, m_bytestreamRegistry,
-// 		m_bytestreamProxy, m_component->getPresenceOracle(),
-// 		m_bytestreamServer);
-// #endif
-
-	
-
-// WARNING: Swiften crashes when this is uncommented... But we probably need it for working Jingle FT
-// 	m_connectivityManager->addListeningPort(19645);
-}
-
-FileTransferManager::~FileTransferManager() {
-// #if !HAVE_SWIFTEN_3
-// 	m_bytestreamServer->stop();
-// 	delete m_remoteCandidateSelectorFactory;
-// 	delete m_localCandidateGeneratorFactory;
-// #endif
-// 	delete m_outgoingFTManager;
-// 	delete m_jingleSessionManager;
-// 	delete m_bytestreamRegistry;
-// #if !HAVE_SWIFTEN_3
-// 	delete m_bytestreamServer;
-// 	delete m_bytestreamProxy;
-// 	delete m_connectivityManager;
-// #endif
-}
-
-FileTransferManager::Transfer FileTransferManager::sendFile(User *user, Buddy *buddy, std::shared_ptr<Swift::ReadBytestream> byteStream, const Swift::StreamInitiationFileInfo &info) {
- 	FileTransferManager::Transfer transfer;
-// 	transfer.from = buddy->getJID();
-// 	transfer.to = user->getJID();
-// 	transfer.readByteStream = byteStream;
-// 
-// 	LOG4CXX_INFO(logger, "Starting FT from '" << transfer.from << "' to '" << transfer.to << "'")
-// 
-// 	transfer.ft = m_outgoingFTManager->createOutgoingFileTransfer(transfer.from, transfer.to, transfer.readByteStream, info);
-// // 	if (transfer.ft) {
-// // 		m_filetransfers.push_back(ft);
-// // 		ft->onStateChange.connect(boost::bind(&User::handleFTStateChanged, this, _1, Buddy::JIDToLegacyName(from), info.getName(), info.getSize(), id));
-// // 		transfer.ft->start();
-// // 	}
- 	return transfer;
-}
-
-}
diff --git a/libtransport/MemoryReadByteStream.cpp b/libtransport/MemoryReadByteStream.cpp
deleted file mode 100644
index b343c0131..000000000
--- a/libtransport/MemoryReadByteStream.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-/**
- * libtransport -- C++ library for easy XMPP Transports development
- *
- * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-
-#include "transport/MemoryReadBytestream.h"
-#include <boost/foreach.hpp>
-
-namespace Transport {
-	
-MemoryReadBytestream::MemoryReadBytestream(unsigned long size) {
-	neededData = false;
-	m_finished = false;
-	m_sent = 0;
-	m_size = size;
-}
-
-MemoryReadBytestream::~MemoryReadBytestream() {
-	
-}
-
-unsigned long MemoryReadBytestream::appendData(const std::string &data) {
-	m_data += data;
-	onDataAvailable();
-	neededData = false;
-	return m_data.size();
-}
-
-std::shared_ptr<std::vector<unsigned char> > MemoryReadBytestream::read(size_t size) {
-	if (m_data.empty()) {
-		onDataNeeded();
-		return std::shared_ptr<std::vector<unsigned char> >(new std::vector<unsigned char>());
-	}
-
-	if (m_data.size() < size) {
-		std::shared_ptr<std::vector<unsigned char> > ptr(new std::vector<unsigned char>(m_data.begin(), m_data.end()));
-		m_sent += m_data.size();
-		m_data.clear();
-		if (m_sent == m_size)
-			m_finished = true;
-		onDataNeeded();
-		return ptr;
-	}
-	std::shared_ptr<std::vector<unsigned char> > ptr(new std::vector<unsigned char>(m_data.begin(), m_data.begin() + size));
-	m_data.erase(m_data.begin(), m_data.begin() + size);
-	m_sent += size;
-	if (m_sent == m_size)
-		m_finished = true;
-	if (m_data.size() < 500000 && !neededData) {
-		neededData = true;
-		onDataNeeded();
-	}
-	return ptr;
-}
-
-bool MemoryReadBytestream::isFinished() const {
-// 	std::cout << "finished? " << m_finished << "\n";
-	return m_finished;
-}
-
-}
diff --git a/libtransport/NetworkPluginServer.cpp b/libtransport/NetworkPluginServer.cpp
index c50a88293..e19416bf4 100644
--- a/libtransport/NetworkPluginServer.cpp
+++ b/libtransport/NetworkPluginServer.cpp
@@ -19,6 +19,7 @@
  */
 
 #include "transport/NetworkPluginServer.h"
+#include "transport/PresenceOracle.h"
 #include "transport/User.h"
 #include "transport/Transport.h"
 #include "transport/RosterManager.h"
@@ -27,7 +28,6 @@
 #include "transport/LocalBuddy.h"
 #include "transport/Config.h"
 #include "transport/Conversation.h"
-#include "transport/MemoryReadBytestream.h"
 #include "transport/Logging.h"
 #include "transport/AdminInterface.h"
 #include "transport/Frontend.h"
@@ -54,9 +54,6 @@
 
 #include "transport/utf8.h"
 
-#include <Swiften/FileTransfer/ReadBytestream.h>
-#include <Swiften/Elements/StreamInitiationFileInfo.h>
-
 #ifdef _WIN32
 #include "windows.h"
 #include <stdint.h>
@@ -259,9 +256,8 @@ static void handleBuddyPayload(LocalBuddy *buddy, const pbnetwork::Buddy &payloa
 	buddy->setBlocked(payload.blocked());
 }
 
-NetworkPluginServer::NetworkPluginServer(Component *component, Config *config, UserManager *userManager, FileTransferManager *ftManager) {
+NetworkPluginServer::NetworkPluginServer(Component *component, Config *config, UserManager *userManager) {
 	_server = this;
-	m_ftManager = ftManager;
 	m_userManager = userManager;
 	m_config = config;
 	m_component = component;
@@ -721,6 +717,10 @@ void NetworkPluginServer::handleConvMessagePayload(const std::string &data, bool
 		msg->addPayload(std::make_shared<Swift::XHTMLIMPayload>(payload.xhtml()));
 	}
 
+	for (const pbnetwork::Attachment &att : payload.attachment()) {
+		addOobPayload(msg, att.url());
+	}
+
 	// Split the message if configured, or just preprocess
 	LOG4CXX_TRACE(logger, "handleConvMessagePayload: wrapping media");
 	typedef std::vector<std::shared_ptr<Swift::Message> > MsgList;
@@ -817,109 +817,6 @@ void NetworkPluginServer::handleStatsPayload(Backend *c, const std::string &data
 	c->id = payload.id();
 }
 
-void NetworkPluginServer::handleFTStartPayload(const std::string &data) {
-	pbnetwork::File payload;
-	if (payload.ParseFromString(data) == false) {
-		// TODO: ERROR
-		return;
-	}
-
-	User *user = m_userManager->getUser(payload.username());
-	if (!user)
-		return;
-
-	LOG4CXX_INFO(logger, "handleFTStartPayload " << payload.filename() << " " << payload.buddyname());
-	
-	LocalBuddy *buddy = (LocalBuddy *) user->getRosterManager()->getBuddy(payload.buddyname());
-	if (!buddy) {
-		// TODO: escape? reject?
-		return;
-	}
-
-	Swift::StreamInitiationFileInfo fileInfo;
-	fileInfo.setSize(payload.size());
-	fileInfo.setName(payload.filename());
-
-	Backend *c = (Backend *) user->getData();
-	std::shared_ptr<MemoryReadBytestream> bytestream(new MemoryReadBytestream(payload.size()));
-	bytestream->onDataNeeded.connect(boost::bind(&NetworkPluginServer::handleFTDataNeeded, this, c, bytestream_id + 1));
-
-	LOG4CXX_INFO(logger, "jid=" << buddy->getJID());
-
-	FileTransferManager::Transfer transfer = m_ftManager->sendFile(user, buddy, bytestream, fileInfo);
-	if (!transfer.ft) {
-		handleFTRejected(user, payload.buddyname(), payload.filename(), payload.size());
-		return;
-	}
-
-	m_filetransfers[++bytestream_id] = transfer;
-}
-
-void NetworkPluginServer::handleFTFinishPayload(const std::string &data) {
-	pbnetwork::File payload;
-	if (payload.ParseFromString(data) == false) {
-		// TODO: ERROR
-		return;
-	}
-
-	if (payload.has_ftid()) {
-		if (m_filetransfers.find(payload.ftid()) != m_filetransfers.end()) {
-			FileTransferManager::Transfer &transfer = m_filetransfers[payload.ftid()];
-			transfer.ft->cancel();
-		}
-		else {
-			LOG4CXX_ERROR(logger, "FTFinishPayload for unknown ftid=" << payload.ftid());
-		}
-	}
-
-}
-
-void NetworkPluginServer::handleFTDataPayload(Backend *b, const std::string &data) {
-	pbnetwork::FileTransferData payload;
-	if (payload.ParseFromString(data) == false) {
-		// TODO: ERROR
-		return;
-	}
-
-// 	User *user = m_userManager->getUser(payload.username());
-// 	if (!user)
-// 		return;
-
-	if (m_filetransfers.find(payload.ftid()) == m_filetransfers.end()) {
-		LOG4CXX_ERROR(logger, "Uknown filetransfer with id " << payload.ftid());
-		return;
-	}
-
-	FileTransferManager::Transfer &transfer = m_filetransfers[payload.ftid()];
-	MemoryReadBytestream *bytestream = (MemoryReadBytestream *) transfer.readByteStream.get();
-
-	if (bytestream->appendData(payload.data()) > 5000000) {
-		pbnetwork::FileTransferData f;
-		f.set_ftid(payload.ftid());
-		f.set_data("");
-
-		std::string message;
-		f.SerializeToString(&message);
-
-		WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_FT_PAUSE);
-
-		send(b->connection, message);
-	}
-}
-
-void NetworkPluginServer::handleFTDataNeeded(Backend *b, unsigned long ftid) {
-	pbnetwork::FileTransferData f;
-	f.set_ftid(ftid);
-	f.set_data("");
-
-	std::string message;
-	f.SerializeToString(&message);
-
-	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_FT_CONTINUE);
-
-	send(b->connection, message);
-}
-
 void NetworkPluginServer::connectWaitingUsers() {
 	// some users are in queue waiting for this backend
 	while (!m_waitingUsers.empty()) {
@@ -1265,15 +1162,6 @@ void NetworkPluginServer::handleDataRead(Backend *c, std::shared_ptr<Swift::Safe
 			case pbnetwork::WrapperMessage_Type_TYPE_STATS:
 				handleStatsPayload(c, wrapper.payload());
 				break;
-			case pbnetwork::WrapperMessage_Type_TYPE_FT_START:
-				handleFTStartPayload(wrapper.payload());
-				break;
-			case pbnetwork::WrapperMessage_Type_TYPE_FT_FINISH:
-				handleFTFinishPayload(wrapper.payload());
-				break;
-			case pbnetwork::WrapperMessage_Type_TYPE_FT_DATA:
-				handleFTDataPayload(c, wrapper.payload());
-				break;
 			case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_REMOVED:
 				handleBuddyRemovedPayload(wrapper.payload());
 				break;
@@ -1843,6 +1731,15 @@ enum OobMode {
 	OobSplit	= 3,	//3. Split into multiple text-only/media-only messages.
 };
 
+void NetworkPluginServer::addOobPayload(Swift::Message::ref message, const std::string &url) {
+	//Add OOB tag
+	std::shared_ptr<Swift::RawXMLPayload>
+		oob_payload(new Swift::RawXMLPayload(
+			"<x xmlns='jabber:x:oob'><url>" + url + "</url>" + "</x>"));
+	// todo: add the payload itself as a caption
+	message->addPayload(oob_payload);
+}
+
 std::vector<std::shared_ptr<Swift::Message> >
 NetworkPluginServer::wrapIncomingMedia(std::shared_ptr<Swift::Message>& msg) {
     std::vector<std::shared_ptr<Swift::Message> > result;
@@ -1923,16 +1820,7 @@ NetworkPluginServer::wrapIncomingMedia(std::shared_ptr<Swift::Message>& msg) {
             this_msg = msg;
         }
 
-        //Add OOB tag
-        std::shared_ptr<Swift::RawXMLPayload>
-            oob_payload(new Swift::RawXMLPayload(
-                "<x xmlns='jabber:x:oob'><url>"
-                + image_url
-                + "</url>"
-                + "</x>"
-            ));
-            // todo: add the payload itself as a caption
-        this_msg->addPayload(oob_payload);
+        addOobPayload(this_msg, image_url);
 
         //In single-OOB mode there's no point to process further media
         if (oobMode == OobExclusive)
@@ -2095,54 +1983,6 @@ void NetworkPluginServer::handleVCardRequired(User *user, const std::string &nam
 	send(c->connection, message);
 }
 
-void NetworkPluginServer::handleFTAccepted(User *user, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long ftID) {
-	pbnetwork::File f;
-	f.set_username(user->getJID().toBare());
-	f.set_buddyname(buddyName);
-	f.set_filename(fileName);
-	f.set_size(size);
-	f.set_ftid(ftID);
-
-	std::string message;
-	f.SerializeToString(&message);
-
-	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_FT_START);
-
-	Backend *c = (Backend *) user->getData();
-	if (!c) {
-		return;
-	}
-	send(c->connection, message);
-}
-
-void NetworkPluginServer::handleFTRejected(User *user, const std::string &buddyName, const std::string &fileName, unsigned long size) {
-	pbnetwork::File f;
-	f.set_username(user->getJID().toBare());
-	f.set_buddyname(buddyName);
-	f.set_filename(fileName);
-	f.set_size(size);
-	f.set_ftid(0);
-
-	std::string message;
-	f.SerializeToString(&message);
-
-	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_FT_FINISH);
-
-	Backend *c = (Backend *) user->getData();
-	if (!c) {
-		return;
-	}
-	send(c->connection, message);
-}
-
-void NetworkPluginServer::handleFTStateChanged(Swift::FileTransfer::State state, const std::string &userName, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long id) {
-	User *user = m_userManager->getUser(userName);
-	if (!user) {
-		// TODO: FIXME We have to remove filetransfer when use disconnects
-		return;
-	}
-}
-
 void NetworkPluginServer::sendPing(Backend *c) {
 
 	std::string message;
diff --git a/plugin/cpp/networkplugin.cpp b/plugin/cpp/networkplugin.cpp
index 8e375f8f6..a7f0a1d78 100644
--- a/plugin/cpp/networkplugin.cpp
+++ b/plugin/cpp/networkplugin.cpp
@@ -84,7 +84,7 @@ void NetworkPlugin::sendConfig(const PluginConfig &cfg) {
 	m.SerializeToString(&message);
 
 	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_BACKEND_CONFIG);
-
+	LOG4CXX_INFO(logger, "Sending data: " << message);
 	send(message);
 }
 
@@ -94,7 +94,10 @@ void NetworkPlugin::sendRawXML(std::string &xml) {
 	send(xml);
 }
 
-void NetworkPlugin::handleMessage(const std::string &user, const std::string &legacyName, const std::string &msg, const std::string &nickname, const std::string &xhtml, const std::string &timestamp, bool headline, bool pm, bool carbon) {
+void NetworkPlugin::handleMessage(const std::string &user, const std::string &legacyName, const std::string &msg,
+								  const std::string &nickname, const std::string &xhtml, const std::string &timestamp,
+								  bool headline, bool pm, bool carbon, const std::vector<pbnetwork::Attachment> &attachments)
+{
 	pbnetwork::ConversationMessage m;
 	m.set_username(user);
 	m.set_buddyname(legacyName);
@@ -106,6 +109,11 @@ void NetworkPlugin::handleMessage(const std::string &user, const std::string &le
 	m.set_pm(pm);
 	m.set_carbon(carbon);
 
+	for (const pbnetwork::Attachment &att : attachments) {
+		auto attach = m.add_attachment();
+		attach->CopyFrom(att);
+	}
+
 	std::string message;
 	m.SerializeToString(&message);
 
@@ -322,52 +330,6 @@ void NetworkPlugin::handleRoomNicknameChanged(const std::string &user, const std
 	send(message);
 }
 
-void NetworkPlugin::handleFTStart(const std::string &user, const std::string &buddyName, const std::string fileName, unsigned long size) {
-	pbnetwork::File room;
-	room.set_username(user);
-	room.set_buddyname(buddyName);
-	room.set_filename(fileName);
-	room.set_size(size);
-
-	std::string message;
-	room.SerializeToString(&message);
-
-	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_FT_START);
- 
-	send(message);
-}
-
-void NetworkPlugin::handleFTFinish(const std::string &user, const std::string &buddyName, const std::string fileName, unsigned long size, unsigned long ftid) {
-	pbnetwork::File room;
-	room.set_username(user);
-	room.set_buddyname(buddyName);
-	room.set_filename(fileName);
-	room.set_size(size);
-	if (ftid) {
-		room.set_ftid(ftid);
-	}
-
-	std::string message;
-	room.SerializeToString(&message);
-
-	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_FT_FINISH);
- 
-	send(message);
-}
-
-void NetworkPlugin::handleFTData(unsigned long ftID, const std::string &data) {
-	pbnetwork::FileTransferData d;
-	d.set_ftid(ftID);
-	d.set_data(data);
-
-	std::string message;
-	d.SerializeToString(&message);
-
-	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_FT_DATA);
- 
-	send(message);
-}
-
 void NetworkPlugin::handleRoomList(const std::string &user, const std::list<std::string> &rooms, const std::list<std::string> &names) {
 	pbnetwork::RoomList d;
 	for (std::list<std::string>::const_iterator it = rooms.begin(); it != rooms.end(); it++) {
@@ -446,46 +408,6 @@ void NetworkPlugin::handleAttentionPayload(const std::string &data) {
 	handleAttentionRequest(payload.username(), payload.buddyname(), payload.message());
 }
 
-void NetworkPlugin::handleFTStartPayload(const std::string &data) {
-	pbnetwork::File payload;
-	if (payload.ParseFromString(data) == false) {
-		// TODO: ERROR
-		return;
-	}
-
-	handleFTStartRequest(payload.username(), payload.buddyname(), payload.filename(), payload.size(), payload.ftid());
-}
-
-void NetworkPlugin::handleFTFinishPayload(const std::string &data) {
-	pbnetwork::File payload;
-	if (payload.ParseFromString(data) == false) {
-		// TODO: ERROR
-		return;
-	}
-
-	handleFTFinishRequest(payload.username(), payload.buddyname(), payload.filename(), payload.size(), payload.ftid());
-}
-
-void NetworkPlugin::handleFTPausePayload(const std::string &data) {
-	pbnetwork::FileTransferData payload;
-	if (payload.ParseFromString(data) == false) {
-		// TODO: ERROR
-		return;
-	}
-
-	handleFTPauseRequest(payload.ftid());
-}
-
-void NetworkPlugin::handleFTContinuePayload(const std::string &data) {
-	pbnetwork::FileTransferData payload;
-	if (payload.ParseFromString(data) == false) {
-		// TODO: ERROR
-		return;
-	}
-
-	handleFTContinueRequest(payload.ftid());
-}
-
 void NetworkPlugin::handleJoinRoomPayload(const std::string &data) {
 	pbnetwork::Room payload;
 	if (payload.ParseFromString(data) == false) {
@@ -645,18 +567,6 @@ void NetworkPlugin::handleDataRead(std::string &data) {
 			case pbnetwork::WrapperMessage_Type_TYPE_ATTENTION:
 				handleAttentionPayload(wrapper.payload());
 				break;
-			case pbnetwork::WrapperMessage_Type_TYPE_FT_START:
-				handleFTStartPayload(wrapper.payload());
-				break;
-			case pbnetwork::WrapperMessage_Type_TYPE_FT_FINISH:
-				handleFTFinishPayload(wrapper.payload());
-				break;
-			case pbnetwork::WrapperMessage_Type_TYPE_FT_PAUSE:
-				handleFTPausePayload(wrapper.payload());
-				break;
-			case pbnetwork::WrapperMessage_Type_TYPE_FT_CONTINUE:
-				handleFTContinuePayload(wrapper.payload());
-				break;
 			case pbnetwork::WrapperMessage_Type_TYPE_EXIT:
 				handleExitRequest();
 				break;
diff --git a/plugin/python/NetworkPlugin.py b/plugin/python/NetworkPlugin.py
index 85daef8f3..cd13fcdbb 100644
--- a/plugin/python/NetworkPlugin.py
+++ b/plugin/python/NetworkPlugin.py
@@ -183,40 +183,6 @@ def handleRoomList(self, rooms):
 		message = WRAP(roomList.SerializeToString(), protocol_pb2.WrapperMessage.TYPE_ROOM_LIST)
 		self.send(message);
 
-
-	def handleFTStart(self, user, buddyName, fileName, size):
-		room = protocol_pb2.File()
-		room.userName = user
-		room.buddyName = buddyName
-		room.fileName = fileName
-		room.size = size
-
-		message = WRAP(room.SerializeToString(), protocol_pb2.WrapperMessage.TYPE_FT_START)
-		self.send(message);
-
-	def handleFTFinish(self, user, buddyName, fileName, size, ftid):
-		room = protocol_pb2.File()
-		room.userName = user
-		room.buddyName = buddyName
-		room.fileName = fileName
-		room.size = size
-
-		# Check later
-		if ftid != 0:
-			room.ftID = ftid 
-			
-		message = WRAP(room.SerializeToString(), protocol_pb2.WrapperMessage.TYPE_FT_FINISH)
-		self.send(message)
-
-
-	def handleFTData(self, ftID, data):
-		d = protocol_pb2.FileTransferData()
-		d.ftid = ftID
-		d.data = data
-
-		message = WRAP(d.SerializeToString(), protocol_pb2.WrapperMessage.TYPE_FT_DATA);
-		self.send(message)
-
 	def handleBackendConfig(self, section, key, value):
 		c = protocol_pb2.BackendConfig()
 		c.config = "[%s]\n%s = %s\n" % (section, key, value)
@@ -267,34 +233,6 @@ def handleAttentionPayload(self, data):
 			#TODO: ERROR
 			return
 		self.handleAttentionRequest(payload.userName, payload.buddyName, payload.message)
-	
-	def handleFTStartPayload(self, data):
-		payload = protocol_pb2.File()
-		if (payload.ParseFromString(data) == False):
-			#TODO: ERROR
-			return
-		self.handleFTStartRequest(payload.userName, payload.buddyName, payload.fileName, payload.size, payload.ftID);
-
-	def handleFTFinishPayload(self, data):
-		payload = protocol_pb2.File()
-		if (payload.ParseFromString(data) == False):
-			#TODO: ERROR
-			return
-		self.handleFTFinishRequest(payload.userName, payload.buddyName, payload.fileName, payload.size, payload.ftID)
-
-	def handleFTPausePayload(self, data):
-		payload = protocol_pb2.FileTransferData()
-		if (payload.ParseFromString(data) == False):
-			#TODO: ERROR
-			return
-		self.handleFTPauseRequest(payload.ftID)
-
-	def handleFTContinuePayload(self, data):
-		payload = protocol_pb2.FileTransferData()
-		if (payload.ParseFromString(data) == False):
-			#TODO: ERROR
-			return
-		self.handleFTContinueRequest(payload.ftID)
 
 	def handleJoinRoomPayload(self, data):
 		payload = protocol_pb2.Room()
@@ -406,14 +344,6 @@ def handleDataRead(self, data):
 					self.handleChatStatePayload(wrapper.payload, protocol_pb2.WrapperMessage.TYPE_BUDDY_STOPPED_TYPING)
 			elif wrapper.type == protocol_pb2.WrapperMessage.TYPE_ATTENTION:
 					self.handleAttentionPayload(wrapper.payload)
-			elif wrapper.type == protocol_pb2.WrapperMessage.TYPE_FT_START:
-					self.handleFTStartPayload(wrapper.payload)
-			elif wrapper.type == protocol_pb2.WrapperMessage.TYPE_FT_FINISH:
-					self.handleFTFinishPayload(wrapper.payload)
-			elif wrapper.type == protocol_pb2.WrapperMessage.TYPE_FT_PAUSE:
-					self.handleFTPausePayload(wrapper.payload)
-			elif wrapper.type == protocol_pb2.WrapperMessage.TYPE_FT_CONTINUE:
-					self.handleFTContinuePayload(wrapper.payload)
 			elif wrapper.type == protocol_pb2.WrapperMessage.TYPE_EXIT:
 				self.handleExitRequest()
 			elif wrapper.type == protocol_pb2.WrapperMessage.TYPE_CONV_MESSAGE_ACK:
@@ -575,18 +505,6 @@ def handleStoppedTypingRequest(self, user, buddyName):
 		
 	def handleAttentionRequest(self, user, buddyName, message):
 		pass
-
-	def handleFTStartRequest(self, user, buddyName, fileName, size, ftID):
-		pass
-		
-	def handleFTFinishRequest(self, user, buddyName, fileName, size, ftID):
-		pass
-		
-	def handleFTPauseRequest(self, ftID):
-		pass
-
-	def handleFTContinueRequest(self, ftID):
-		pass
 	
 
 	def handleMemoryUsage(self):
diff --git a/spectrum/src/frontends/xmpp/XMPPUser.h b/spectrum/src/frontends/xmpp/XMPPUser.h
index 1e83f1ffd..c7a2f3c11 100644
--- a/spectrum/src/frontends/xmpp/XMPPUser.h
+++ b/spectrum/src/frontends/xmpp/XMPPUser.h
@@ -25,7 +25,6 @@
 #include <time.h>
 #include "Swiften/Disco/EntityCapsManager.h"
 #include "Swiften/Disco/EntityCapsProvider.h"
-#include <Swiften/FileTransfer/OutgoingFileTransfer.h>
 #include "Swiften/Elements/SpectrumErrorPayload.h"
 #include "Swiften/Network/Timer.h"
 #include "Swiften/Network/Connection.h"
diff --git a/spectrum/src/main.cpp b/spectrum/src/main.cpp
index 2702a9230..ba2e98c2e 100644
--- a/spectrum/src/main.cpp
+++ b/spectrum/src/main.cpp
@@ -1,6 +1,5 @@
 #include "transport/Config.h"
 #include "transport/Transport.h"
-#include "transport/FileTransferManager.h"
 #include "transport/UserManager.h"
 #include "transport/SQLite3Backend.h"
 #include "transport/MySQLBackend.h"
@@ -272,9 +271,7 @@ int mainloop() {
 		LOG4CXX_WARN(logger, "Registrations won't work, you have specified [database] type=none in config file.");
 	}
 
-	FileTransferManager ftManager(&transport, userManager);
-
-	NetworkPluginServer plugin(&transport, config_, userManager, &ftManager);
+	NetworkPluginServer plugin(&transport, config_, userManager);
 	plugin.start();
 
 	AdminInterface adminInterface(&transport, userManager, &plugin, storageBackend, userRegistration);


Test patch:
diff --git a/include/transport/MemoryReadBytestream.h b/include/transport/MemoryReadBytestream.h
deleted file mode 100644
index 762ebbbde..000000000
--- a/include/transport/MemoryReadBytestream.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/**
- * libtransport -- C++ library for easy XMPP Transports development
- *
- * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-
-#pragma once
-
-#include <string>
-#include <map>
-
-#include "Swiften/FileTransfer/ReadBytestream.h"
-
-namespace Transport {
-
-class MemoryReadBytestream : public Swift::ReadBytestream {
-	public:
-		MemoryReadBytestream(unsigned long size);
-		virtual ~MemoryReadBytestream();
-
-		unsigned long appendData(const std::string &data);
-
-		virtual std::shared_ptr<std::vector<unsigned char> > read(size_t size);
-
-		void setFinished() { m_finished = true; }
-		bool isFinished() const;
-
-		boost::signals2::signal<void ()> onDataNeeded;
-
-	private:
-		bool m_finished;
-		std::string m_data;
-		bool neededData;
-		unsigned long m_sent;
-		unsigned long m_size;
-};
-
-}
diff --git a/tests/libtransport/AdminInterface.cpp b/tests/libtransport/AdminInterface.cpp
index 17cfc176e..e3244ed2c 100644
--- a/tests/libtransport/AdminInterface.cpp
+++ b/tests/libtransport/AdminInterface.cpp
@@ -32,7 +32,7 @@ class AdminInterfaceTest : public CPPUNIT_NS :: TestFixture, public BasicSlackTe
 
 		void setUp (void) {
 			setMeUp();
-			serv = new NetworkPluginServer(component, cfg, userManager, NULL);
+			serv = new NetworkPluginServer(component, cfg, userManager);
 			admin = new AdminInterface(component, userManager, serv, storage, NULL);
 			component->setAdminInterface(admin);
 		}
diff --git a/tests/libtransport/networkpluginserver.cpp b/tests/libtransport/networkpluginserver.cpp
index 3055ba41f..3e280272c 100644
--- a/tests/libtransport/networkpluginserver.cpp
+++ b/tests/libtransport/networkpluginserver.cpp
@@ -71,7 +71,7 @@ class NetworkPluginServerTest : public CPPUNIT_NS :: TestFixture, public BasicTe
 		void setUp (void) {
 			setMeUp();
 
-			serv = new NetworkPluginServer(component, cfg, userManager, NULL);
+			serv = new NetworkPluginServer(component, cfg, userManager);
 			connectUser();
 			User *user = userManager->getUser("user@localhost");
 			user->setData(&backend);