From 4052a822acd9da9dab6a8e2343c6170fb08dd8d6 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:48:20 +0200
Subject: Implement outgoing presence bridge.


diff --git a/Nim/main.cpp b/Nim/main.cpp
index 024ecd1..8166534 100644
--- a/Nim/main.cpp
+++ b/Nim/main.cpp
@@ -1,5 +1,3 @@
-// TODO: Prohibit multiple logins
-
 #include <string>
 #include <boost/bind.hpp>
 #include <boost/shared_ptr.hpp>
@@ -54,6 +52,8 @@ class Server {
 			serverFromNetworkConnectionServer_->start();
 
 			dnsSDService_ = boost::shared_ptr<AppleDNSSDService>(new AppleDNSSDService());
+			dnsSDService_->onServiceRegistered.connect(boost::bind(&Server::handleServiceRegistered, this, _1));
+
 			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));
@@ -67,7 +67,6 @@ class Server {
 			}
 			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();
 		}
@@ -76,34 +75,21 @@ class Server {
 			std::cout << "Incoming link local connection" << std::endl;
 		}
 		
-		void handleSessionStarted(boost::shared_ptr<ServerFromClientSession> session) {
-			if (!dnsSDServiceRegistered_) {
-				dnsSDServiceRegistered_ = true;
-				dnsSDService_->onServiceRegistered.connect(boost::bind(&Server::handleServiceRegistered, this, _1));
-				LinkLocalServiceInfo info;
-				info.setFirstName("Remko");
-				info.setLastName("Tron\xc3\xa7on");
-				info.setEMail("email@example.com");
-				info.setJID(JID("jid@example.com"));
-				info.setMessage("I'm not Here");
-				info.setNick("Remko");
-				info.setStatus(LinkLocalServiceInfo::Away);
-				info.setPort(linkLocalConnectionPort_);
-				dnsSDService_->registerService(session->getJID().toBare().toString(), linkLocalConnectionPort_, info);
-			}
-		}
-
 		void handleServiceRegistered(const DNSSDService::Service& service) {
 			std::cout << "Service registered " << service.name << " " << service.type << " " << service.domain << std::endl;
 		}
 
 		void handleSessionFinished(boost::shared_ptr<ServerFromClientSession>) {
 			serverFromClientSession_.reset();
-
-			std::cout << "Service unregistered" << std::endl;
-			dnsSDServiceRegistered_ = false;
+			unregisterService();
 			rosterRequested_ = false;
-			dnsSDService_->unregisterService();
+		}
+
+		void unregisterService() {
+			if (dnsSDServiceRegistered_) {
+				dnsSDServiceRegistered_ = false;
+				dnsSDService_->unregisterService();
+			}
 		}
 
 		void handleStanzaReceived(boost::shared_ptr<Stanza> stanza, boost::shared_ptr<ServerFromClientSession> session) {
@@ -111,7 +97,22 @@ class Server {
 			if (!stanza->getTo().isValid()) {
 				stanza->setTo(JID(session->getDomain()));
 			}
-			if (!stanza->getTo().isValid() || stanza->getTo() == session->getDomain() || stanza->getTo() == session->getJID().toBare()) {
+
+			if (boost::shared_ptr<Presence> presence = boost::dynamic_pointer_cast<Presence>(stanza)) {
+				if (presence->getType() == Presence::Available) {
+					if (!dnsSDServiceRegistered_) {
+						dnsSDServiceRegistered_ = true;
+						dnsSDService_->registerService(session->getJID().toBare().toString(), linkLocalConnectionPort_, getLinkLocalServiceInfo(presence));
+					}
+					else {
+						dnsSDService_->updateService(getLinkLocalServiceInfo(presence));
+					}
+				}
+				else {
+					unregisterService();
+				}
+			}
+			else 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>()) {
 						if (iq->getType() == IQ::Get) {
@@ -156,6 +157,32 @@ class Server {
 			}
 		}
 
+		LinkLocalServiceInfo getLinkLocalServiceInfo(boost::shared_ptr<Presence> presence) {
+			LinkLocalServiceInfo info;
+			info.setFirstName("Remko");
+			info.setLastName("Tron\xc3\xa7on");
+			info.setEMail("email@example.com");
+			info.setJID(JID("jid@example.com"));
+			info.setMessage(presence->getStatus());
+			info.setNick("Remko");
+			switch (presence->getShow()) {
+				case StatusShow::Online: 
+				case StatusShow::None: 
+				case StatusShow::FFC: 
+					info.setStatus(LinkLocalServiceInfo::Available);
+					break;
+				case StatusShow::Away: 
+				case StatusShow::XA: 
+					info.setStatus(LinkLocalServiceInfo::Away);
+					break;
+				case StatusShow::DND: 
+					info.setStatus(LinkLocalServiceInfo::DND);
+					break;
+			}
+			info.setPort(linkLocalConnectionPort_);
+			return info;
+		}
+
 	private:
 		IDGenerator idGenerator_;
 		BoostIOServiceThread boostIOServiceThread_;
diff --git a/Swiften/LinkLocal/AppleDNSSDService.cpp b/Swiften/LinkLocal/AppleDNSSDService.cpp
index 18cdcb0..d4befd1 100644
--- a/Swiften/LinkLocal/AppleDNSSDService.cpp
+++ b/Swiften/LinkLocal/AppleDNSSDService.cpp
@@ -55,6 +55,13 @@ void AppleDNSSDService::registerService(const String& name, int port, const Link
 	interruptSelect();
 }
 
+void AppleDNSSDService::updateService(const LinkLocalServiceInfo& info) {
+	boost::lock_guard<boost::mutex> lock(sdRefsMutex);
+	assert(registerSDRef);
+	ByteArray txtRecord = info.toTXTRecord();
+	DNSServiceUpdateRecord(registerSDRef, NULL, NULL, txtRecord.getSize(), txtRecord.getData(), 0);
+}
+
 void AppleDNSSDService::unregisterService() {
 	boost::lock_guard<boost::mutex> lock(sdRefsMutex);
 
@@ -91,6 +98,7 @@ void AppleDNSSDService::stopResolvingService(const Service& service) {
 
 void AppleDNSSDService::resolveHostname(const String& hostname, int interfaceIndex) {
 	boost::lock_guard<boost::mutex> lock(sdRefsMutex);
+	std::cout << "Resolve " << hostname << std::endl;
 
 	DNSServiceRef hostnameResolveSDRef;
 	DNSServiceErrorType result = DNSServiceGetAddrInfo(&hostnameResolveSDRef, 0, interfaceIndex, kDNSServiceProtocol_IPv4, hostname.getUTF8Data(), &AppleDNSSDService::handleHostnameResolvedGlobal, this);
@@ -110,11 +118,14 @@ void AppleDNSSDService::doStart() {
 	onStarted();
 
 	// Listen for new services
-	assert(!browseSDRef);
-	DNSServiceErrorType result = DNSServiceBrowse(&browseSDRef, 0, 0, "_presence._tcp", 0, &AppleDNSSDService::handleServiceDiscoveredGlobal , this);
-	if (result != kDNSServiceErr_NoError) {
-		std::cerr << "Error creating browse query" << std::endl;
-		haveError = true;
+	{
+		boost::lock_guard<boost::mutex> lock(sdRefsMutex);
+		assert(!browseSDRef);
+		DNSServiceErrorType result = DNSServiceBrowse(&browseSDRef, 0, 0, "_presence._tcp", 0, &AppleDNSSDService::handleServiceDiscoveredGlobal , this);
+		if (result != kDNSServiceErr_NoError) {
+			std::cerr << "Error creating browse query" << std::endl;
+			haveError = true;
+		}
 	}
 
 	// Run the main loop
@@ -148,6 +159,7 @@ void AppleDNSSDService::doStart() {
 
 			// Hostname resolving
 			for (HostnameSDRefs::const_iterator i = hostnameResolveSDRefs.begin(); i != hostnameResolveSDRefs.end(); ++i) {
+				std::cout << "Adding ostname resolve " << std::endl;
 				int hostnameResolveSocket = DNSServiceRefSockFD(*i);
 				maxSocket = std::max(maxSocket, hostnameResolveSocket);
 				FD_SET(hostnameResolveSocket, &fdSet);
@@ -181,6 +193,7 @@ void AppleDNSSDService::doStart() {
 			for (HostnameSDRefs::const_iterator i = hostnameResolveSDRefs.begin(); i != hostnameResolveSDRefs.end(); ++i) {
 				if (FD_ISSET(DNSServiceRefSockFD(*i), &fdSet)) {
 					DNSServiceProcessResult(*i);
+					std::cout << "Removing hostnameResolve" << std::endl;
 					hostnameResolveSDRefs.erase(std::remove(hostnameResolveSDRefs.begin(), hostnameResolveSDRefs.end(), *i), hostnameResolveSDRefs.end());
 					DNSServiceRefDeallocate(*i);
 					break; // Stop the loop, because we removed an element
@@ -189,23 +202,28 @@ void AppleDNSSDService::doStart() {
 		}
 	}
 
-	for (ServiceSDRefMap::const_iterator i = resolveSDRefs.begin(); i != resolveSDRefs.end(); ++i) {
-		DNSServiceRefDeallocate(i->second);
-	}
-	resolveSDRefs.clear();
+	{
+		boost::lock_guard<boost::mutex> lock(sdRefsMutex);
 
-	for (HostnameSDRefs::const_iterator i = hostnameResolveSDRefs.begin(); i != hostnameResolveSDRefs.end(); ++i) {
-		DNSServiceRefDeallocate(*i);
-	}
-	hostnameResolveSDRefs.clear();
+		for (ServiceSDRefMap::const_iterator i = resolveSDRefs.begin(); i != resolveSDRefs.end(); ++i) {
+			DNSServiceRefDeallocate(i->second);
+		}
+		resolveSDRefs.clear();
 
-	if (registerSDRef) {
-		DNSServiceRefDeallocate(registerSDRef);
-		registerSDRef = NULL;
-	}
+		for (HostnameSDRefs::const_iterator i = hostnameResolveSDRefs.begin(); i != hostnameResolveSDRefs.end(); ++i) {
+			std::cout << "Removing hostnameResolve" << std::endl;
+			DNSServiceRefDeallocate(*i);
+		}
+		hostnameResolveSDRefs.clear();
+
+		if (registerSDRef) {
+			DNSServiceRefDeallocate(registerSDRef);
+			registerSDRef = NULL;
+		}
 
-	DNSServiceRefDeallocate(browseSDRef);
-	browseSDRef = NULL;
+		DNSServiceRefDeallocate(browseSDRef);
+		browseSDRef = NULL;
+	}
 
 	MainEventLoop::postEvent(boost::bind(boost::ref(onStopped), haveError), shared_from_this());
 }
diff --git a/Swiften/LinkLocal/AppleDNSSDService.h b/Swiften/LinkLocal/AppleDNSSDService.h
index cdeafed..fe4a648 100644
--- a/Swiften/LinkLocal/AppleDNSSDService.h
+++ b/Swiften/LinkLocal/AppleDNSSDService.h
@@ -18,6 +18,7 @@ namespace Swift {
 			virtual void stop();
 
 			virtual void registerService(const String& name, int port, const LinkLocalServiceInfo&);
+			virtual void updateService(const LinkLocalServiceInfo&);
 			virtual void unregisterService();
 
 			virtual void startResolvingService(const Service&);
diff --git a/Swiften/LinkLocal/DNSSDService.h b/Swiften/LinkLocal/DNSSDService.h
index 3dd8c7b..9689352 100644
--- a/Swiften/LinkLocal/DNSSDService.h
+++ b/Swiften/LinkLocal/DNSSDService.h
@@ -55,6 +55,7 @@ namespace Swift {
 			virtual void stop() = 0;
 
 			virtual void registerService(const String& name, int port, const LinkLocalServiceInfo&) = 0;
+			virtual void updateService(const LinkLocalServiceInfo&) = 0;
 			virtual void unregisterService() = 0;
 
 			virtual void startResolvingService(const Service&) = 0;
diff --git a/Swiften/LinkLocal/LinkLocalRoster.cpp b/Swiften/LinkLocal/LinkLocalRoster.cpp
index aedea77..89bfa55 100644
--- a/Swiften/LinkLocal/LinkLocalRoster.cpp
+++ b/Swiften/LinkLocal/LinkLocalRoster.cpp
@@ -81,6 +81,9 @@ void LinkLocalRoster::handleServiceAdded(const DNSSDService::Service& service) {
 }
 
 void LinkLocalRoster::handleServiceRemoved(const DNSSDService::Service& service) {
+	if (selfService && *selfService == service) {
+		return;
+	}
 	dnsSDService->stopResolvingService(service);
 	services.erase(service);
 	boost::shared_ptr<RosterPayload> roster(new RosterPayload());
-- 
cgit v0.10.2-6-g49f6