From fd17fe0d239f97cedebe4ceffa234155bd299b68 Mon Sep 17 00:00:00 2001
From: Thilo Cestonaro <thilo@cestona.ro>
Date: Thu, 29 Sep 2011 00:03:14 +0200
Subject: BOSH implementation started

License: This patch is BSD-licensed, see http://www.opensource.org/licenses/bsd-license.php

diff --git a/Swiften/Client/CoreClient.cpp b/Swiften/Client/CoreClient.cpp
index a8a5c8f..08f31a0 100644
--- a/Swiften/Client/CoreClient.cpp
+++ b/Swiften/Client/CoreClient.cpp
@@ -24,6 +24,7 @@
 #include <Swiften/Client/ClientSessionStanzaChannel.h>
 #include <Swiften/Network/SOCKS5ProxiedConnectionFactory.h>
 #include <Swiften/Network/HTTPConnectProxiedConnectionFactory.h>
+#include <Swiften/Network/BOSHConnectionFactory.h>
 
 namespace Swift {
 
@@ -69,7 +70,8 @@ void CoreClient::connect(const std::string& host) {
 		proxyConnectionFactories.push_back(new HTTPConnectProxiedConnectionFactory(networkFactories->getConnectionFactory(), networkFactories->getProxyProvider()->getHTTPConnectProxy()));
 	}
 	std::vector<ConnectionFactory*> connectionFactories(proxyConnectionFactories);
-	connectionFactories.push_back(networkFactories->getConnectionFactory());
+	// connectionFactories.push_back(networkFactories->getConnectionFactory());
+	connectionFactories.push_back(new BOSHConnectionFactory(networkFactories->getConnectionFactory()));
 
 	connector_ = boost::make_shared<ChainedConnector>(host, networkFactories->getDomainNameResolver(), connectionFactories, networkFactories->getTimerFactory());
 	connector_->onConnectFinished.connect(boost::bind(&CoreClient::handleConnectorFinished, this, _1));
diff --git a/Swiften/Network/BOSHConnection.cpp b/Swiften/Network/BOSHConnection.cpp
new file mode 100644
index 0000000..549c652
--- /dev/null
+++ b/Swiften/Network/BOSHConnection.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2011 Thilo Cestonaro
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "BOSHConnection.h"
+#include <boost/bind.hpp>
+#include <boost/thread.hpp>
+#include <string>
+
+#include <Swiften/Network/ConnectionFactory.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Base/String.h>
+#include <Swiften/Base/ByteArray.h>
+#include <Swiften/Network/HostAddressPort.h>
+#include <Swiften/Parser/BOSHParser.h>
+
+namespace Swift {
+
+	BOSHConnection::BOSHConnection(ConnectionFactory* connectionFactory)
+	: connectionFactory_(connectionFactory), server_(HostAddressPort(HostAddress("0.0.0.0"), 0)), sid_()
+	{
+		reopenAfterAction = true;
+	}
+
+	BOSHConnection::~BOSHConnection() {
+		if (newConnection_) {
+			newConnection_->onDataRead.disconnect(boost::bind(&BOSHConnection::handleDataRead, shared_from_this(), _1));
+			newConnection_->onDisconnected.disconnect(boost::bind(&BOSHConnection::handleDisconnected, shared_from_this(), _1));
+		}
+		if (currentConnection_) {
+			currentConnection_->onDataRead.disconnect(boost::bind(&BOSHConnection::handleDataRead, shared_from_this(), _1));
+			currentConnection_->onDisconnected.disconnect(boost::bind(&BOSHConnection::handleDisconnected, shared_from_this(), _1));
+		}
+	}
+
+	void BOSHConnection::connect(const HostAddressPort& server) {
+		server_ = server;
+		newConnection_ = connectionFactory_->createConnection();
+		newConnection_->onConnectFinished.connect(boost::bind(&BOSHConnection::handleConnectionConnectFinished, shared_from_this(), _1));
+		newConnection_->onDataRead.connect(boost::bind(&BOSHConnection::handleDataRead, shared_from_this(), _1));
+		newConnection_->onDisconnected.connect(boost::bind(&BOSHConnection::handleDisconnected, shared_from_this(), _1));
+		SWIFT_LOG(debug) << "connect to server " << server.getAddress().toString() << ":" << server.getPort() << std::endl;
+		newConnection_->connect(HostAddressPort(HostAddress("85.10.192.88"), 5280));
+	}
+
+	void BOSHConnection::listen() {
+		assert(false);
+	}
+
+	void BOSHConnection::disconnect() {
+		if(newConnection_)
+			newConnection_->disconnect();
+
+		if(currentConnection_)
+			currentConnection_->disconnect();
+	}
+
+	void BOSHConnection::write(const SafeByteArray& data) {
+		SWIFT_LOG(debug) << "write data: " << safeByteArrayToString(data) << std::endl;
+	}
+
+	void BOSHConnection::handleConnectionConnectFinished(bool error) {
+		newConnection_->onConnectFinished.disconnect(boost::bind(&BOSHConnection::handleConnectionConnectFinished, shared_from_this(), _1));
+		if(error) {
+			onConnectFinished(true);
+			return;
+		}
+
+		if(sid_.size() == 0) {
+			// Session Creation Request
+			std::stringstream content;
+			std::stringstream header;
+
+			content << "<body content='text/xml; charset=utf-8'"
+					<< " from='ephraim@0x10.de'"
+					<< " hold='1'"
+					<< " to='0x10.de'"
+					<< " ver='1.6'"
+					<< " wait='60'"
+					<< " ack='1'"
+					<< " xml:lang='en'"
+					<< " xmlns='http://jabber.org/protocol/httpbind' />\r\n";
+
+			header	<< "POST /http-bind HTTP/1.1\r\n"
+					<< "Host: 0x10.de:5280\r\n"
+					<< "Accept-Encoding: deflate\r\n"
+					<< "Content-Type: text/xml; charset=utf-8\r\n"
+					<< "Content-Length: " << content.str().size() << "\r\n\r\n"
+					<< content.str();
+
+			SWIFT_LOG(debug) << "request: ";
+			newConnection_->write(createSafeByteArray(header.str()));
+		}
+	}
+
+	void BOSHConnection::handleDataRead(const SafeByteArray& data) {
+		std::string response = safeByteArrayToString(data);
+		assert(response.find("\r\n\r\n") != std::string::npos);
+
+		SWIFT_LOG(debug) << "response: " << response.substr(response.find("\r\n\r\n") + 4) << std::endl;
+
+		BOSHParser parser;
+		if(parser.parse(response.substr(response.find("\r\n\r\n") + 4))) {
+			sid_ = parser.getAttribute("sid");
+			onConnectFinished(false);
+			int bodyStartElementLength = 0;
+			bool inQuote = false;
+			for(size_t i= 0; i < response.size(); i++) {
+				if(response.c_str()[i] == '\'' || response.c_str()[i] == '"') {
+					inQuote = !inQuote;
+				}
+				else if(!inQuote && response.c_str()[i] == '>') {
+					bodyStartElementLength = i + 1;
+					break;
+				}
+			}
+			SafeByteArray payload = createSafeByteArray(response.substr(bodyStartElementLength, response.size() - bodyStartElementLength - 7));
+			SWIFT_LOG(debug) << "payload: " << safeByteArrayToString(payload) << std::endl;
+			onDataRead(payload);
+		}
+	}
+
+	void BOSHConnection::handleDisconnected(const boost::optional<Error>& error) {
+		onDisconnected(error);
+	}
+
+	HostAddressPort BOSHConnection::getLocalAddress() const {
+		return newConnection_->getLocalAddress();
+	}
+}
diff --git a/Swiften/Network/BOSHConnection.h b/Swiften/Network/BOSHConnection.h
new file mode 100644
index 0000000..0da92ba
--- /dev/null
+++ b/Swiften/Network/BOSHConnection.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2011 Thilo Cestonaro
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/enable_shared_from_this.hpp>
+
+#include <Swiften/Network/Connection.h>
+#include <Swiften/Network/HostAddressPort.h>
+#include <Swiften/Base/String.h>
+
+namespace boost {
+	class thread;
+	namespace system {
+		class error_code;
+	}
+}
+
+namespace Swift {
+	class ConnectionFactory;
+
+	class BOSHConnection : public Connection, public boost::enable_shared_from_this<BOSHConnection> {
+		public:
+			typedef boost::shared_ptr<BOSHConnection> ref;
+			static ref create(ConnectionFactory* connectionFactory) {
+				return ref(new BOSHConnection(connectionFactory));
+			}
+			virtual ~BOSHConnection();
+			virtual void listen();
+			virtual void connect(const HostAddressPort& address);
+			virtual void disconnect();
+			virtual void write(const SafeByteArray& data);
+			virtual HostAddressPort getLocalAddress() const;
+
+		private:
+			BOSHConnection(ConnectionFactory* connectionFactory);
+
+			void handleConnectionConnectFinished(bool error);
+			void handleDataRead(const SafeByteArray& data);
+			void handleDisconnected(const boost::optional<Error>& error);
+
+			bool reopenAfterAction;
+			ConnectionFactory* connectionFactory_;
+			HostAddressPort server_;
+			boost::shared_ptr<Connection> newConnection_;
+			boost::shared_ptr<Connection> currentConnection_;
+			std::string sid_;
+	};
+}
diff --git a/Swiften/Network/BOSHConnectionFactory.cpp b/Swiften/Network/BOSHConnectionFactory.cpp
new file mode 100644
index 0000000..4c49cae
--- /dev/null
+++ b/Swiften/Network/BOSHConnectionFactory.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2011 Thilo Cestonaro
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "BOSHConnectionFactory.h"
+#include <Swiften/Network/BOSHConnection.h>
+
+namespace Swift {
+
+BOSHConnectionFactory::BOSHConnectionFactory(ConnectionFactory* connectionFactory) {
+	connectionFactory_ = connectionFactory;
+
+}
+
+boost::shared_ptr<Connection> BOSHConnectionFactory::createConnection() {
+	return BOSHConnection::create(connectionFactory_);
+}
+
+}
diff --git a/Swiften/Network/BOSHConnectionFactory.h b/Swiften/Network/BOSHConnectionFactory.h
new file mode 100644
index 0000000..7431cf4
--- /dev/null
+++ b/Swiften/Network/BOSHConnectionFactory.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2011 Thilo Cestonaro
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Network/ConnectionFactory.h>
+#include <Swiften/Network/HostAddressPort.h>
+
+namespace Swift {
+	class BOSHConnectionFactory : public ConnectionFactory {
+		public:
+		BOSHConnectionFactory(ConnectionFactory* connectionFactory);
+
+			virtual boost::shared_ptr<Connection> createConnection();
+
+		private:
+			ConnectionFactory* connectionFactory_;
+	};
+}
diff --git a/Swiften/Network/SConscript b/Swiften/Network/SConscript
index 49df18f..399cec8 100644
--- a/Swiften/Network/SConscript
+++ b/Swiften/Network/SConscript
@@ -15,6 +15,8 @@ sourceList = [
 			"BoostConnectionServer.cpp",
 			"BoostConnectionServerFactory.cpp",
 			"BoostIOServiceThread.cpp",
+			"BOSHConnection.cpp",
+			"BOSHConnectionFactory.cpp"
 			"ConnectionFactory.cpp",
 			"ConnectionServer.cpp",
 			"ConnectionServerFactory.cpp",
diff --git a/Swiften/Parser/BOSHParser.cpp b/Swiften/Parser/BOSHParser.cpp
new file mode 100644
index 0000000..9fb218a
--- /dev/null
+++ b/Swiften/Parser/BOSHParser.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2011 Thilo Cestonaro
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <cassert>
+
+#include <Swiften/Parser/BOSHParser.h>
+#include <Swiften/Parser/XMLParser.h>
+#include <Swiften/Parser/PlatformXMLParserFactory.h>
+
+namespace Swift {
+
+BOSHParser::BOSHParser() :
+	xmlParser_(0),
+	level_(-1),
+	parseErrorOccurred_(false)
+{
+	xmlParser_ = PlatformXMLParserFactory().createXMLParser(this);
+}
+
+BOSHParser::~BOSHParser() {
+	delete xmlParser_;
+}
+
+bool BOSHParser::parse(const std::string& data) {
+	bool xmlParseResult = xmlParser_->parse(data);
+	return xmlParseResult && !parseErrorOccurred_;
+}
+
+void BOSHParser::handleStartElement(const std::string& /*element*/, const std::string& /*ns*/, const AttributeMap& attributes) {
+	if (!parseErrorOccurred_) {
+		if (level_ == BoshTopLevel) {
+			boshBodyAttributes_ = attributes;
+		}
+	}
+	++level_;
+}
+
+void BOSHParser::handleEndElement(const std::string& /*element*/, const std::string& /*ns*/) {
+	assert(level_ > BoshTopLevel);
+	--level_;
+	if (!parseErrorOccurred_) {
+
+	}
+}
+
+void BOSHParser::handleCharacterData(const std::string& /*data*/) {
+	if (!parseErrorOccurred_) {
+
+	}
+}
+
+}
diff --git a/Swiften/Parser/BOSHParser.h b/Swiften/Parser/BOSHParser.h
new file mode 100644
index 0000000..69b3d13
--- /dev/null
+++ b/Swiften/Parser/BOSHParser.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2011 Thilo Cestonaro
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/noncopyable.hpp>
+
+#include <Swiften/Parser/XMLParserClient.h>
+#include <Swiften/Parser/AttributeMap.h>
+
+namespace Swift {
+	class XMLParser;
+
+	class BOSHParser : public XMLParserClient, boost::noncopyable {
+		public:
+			BOSHParser();
+			~BOSHParser();
+
+			bool parse(const std::string&);
+
+			std::string getAttribute(const std::string& attribute, const std::string& ns = "") const {
+				return boshBodyAttributes_.getAttribute(attribute, ns);
+			}
+		private:
+			virtual void handleStartElement(
+					const std::string& element,
+					const std::string& ns,
+					const AttributeMap& attributes);
+			virtual void handleEndElement(const std::string& element, const std::string& ns);
+			virtual void handleCharacterData(const std::string& data);
+
+		private:
+			AttributeMap boshBodyAttributes_;
+			XMLParser* xmlParser_;
+			enum Level {
+				BoshTopLevel = -1,
+				TopLevel = 0,
+				StreamLevel = 1,
+				ElementLevel = 2
+			};
+			int level_;
+			bool parseErrorOccurred_;
+	};
+}
diff --git a/Swiften/Parser/SConscript b/Swiften/Parser/SConscript
index e4c2778..dd19238 100644
--- a/Swiften/Parser/SConscript
+++ b/Swiften/Parser/SConscript
@@ -11,6 +11,7 @@ sources = [
 		"AuthChallengeParser.cpp",
 		"AuthSuccessParser.cpp",
 		"AuthResponseParser.cpp",
+		"BOSHParser.cpp",
 		"CompressParser.cpp",
 		"ElementParser.cpp",
 		"IQParser.cpp",
-- 
cgit v0.10.2-6-g49f6