From a328466bc492c50c443e406b9325542a75182327 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Fri, 17 Jul 2009 21:15:54 +0200
Subject: Implemented clean session/connection shutdown.


diff --git a/Limber/main.cpp b/Limber/main.cpp
index c478924..2a2197d 100644
--- a/Limber/main.cpp
+++ b/Limber/main.cpp
@@ -31,18 +31,18 @@ class Server {
 
 	private:
 		void handleNewConnection(boost::shared_ptr<Connection> c) {
-			ServerFromClientSession* session = new ServerFromClientSession(idGenerator_.generateID(), c, &payloadParserFactories_, &payloadSerializers_, userRegistry_);
+			boost::shared_ptr<ServerFromClientSession> session(new ServerFromClientSession(idGenerator_.generateID(), c, &payloadParserFactories_, &payloadSerializers_, userRegistry_));
 			serverFromClientSessions_.push_back(session);
 			session->onStanzaReceived.connect(boost::bind(&Server::handleStanzaReceived, this, _1, session));
 			session->onSessionFinished.connect(boost::bind(&Server::handleSessionFinished, this, session));
+			session->start();
 		}
 
-		void handleSessionFinished(ServerFromClientSession* session) {
+		void handleSessionFinished(boost::shared_ptr<ServerFromClientSession> session) {
 			serverFromClientSessions_.erase(std::remove(serverFromClientSessions_.begin(), serverFromClientSessions_.end(), session), serverFromClientSessions_.end());
-			delete session;
 		}
 
-		void handleStanzaReceived(boost::shared_ptr<Stanza> stanza, ServerFromClientSession* session) {
+		void handleStanzaReceived(boost::shared_ptr<Stanza> stanza, boost::shared_ptr<ServerFromClientSession> session) {
 			stanza->setFrom(session->getJID());
 			if (!stanza->getTo().isValid()) {
 				stanza->setTo(JID(session->getDomain()));
@@ -74,7 +74,7 @@ class Server {
 		UserRegistry* userRegistry_;
 		BoostIOServiceThread boostIOServiceThread_;
 		boost::shared_ptr<BoostConnectionServer> serverFromClientConnectionServer_;
-		std::vector<ServerFromClientSession*> serverFromClientSessions_;
+		std::vector< boost::shared_ptr<ServerFromClientSession> > serverFromClientSessions_;
 		FullPayloadParserFactoryCollection payloadParserFactories_;
 		FullPayloadSerializerCollection payloadSerializers_;
 };
diff --git a/Nim/main.cpp b/Nim/main.cpp
index 6747d4a..7c7105e 100644
--- a/Nim/main.cpp
+++ b/Nim/main.cpp
@@ -53,14 +53,15 @@ class Server {
 
 	private:
 		void handleNewConnection(boost::shared_ptr<Connection> c) {
-			ServerFromClientSession* session = new ServerFromClientSession(idGenerator_.generateID(), c, &payloadParserFactories_, &payloadSerializers_, &userRegistry_);
+			boost::shared_ptr<ServerFromClientSession> session(new ServerFromClientSession(idGenerator_.generateID(), c, &payloadParserFactories_, &payloadSerializers_, &userRegistry_));
 			serverFromClientSessions_.push_back(session);
 			session->onStanzaReceived.connect(boost::bind(&Server::handleStanzaReceived, this, _1, session));
 			session->onSessionStarted.connect(boost::bind(&Server::handleSessionStarted, this, session));
 			session->onSessionFinished.connect(boost::bind(&Server::handleSessionFinished, this, session));
+			session->start();
 		}
 		
-		void handleSessionStarted(ServerFromClientSession* session) {
+		void handleSessionStarted(boost::shared_ptr<ServerFromClientSession> session) {
 			if (!dnsSDServiceRegistered_) {
 				dnsSDServiceRegistered_ = true;
 				dnsSDService_->onServiceRegistered.connect(boost::bind(&Server::handleServiceRegistered, this, _1));
@@ -81,16 +82,15 @@ class Server {
 			std::cout << "Service registered " << service.name << " " << service.type << " " << service.domain << std::endl;
 		}
 
-		void handleSessionFinished(ServerFromClientSession* session) {
+		void handleSessionFinished(boost::shared_ptr<ServerFromClientSession> session) {
 			serverFromClientSessions_.erase(std::remove(serverFromClientSessions_.begin(), serverFromClientSessions_.end(), session), serverFromClientSessions_.end());
-			delete session;
 			if (serverFromClientSessions_.empty()) {
 				dnsSDServiceRegistered_ = false;
 				dnsSDService_->unregisterService();
 			}
 		}
 
-		void handleStanzaReceived(boost::shared_ptr<Stanza> stanza, ServerFromClientSession* session) {
+		void handleStanzaReceived(boost::shared_ptr<Stanza> stanza, boost::shared_ptr<ServerFromClientSession> session) {
 			stanza->setFrom(session->getJID());
 			if (!stanza->getTo().isValid()) {
 				stanza->setTo(JID(session->getDomain()));
@@ -124,7 +124,7 @@ class Server {
 		boost::shared_ptr<AppleDNSSDService> dnsSDService_;
 		boost::shared_ptr<LinkLocalRoster> linkLocalRoster_;
 		boost::shared_ptr<BoostConnectionServer> serverFromClientConnectionServer_;
-		std::vector<ServerFromClientSession*> serverFromClientSessions_;
+		std::vector< boost::shared_ptr<ServerFromClientSession> > serverFromClientSessions_;
 		FullPayloadParserFactoryCollection payloadParserFactories_;
 		FullPayloadSerializerCollection payloadSerializers_;
 		bool dnsSDServiceRegistered_;
diff --git a/Swiften/Client/Session.cpp b/Swiften/Client/Session.cpp
index dbeddb7..2f0e076 100644
--- a/Swiften/Client/Session.cpp
+++ b/Swiften/Client/Session.cpp
@@ -44,7 +44,7 @@ void Session::start() {
 	state_ = Connecting;
 	connection_ = connectionFactory_->createConnection();
 	connection_->onConnected.connect(boost::bind(&Session::handleConnected, this));
-	connection_->onError.connect(boost::bind(&Session::handleConnectionError, this, _1));
+	connection_->onDisconnected.connect(boost::bind(&Session::handleDisconnected, this, _1));
 	connection_->connect(jid_.getDomain());
 }
 
@@ -75,20 +75,22 @@ void Session::initializeStreamStack() {
 	streamStack_ = new StreamStack(xmppLayer_, connectionLayer_);
 }
 
-void Session::handleConnectionError(Connection::Error error) {
-	switch (error) {
-		case Connection::DomainNameResolveError:
-			setError(DomainNameResolveError);
-			break;
-		case Connection::ReadError:
-			setError(ConnectionReadError);
-			break;
-		case Connection::WriteError:
-			setError(ConnectionWriteError);
-			break;
-		case Connection::ConnectionError:
-			setError(ConnectionError);
-			break;
+void Session::handleDisconnected(const boost::optional<Connection::Error>& error) {
+	if (error) {
+		switch (*error) {
+			case Connection::DomainNameResolveError:
+				setError(DomainNameResolveError);
+				break;
+			case Connection::ReadError:
+				setError(ConnectionReadError);
+				break;
+			case Connection::WriteError:
+				setError(ConnectionWriteError);
+				break;
+			case Connection::ConnectionError:
+				setError(ConnectionError);
+				break;
+		}
 	}
 }
 
diff --git a/Swiften/Client/Session.h b/Swiften/Client/Session.h
index 1b4d1fe..17c10b9 100644
--- a/Swiften/Client/Session.h
+++ b/Swiften/Client/Session.h
@@ -87,7 +87,7 @@ namespace Swift {
 			void sendSessionStart();
 
 			void handleConnected();
-			void handleConnectionError(Connection::Error);
+			void handleDisconnected(const boost::optional<Connection::Error>&);
 			void handleElement(boost::shared_ptr<Element>);
 			void handleStreamStart();
 			void handleTLSConnected();
diff --git a/Swiften/Client/UnitTest/SessionTest.cpp b/Swiften/Client/UnitTest/SessionTest.cpp
index 5d43736..45c0996 100644
--- a/Swiften/Client/UnitTest/SessionTest.cpp
+++ b/Swiften/Client/UnitTest/SessionTest.cpp
@@ -488,7 +488,7 @@ class SessionTest : public CppUnit::TestFixture {
 
 			void connect(const String& domain) {
 				if (fail_) {
-					MainEventLoop::postEvent(boost::bind(boost::ref(onError), Connection::ConnectionError));
+					MainEventLoop::postEvent(boost::bind(boost::ref(onDisconnected), Connection::ConnectionError));
 				}
 				else {
 					domain_ = domain;
@@ -497,7 +497,7 @@ class SessionTest : public CppUnit::TestFixture {
 			}
 
 			void setError() {
-				MainEventLoop::postEvent(boost::bind(boost::ref(onError), Connection::ConnectionError));
+				MainEventLoop::postEvent(boost::bind(boost::ref(onDisconnected), Connection::ConnectionError));
 			}
 
 			void write(const ByteArray& data) {
diff --git a/Swiften/LinkLocal/LinkLocalServiceInfo.cpp b/Swiften/LinkLocal/LinkLocalServiceInfo.cpp
index 99f83b8..8ee7ae0 100644
--- a/Swiften/LinkLocal/LinkLocalServiceInfo.cpp
+++ b/Swiften/LinkLocal/LinkLocalServiceInfo.cpp
@@ -45,4 +45,70 @@ ByteArray LinkLocalServiceInfo::getEncoded(const String& s) {
 	return sizeByte + ByteArray(s);
 }
 
+LinkLocalServiceInfo LinkLocalServiceInfo::createFromTXTRecord(const ByteArray& record) {
+	LinkLocalServiceInfo info;
+	size_t i = 0;
+	while (i < record.getSize()) {
+		std::pair<String,String> entry = readEntry(record, &i);
+		if (entry.first.isEmpty()) {
+			break;
+		}
+		else if (entry.first == "1st") {
+			info.setFirstName(entry.second);
+		}
+		else if (entry.first == "last") {
+			info.setLastName(entry.second);
+		}
+		else if (entry.first == "email") {
+			info.setEMail(entry.second);
+		}
+		else if (entry.first == "jid") {
+			info.setJID(JID(entry.second));
+		}
+		else if (entry.first == "msg") {
+			info.setMessage(entry.second);
+		}
+		else if (entry.first == "nick") {
+			info.setNick(entry.second);
+		}
+		else if (entry.first == "port.p2pj") {
+			info.setPort(boost::lexical_cast<int>(entry.second));
+		}
+		else if (entry.first == "status") {
+			if (entry.second == "away") {
+				info.setStatus(Away);
+			}
+			else if (entry.second == "dnd") {
+				info.setStatus(DND);
+			}
+		}
+	}
+	return info;
+}
+
+std::pair<String,String> LinkLocalServiceInfo::readEntry(const ByteArray& record, size_t* index) {
+	size_t& i = *index;
+	String key;
+	String value;
+
+	size_t entryEnd = i + 1 + record[i];
+	++i;
+	bool inKey = true;
+	while (i < entryEnd && i < record.getSize()) {
+		if (inKey) {
+			if (record[i] == '=') {
+				inKey = false;
+			}
+			else {
+				key += record[i];
+			}
+		}
+		else {
+			value += record[i];
+		}
+		++i;
+	}
+	return std::make_pair(key, value);
+}
+
 }
diff --git a/Swiften/LinkLocal/LinkLocalServiceInfo.h b/Swiften/LinkLocal/LinkLocalServiceInfo.h
index bd5286b..d78b70c 100644
--- a/Swiften/LinkLocal/LinkLocalServiceInfo.h
+++ b/Swiften/LinkLocal/LinkLocalServiceInfo.h
@@ -40,8 +40,11 @@ namespace Swift {
 
 			ByteArray toTXTRecord() const;
 
+			static LinkLocalServiceInfo createFromTXTRecord(const ByteArray& record);
+
 		private:
 			static ByteArray getEncoded(const String&);
+			static std::pair<String,String> readEntry(const ByteArray&, size_t*);
 
 		private:
 			String firstName;
diff --git a/Swiften/LinkLocal/UnitTest/LinkLocalServiceInfoTest.cpp b/Swiften/LinkLocal/UnitTest/LinkLocalServiceInfoTest.cpp
index 8efe4ae..b850f14 100644
--- a/Swiften/LinkLocal/UnitTest/LinkLocalServiceInfoTest.cpp
+++ b/Swiften/LinkLocal/UnitTest/LinkLocalServiceInfoTest.cpp
@@ -8,6 +8,8 @@ using namespace Swift;
 class LinkLocalServiceInfoTest : public CppUnit::TestFixture {
 		CPPUNIT_TEST_SUITE(LinkLocalServiceInfoTest);
 		CPPUNIT_TEST(testGetTXTRecord);
+		CPPUNIT_TEST(testCreateFromTXTRecord);
+		CPPUNIT_TEST(testCreateFromTXTRecord_InvalidSize);
 		CPPUNIT_TEST_SUITE_END();
 
 	public:
@@ -21,6 +23,20 @@ class LinkLocalServiceInfoTest : public CppUnit::TestFixture {
 
 			CPPUNIT_ASSERT_EQUAL(ByteArray("\x09txtvers=1\x09" + String("1st=Remko\x0dlast=Tron\xc3\xe7on\x0bstatus=away")), info.toTXTRecord());
 		}
+
+		void testCreateFromTXTRecord() {
+			LinkLocalServiceInfo info = LinkLocalServiceInfo::createFromTXTRecord(ByteArray("\x09txtvers=1\x09" + String("1st=Remko\x0dlast=Tron\xc3\xe7on\x0bstatus=away")));
+
+			CPPUNIT_ASSERT_EQUAL(String("Remko"), info.getFirstName());
+			CPPUNIT_ASSERT_EQUAL(String("Tron\xc3\xe7on"), info.getLastName());
+			CPPUNIT_ASSERT_EQUAL(LinkLocalServiceInfo::Away, info.getStatus());
+		}
+
+		void testCreateFromTXTRecord_InvalidSize() {
+			LinkLocalServiceInfo info = LinkLocalServiceInfo::createFromTXTRecord(ByteArray("\x10last=a"));
+
+			CPPUNIT_ASSERT_EQUAL(String("a"), info.getLastName());
+		}
 };
 
 CPPUNIT_TEST_SUITE_REGISTRATION(LinkLocalServiceInfoTest);
diff --git a/Swiften/Network/BoostConnection.cpp b/Swiften/Network/BoostConnection.cpp
index f837b50..d374221 100644
--- a/Swiften/Network/BoostConnection.cpp
+++ b/Swiften/Network/BoostConnection.cpp
@@ -59,7 +59,7 @@ void BoostConnection::connect(const String& domain) {
 				boost::bind(&BoostConnection::handleConnectFinished, shared_from_this(), boost::asio::placeholders::error));
 	}
 	catch (const DomainNameResolveException& e) {
-		onError(DomainNameResolveError);
+		onDisconnected(DomainNameResolveError);
 	}
 }
 
@@ -79,7 +79,7 @@ void BoostConnection::handleConnectFinished(const boost::system::error_code& err
 		doRead();
 	}
 	else if (error != boost::asio::error::operation_aborted) {
-		MainEventLoop::postEvent(boost::bind(boost::ref(onError), ConnectionError), shared_from_this());
+		MainEventLoop::postEvent(boost::bind(boost::ref(onDisconnected), ConnectionError), shared_from_this());
 	}
 }
 
@@ -94,14 +94,23 @@ void BoostConnection::handleSocketRead(const boost::system::error_code& error, s
 		MainEventLoop::postEvent(boost::bind(boost::ref(onDataRead), ByteArray(&readBuffer_[0], bytesTransferred)), shared_from_this());
 		doRead();
 	}
+	else if (error == boost::asio::error::eof) {
+		MainEventLoop::postEvent(boost::bind(boost::ref(onDisconnected), boost::optional<Error>()), shared_from_this());
+	}
 	else if (error != boost::asio::error::operation_aborted) {
-		MainEventLoop::postEvent(boost::bind(boost::ref(onError), ReadError), shared_from_this());
+		MainEventLoop::postEvent(boost::bind(boost::ref(onDisconnected), ReadError), shared_from_this());
 	}
 }
 
 void BoostConnection::handleDataWritten(const boost::system::error_code& error) {
-	if (error && error != boost::asio::error::operation_aborted) {
-		MainEventLoop::postEvent(boost::bind(boost::ref(onError), WriteError), shared_from_this());
+	if (!error) {
+		return;
+	}
+	if (error == boost::asio::error::eof) {
+		MainEventLoop::postEvent(boost::bind(boost::ref(onDisconnected), boost::optional<Error>()), shared_from_this());
+	}
+	else if (error && error != boost::asio::error::operation_aborted) {
+		MainEventLoop::postEvent(boost::bind(boost::ref(onDisconnected), WriteError), shared_from_this());
 	}
 }
 
diff --git a/Swiften/Network/Connection.h b/Swiften/Network/Connection.h
index 16d5d7b..c34c21d 100644
--- a/Swiften/Network/Connection.h
+++ b/Swiften/Network/Connection.h
@@ -26,7 +26,7 @@ namespace Swift {
 
 		public:
 			boost::signal<void ()> onConnected;
-			boost::signal<void (Error)> onError;
+			boost::signal<void (const boost::optional<Error>&)> onDisconnected;
 			boost::signal<void (const ByteArray&)> onDataRead;
 	};
 }
diff --git a/Swiften/Server/ServerFromClientSession.cpp b/Swiften/Server/ServerFromClientSession.cpp
index fe4388e..9a3cf83 100644
--- a/Swiften/Server/ServerFromClientSession.cpp
+++ b/Swiften/Server/ServerFromClientSession.cpp
@@ -32,6 +32,15 @@ ServerFromClientSession::ServerFromClientSession(
 			authenticated_(false),
 			initialized_(false) {
 	xmppLayer_ = boost::shared_ptr<XMPPLayer>(new XMPPLayer(payloadParserFactories_, payloadSerializers_));
+	connectionLayer_ = boost::shared_ptr<ConnectionLayer>(new ConnectionLayer(connection_));
+	streamStack_ = new StreamStack(xmppLayer_, connectionLayer_);
+}
+
+ServerFromClientSession::~ServerFromClientSession() {
+	delete streamStack_;
+}
+
+void ServerFromClientSession::start() {
 	xmppLayer_->onStreamStart.connect(
 			boost::bind(&ServerFromClientSession::handleStreamStart, this, _2));
 	xmppLayer_->onElement.connect(
@@ -42,12 +51,7 @@ ServerFromClientSession::ServerFromClientSession(
 			boost::bind(boost::ref(onDataRead), _1));
 	xmppLayer_->onWriteData.connect(
 			boost::bind(boost::ref(onDataWritten), _1));
-	connectionLayer_ = boost::shared_ptr<ConnectionLayer>(new ConnectionLayer(connection_));
-	streamStack_ = new StreamStack(xmppLayer_, connectionLayer_);
-}
-
-ServerFromClientSession::~ServerFromClientSession() {
-	delete streamStack_;
+	connection_->onDisconnected.connect(boost::bind(&ServerFromClientSession::handleDisconnected, shared_from_this(), _1));
 }
 
 void ServerFromClientSession::handleElement(boost::shared_ptr<Element> element) {
@@ -114,5 +118,9 @@ void ServerFromClientSession::sendStanza(boost::shared_ptr<Stanza> stanza) {
 	xmppLayer_->writeElement(stanza);
 }
 
+void ServerFromClientSession::handleDisconnected(const boost::optional<Connection::Error>&) {
+	onSessionFinished();
+}
+
 
 }
diff --git a/Swiften/Server/ServerFromClientSession.h b/Swiften/Server/ServerFromClientSession.h
index 0110d5d..e7df99d 100644
--- a/Swiften/Server/ServerFromClientSession.h
+++ b/Swiften/Server/ServerFromClientSession.h
@@ -2,9 +2,11 @@
 
 #include <boost/shared_ptr.hpp>
 #include <boost/signal.hpp>
+#include <boost/enable_shared_from_this.hpp>
 
 #include "Swiften/Base/String.h"
 #include "Swiften/JID/JID.h"
+#include "Swiften/Network/Connection.h"
 
 namespace Swift {
 	class Element;
@@ -18,7 +20,7 @@ namespace Swift {
 	class Connection;
 	class ByteArray;
 
-	class ServerFromClientSession {
+	class ServerFromClientSession : public boost::enable_shared_from_this<ServerFromClientSession> {
 		public:
 			ServerFromClientSession(
 					const String& id,
@@ -28,6 +30,8 @@ namespace Swift {
 					UserRegistry* userRegistry);
 			~ServerFromClientSession();
 
+			void start();
+
 			void sendStanza(boost::shared_ptr<Stanza>);
 
 			const JID& getJID() const {
@@ -45,6 +49,7 @@ namespace Swift {
 			boost::signal<void (const ByteArray&)> onDataRead;
 
 		private:
+			void handleDisconnected(const boost::optional<Connection::Error>& error);
 			void handleElement(boost::shared_ptr<Element>);
 			void handleStreamStart(const String& domain);
 
-- 
cgit v0.10.2-6-g49f6