From 70e454fd02597c9a71ed89356a572e48256f8349 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Sat, 18 Jul 2009 16:10:19 +0200
Subject: Implement roster<->linklocal bridge.


diff --git a/Nim/main.cpp b/Nim/main.cpp
index 390bc6c..024ecd1 100644
--- a/Nim/main.cpp
+++ b/Nim/main.cpp
@@ -1,8 +1,11 @@
+// TODO: Prohibit multiple logins
+
 #include <string>
 #include <boost/bind.hpp>
 #include <boost/shared_ptr.hpp>
 
 #include "Swiften/Elements/IQ.h"
+#include "Swiften/Elements/Presence.h"
 #include "Swiften/Elements/RosterPayload.h"
 #include "Swiften/Elements/VCard.h"
 #include "Swiften/Server/UserRegistry.h"
@@ -41,24 +44,36 @@ class DummyUserRegistry : public UserRegistry {
 
 class Server {
 	public:
-		Server(int clientConnectionPort, int linkLocalConnectionPort) : dnsSDServiceRegistered_(false), clientConnectionPort_(clientConnectionPort), linkLocalConnectionPort_(linkLocalConnectionPort) {
+		Server(int clientConnectionPort, int linkLocalConnectionPort) : dnsSDServiceRegistered_(false), rosterRequested_(false), clientConnectionPort_(clientConnectionPort), linkLocalConnectionPort_(linkLocalConnectionPort) {
 			serverFromClientConnectionServer_ = boost::shared_ptr<BoostConnectionServer>(new BoostConnectionServer(clientConnectionPort, &boostIOServiceThread_.getIOService()));
-			serverFromClientConnectionServer_->onNewConnection.connect(boost::bind(&Server::handleNewConnection, this, _1));
+			serverFromClientConnectionServer_->onNewConnection.connect(boost::bind(&Server::handleNewClientConnection, this, _1));
 			serverFromClientConnectionServer_->start();
 
+			serverFromNetworkConnectionServer_ = boost::shared_ptr<BoostConnectionServer>(new BoostConnectionServer(linkLocalConnectionPort, &boostIOServiceThread_.getIOService()));
+			serverFromNetworkConnectionServer_->onNewConnection.connect(boost::bind(&Server::handleNewLinkLocalConnection, this, _1));
+			serverFromNetworkConnectionServer_->start();
+
 			dnsSDService_ = boost::shared_ptr<AppleDNSSDService>(new AppleDNSSDService());
 			linkLocalRoster_ = boost::shared_ptr<LinkLocalRoster>(new LinkLocalRoster(dnsSDService_));
+			linkLocalRoster_->onRosterChanged.connect(boost::bind(&Server::handleRosterChanged, this, _1));
+			linkLocalRoster_->onPresenceChanged.connect(boost::bind(&Server::handlePresenceChanged, this, _1));
 			dnsSDService_->start();
 		}
 
 	private:
-		void handleNewConnection(boost::shared_ptr<Connection> c) {
-			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 handleNewClientConnection(boost::shared_ptr<Connection> c) {
+			if (serverFromClientSession_) {
+				c->disconnect();
+			}
+			serverFromClientSession_ = boost::shared_ptr<ServerFromClientSession>(new ServerFromClientSession(idGenerator_.generateID(), c, &payloadParserFactories_, &payloadSerializers_, &userRegistry_));
+			serverFromClientSession_->onStanzaReceived.connect(boost::bind(&Server::handleStanzaReceived, this, _1, serverFromClientSession_));
+			serverFromClientSession_->onSessionStarted.connect(boost::bind(&Server::handleSessionStarted, this, serverFromClientSession_));
+			serverFromClientSession_->onSessionFinished.connect(boost::bind(&Server::handleSessionFinished, this, serverFromClientSession_));
+			serverFromClientSession_->start();
+		}
+
+		void handleNewLinkLocalConnection(boost::shared_ptr<Connection>) {
+			std::cout << "Incoming link local connection" << std::endl;
 		}
 		
 		void handleSessionStarted(boost::shared_ptr<ServerFromClientSession> session) {
@@ -82,13 +97,13 @@ class Server {
 			std::cout << "Service registered " << service.name << " " << service.type << " " << service.domain << std::endl;
 		}
 
-		void handleSessionFinished(boost::shared_ptr<ServerFromClientSession> session) {
-			serverFromClientSessions_.erase(std::remove(serverFromClientSessions_.begin(), serverFromClientSessions_.end(), session), serverFromClientSessions_.end());
-			if (serverFromClientSessions_.empty()) {
-				std::cout << "Service unregistered" << std::endl;
-				dnsSDServiceRegistered_ = false;
-				dnsSDService_->unregisterService();
-			}
+		void handleSessionFinished(boost::shared_ptr<ServerFromClientSession>) {
+			serverFromClientSession_.reset();
+
+			std::cout << "Service unregistered" << std::endl;
+			dnsSDServiceRegistered_ = false;
+			rosterRequested_ = false;
+			dnsSDService_->unregisterService();
 		}
 
 		void handleStanzaReceived(boost::shared_ptr<Stanza> stanza, boost::shared_ptr<ServerFromClientSession> session) {
@@ -99,7 +114,16 @@ class Server {
 			if (!stanza->getTo().isValid() || stanza->getTo() == session->getDomain() || stanza->getTo() == session->getJID().toBare()) {
 				if (boost::shared_ptr<IQ> iq = boost::dynamic_pointer_cast<IQ>(stanza)) {
 					if (iq->getPayload<RosterPayload>()) {
-						session->sendStanza(IQ::createResult(iq->getFrom(), iq->getID(), boost::shared_ptr<RosterPayload>(new RosterPayload())));
+						if (iq->getType() == IQ::Get) {
+							session->sendStanza(IQ::createResult(iq->getFrom(), iq->getID(), linkLocalRoster_->getRoster()));
+							rosterRequested_ = true;
+							foreach(const boost::shared_ptr<Presence> presence, linkLocalRoster_->getAllPresence()) {
+								session->sendStanza(presence);
+							}
+						}
+						else {
+							session->sendStanza(IQ::createError(iq->getFrom(), iq->getID(), Error::Forbidden, Error::Cancel));
+						}
 					}
 					if (iq->getPayload<VCard>()) {
 						if (iq->getType() == IQ::Get) {
@@ -118,6 +142,20 @@ class Server {
 			}
 		}
 
+		void handleRosterChanged(boost::shared_ptr<RosterPayload> roster) {
+			if (rosterRequested_) {
+				boost::shared_ptr<IQ> iq = IQ::createRequest(IQ::Set, serverFromClientSession_->getJID(), idGenerator_.generateID(), roster);
+				iq->setFrom(serverFromClientSession_->getJID().toBare());
+				serverFromClientSession_->sendStanza(iq);
+			}
+		}
+
+		void handlePresenceChanged(boost::shared_ptr<Presence> presence) {
+			if (rosterRequested_) {
+				serverFromClientSession_->sendStanza(presence);
+			}
+		}
+
 	private:
 		IDGenerator idGenerator_;
 		BoostIOServiceThread boostIOServiceThread_;
@@ -125,10 +163,12 @@ class Server {
 		boost::shared_ptr<AppleDNSSDService> dnsSDService_;
 		boost::shared_ptr<LinkLocalRoster> linkLocalRoster_;
 		boost::shared_ptr<BoostConnectionServer> serverFromClientConnectionServer_;
-		std::vector< boost::shared_ptr<ServerFromClientSession> > serverFromClientSessions_;
+		boost::shared_ptr<ServerFromClientSession> serverFromClientSession_;
+		boost::shared_ptr<BoostConnectionServer> serverFromNetworkConnectionServer_;
 		FullPayloadParserFactoryCollection payloadParserFactories_;
 		FullPayloadSerializerCollection payloadSerializers_;
 		bool dnsSDServiceRegistered_;
+		bool rosterRequested_;
 		int clientConnectionPort_;
 		int linkLocalConnectionPort_;
 };
diff --git a/Swiften/LinkLocal/LinkLocalRoster.cpp b/Swiften/LinkLocal/LinkLocalRoster.cpp
index feec7b7..aedea77 100644
--- a/Swiften/LinkLocal/LinkLocalRoster.cpp
+++ b/Swiften/LinkLocal/LinkLocalRoster.cpp
@@ -12,37 +12,92 @@ LinkLocalRoster::LinkLocalRoster(boost::shared_ptr<DNSSDService> service) : dnsS
 	dnsSDService->onServiceAdded.connect(boost::bind(&LinkLocalRoster::handleServiceAdded, this, _1));
 	dnsSDService->onServiceRemoved.connect(boost::bind(&LinkLocalRoster::handleServiceRemoved, this, _1));
 	dnsSDService->onServiceResolved.connect(boost::bind(&LinkLocalRoster::handleServiceResolved, this, _1, _2));
-	dnsSDService->onHostnameResolved.connect(boost::bind(&LinkLocalRoster::handleHostnameResolved, this, _1, _2));
+}
+
+boost::shared_ptr<RosterPayload> LinkLocalRoster::getRoster() const {
+	boost::shared_ptr<RosterPayload> roster(new RosterPayload());
+	for(ServiceMap::const_iterator i = services.begin(); i != services.end(); ++i) {
+		roster->addItem(getRosterItem(i->first, i->second));
+	}
+	return roster;
+}
+
+std::vector<boost::shared_ptr<Presence> > LinkLocalRoster::getAllPresence() const {
+	std::vector<boost::shared_ptr<Presence> > result;
+	for(ServiceMap::const_iterator i = services.begin(); i != services.end(); ++i) {
+		result.push_back(getPresence(i->first, i->second));
+	}
+	return result;
+}
+
+RosterItemPayload LinkLocalRoster::getRosterItem(const DNSSDService::Service& service, const DNSSDService::ResolveResult& resolveResult) const {
+ return RosterItemPayload(getJIDForService(service), getRosterName(service, resolveResult), RosterItemPayload::Both);
+}
+
+String LinkLocalRoster::getRosterName(const DNSSDService::Service& service, const DNSSDService::ResolveResult& resolveResult) const {
+	if (!resolveResult.info.getNick().isEmpty()) {
+		return resolveResult.info.getNick();
+	}
+	else if (!resolveResult.info.getFirstName().isEmpty()) {
+		String result = resolveResult.info.getFirstName();
+		if (!resolveResult.info.getLastName().isEmpty()) {
+			result += " " + resolveResult.info.getLastName();
+		}
+		return result;
+	}
+	else if (!resolveResult.info.getLastName().isEmpty()) {
+		return resolveResult.info.getLastName();
+	}
+	return service.name;
+}
+
+JID LinkLocalRoster::getJIDForService(const DNSSDService::Service& service) const {
+	return JID(service.name);
+}
+
+boost::shared_ptr<Presence> LinkLocalRoster::getPresence(const DNSSDService::Service& service, const DNSSDService::ResolveResult& resolveResult) const {
+	boost::shared_ptr<Presence> presence(new Presence());
+	presence->setFrom(getJIDForService(service));
+	switch (resolveResult.info.getStatus()) {
+		case LinkLocalServiceInfo::Available:
+			presence->setShow(StatusShow::Online);
+			break;
+		case LinkLocalServiceInfo::Away:
+			presence->setShow(StatusShow::Away);
+			break;
+		case LinkLocalServiceInfo::DND:
+			presence->setShow(StatusShow::DND);
+			break;
+	}
+	presence->setStatus(resolveResult.info.getMessage());
+	return presence;
 }
 
 void LinkLocalRoster::handleServiceAdded(const DNSSDService::Service& service) {
 	if (selfService && *selfService == service) {
 		return;
 	}
-	std::cout << "Service added " << service.name << " " << service.type << " " << service.domain << std::endl;
 	dnsSDService->startResolvingService(service);
 }
 
 void LinkLocalRoster::handleServiceRemoved(const DNSSDService::Service& service) {
-	std::cout << "Service removed " << service.name << " " << service.type << " " << service.domain << std::endl;
 	dnsSDService->stopResolvingService(service);
+	services.erase(service);
+	boost::shared_ptr<RosterPayload> roster(new RosterPayload());
+	roster->addItem(RosterItemPayload(getJIDForService(service), "", RosterItemPayload::Remove));
+	onRosterChanged(roster);
 }
 
 void LinkLocalRoster::handleServiceResolved(const DNSSDService::Service& service, const DNSSDService::ResolveResult& result) {
-	std::cout << "Service resolved: " << service.name << "->" << result.host << " " << result.port << " " << result.info.getLastName() << std::endl;
+	services.insert(std::make_pair(service, result));
 	dnsSDService->resolveHostname(result.host);
-}
 
-void LinkLocalRoster::handleHostnameResolved(const String& hostname, const boost::optional<HostAddress>& address) {
-	if (address) {
-		std::cout << "Address resolved: " << hostname << " " << address->toString() << std::endl;
-	}
-	else {
-		std::cout << "Unable to resolve address for " << hostname << std::endl;
-	}
+	boost::shared_ptr<RosterPayload> roster(new RosterPayload());
+	roster->addItem(getRosterItem(service, result));
+	onRosterChanged(roster);
+	onPresenceChanged(getPresence(service, result));
 }
 
-
 void LinkLocalRoster::handleServiceRegistered(const DNSSDService::Service& service) {
 	selfService = service;
 }
diff --git a/Swiften/LinkLocal/LinkLocalRoster.h b/Swiften/LinkLocal/LinkLocalRoster.h
index 321bd96..33dd455 100644
--- a/Swiften/LinkLocal/LinkLocalRoster.h
+++ b/Swiften/LinkLocal/LinkLocalRoster.h
@@ -2,8 +2,13 @@
 
 #include <boost/shared_ptr.hpp>
 #include <boost/optional.hpp>
+#include <set>
 
+#include "Swiften/Base/String.h"
+#include "Swiften/JID/JID.h"
 #include "Swiften/LinkLocal/DNSSDService.h"
+#include "Swiften/Elements/RosterPayload.h"
+#include "Swiften/Elements/Presence.h"
 
 namespace Swift {
 	class HostAddress;
@@ -12,16 +17,28 @@ namespace Swift {
 		public:
 			LinkLocalRoster(boost::shared_ptr<DNSSDService> service);
 
+			boost::shared_ptr<RosterPayload> getRoster() const;
+			std::vector<boost::shared_ptr<Presence> > getAllPresence() const;
+
+			boost::signal<void (boost::shared_ptr<RosterPayload>)> onRosterChanged;
+			boost::signal<void (boost::shared_ptr<Presence>)> onPresenceChanged;
+
 		private:
+			RosterItemPayload getRosterItem(const DNSSDService::Service& service, const DNSSDService::ResolveResult& info) const;
+			String getRosterName(const DNSSDService::Service& service, const DNSSDService::ResolveResult& info) const;
+			JID getJIDForService(const DNSSDService::Service& service) const;
+			boost::shared_ptr<Presence> getPresence(const DNSSDService::Service& service, const DNSSDService::ResolveResult& info) const;
+
 			void handleStopped(bool);
 			void handleServiceRegistered(const DNSSDService::Service& service);
 			void handleServiceAdded(const DNSSDService::Service&);
 			void handleServiceRemoved(const DNSSDService::Service&);
 			void handleServiceResolved(const DNSSDService::Service& service, const DNSSDService::ResolveResult& result);
-			void handleHostnameResolved(const String& hostname, const boost::optional<HostAddress>& address);
 
 		private:
 			boost::shared_ptr<DNSSDService> dnsSDService;
 			boost::optional<DNSSDService::Service> selfService;
+			typedef std::map<DNSSDService::Service, DNSSDService::ResolveResult> ServiceMap;
+			ServiceMap services;
 	};
 }
-- 
cgit v0.10.2-6-g49f6