From 2c954f7e9464b8fdd970eed829e2ba632e8979d8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Thu, 16 Jul 2009 22:06:28 +0200
Subject: Implemented AppleDNSSDService registration & browsing.


diff --git a/Nim/main.cpp b/Nim/main.cpp
index 2709bb1..607cc80 100644
--- a/Nim/main.cpp
+++ b/Nim/main.cpp
@@ -40,15 +40,14 @@ class DummyUserRegistry : public UserRegistry {
 
 class Server {
 	public:
-		Server() {
-			serverFromClientConnectionServer_ = boost::shared_ptr<BoostConnectionServer>(new BoostConnectionServer(5224, &boostIOServiceThread_.getIOService()));
+		Server(int clientConnectionPort, int linkLocalConnectionPort) : dnsSDServiceRegistered_(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_->start();
 
 			dnsSDService_ = boost::shared_ptr<AppleDNSSDService>(new AppleDNSSDService());
 			linkLocalRoster_ = boost::shared_ptr<LinkLocalRoster>(new LinkLocalRoster(dnsSDService_));
 			dnsSDService_->start();
-			// dnsSDService_->publish
 		}
 
 	private:
@@ -56,12 +55,29 @@ class Server {
 			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));
 		}
+		
+		void handleSessionStarted(ServerFromClientSession* session) {
+			if (!dnsSDServiceRegistered_) {
+				dnsSDServiceRegistered_ = true;
+				dnsSDService_->onServiceRegistered.connect(boost::bind(&Server::handleServiceRegistered, this, _1));
+				dnsSDService_->registerService(session->getJID().toBare().toString(), linkLocalConnectionPort_, std::map<String,String>());
+			}
+		}
+
+		void handleServiceRegistered(const DNSSDService::Service& service) {
+			std::cout << "Service registered " << service.name << " " << service.type << " " << service.domain << std::endl;
+		}
 
 		void handleSessionFinished(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) {
@@ -101,11 +117,14 @@ class Server {
 		std::vector<ServerFromClientSession*> serverFromClientSessions_;
 		FullPayloadParserFactoryCollection payloadParserFactories_;
 		FullPayloadSerializerCollection payloadSerializers_;
+		bool dnsSDServiceRegistered_;
+		int clientConnectionPort_;
+		int linkLocalConnectionPort_;
 };
 
 int main() {
 	SimpleEventLoop eventLoop;
-	Server server;
+	Server server(5222, 5562);
 	eventLoop.run();
 	return 0;
 }
diff --git a/Swiften/LinkLocal/AppleDNSSDService.cpp b/Swiften/LinkLocal/AppleDNSSDService.cpp
index b68b09e..3b71f74 100644
--- a/Swiften/LinkLocal/AppleDNSSDService.cpp
+++ b/Swiften/LinkLocal/AppleDNSSDService.cpp
@@ -1,48 +1,74 @@
 #include "Swiften/LinkLocal/AppleDNSSDService.h"
 
-#include <dns_sd.h>
+#include <algorithm>
+#include <unistd.h>
 #include <iostream>
 #include <sys/socket.h>
 
+#include "Swiften/EventLoop/MainEventLoop.h"
+
 namespace Swift {
 
+#if 0
 namespace {
-	void handleServiceRegistered(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, void *context ) {
-		std::cerr << "Service registered " << name << " " << regtype << " " << domain << std::endl;
-	}
-
-	void handleServiceDiscovered(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, const char *replyDomain, void *context ) {
-		std::cerr << "Service discovered " << interfaceIndex << " " << serviceName << " " << regtype << " " << replyDomain << " " << flags << std::endl;
-	}
-
 	void handleServiceResolved( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const unsigned char *txtRecord, void *context ) {
 		std::cerr << "Service resolved " << fullname << " " << hosttarget << " " << port << " " << txtLen << " " << /*std::string((const char*) txtRecord, txtLen) <<*/ std::endl;
 	}
 
 	void handleAddressInfoReceived ( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *hostname, const struct sockaddr *address, uint32_t ttl, void *context ) {
-		std::cerr << "Address received " << HostAddress((const unsigned char*) address->sa_data, 4).toString() << std::endl;
+		//std::cerr << "Address received " << HostAddress((const unsigned char*) address->sa_data, 4).toString() << std::endl;
 	} 
 }
 
+/*
+		result = DNSServiceResolve(&resolveSDRef, 0, 6, "Remko@Micro", "_presence._tcp.", "local.", handleServiceResolved , 0);
+		result = DNSServiceGetAddrInfo(&addressSDRef, 0, 6, kDNSServiceProtocol_IPv4, "Micro.local.", handleAddressInfoReceived, 0);
+	*/
+#endif
+
 
-AppleDNSSDService::AppleDNSSDService() : thread(0), stopRequested(false) {
+AppleDNSSDService::AppleDNSSDService() : thread(0), stopRequested(false), browseSDRef(0), registerSDRef(0) {
+	int fds[2];
+	int result = pipe(fds);
+	assert(result == 0);
+	interruptSelectReadSocket = fds[0];
+	interruptSelectWriteSocket = fds[1];
 }
 
 AppleDNSSDService::~AppleDNSSDService() {
 	stop();
 }
 
-void AppleDNSSDService::publishService(const std::map<String,String>) {
-}
-
 void AppleDNSSDService::start() {
 	assert(!thread);
 	thread = new boost::thread(boost::bind(&AppleDNSSDService::doStart, shared_from_this()));
 }
 
+void AppleDNSSDService::registerService(const String& name, int port, const std::map<String,String>&) {
+	// TODO: Use properties
+	boost::lock_guard<boost::mutex> lock(sdRefsMutex);
+
+	assert(!registerSDRef);
+	DNSServiceErrorType result = DNSServiceRegister(&registerSDRef, 0, 0, name.getUTF8Data(), "_presence._tcp", NULL, NULL, port, 0, NULL, &AppleDNSSDService::handleServiceRegisteredGlobal, this);
+	interruptSelect();
+	if (result != kDNSServiceErr_NoError) {
+		onError();
+	}
+}
+
+void AppleDNSSDService::unregisterService() {
+	boost::lock_guard<boost::mutex> lock(sdRefsMutex);
+
+	assert(registerSDRef);
+	interruptSelect();
+	DNSServiceRefDeallocate(registerSDRef);
+	registerSDRef = NULL;
+}
+
 void AppleDNSSDService::stop() {
 	if (thread) {
 		stopRequested = true;
+		interruptSelect();
 		thread->join();
 		delete thread;
 		stopRequested = false;
@@ -50,66 +76,100 @@ void AppleDNSSDService::stop() {
 }
 
 void AppleDNSSDService::doStart() {
-	while (!stopRequested) {
+	// Listen for new services
+	assert(!browseSDRef);
+	DNSServiceErrorType result = DNSServiceBrowse(&browseSDRef, 0, 0, "_presence._tcp", 0, &AppleDNSSDService::handleServiceDiscoveredGlobal , this);
+	if (result != kDNSServiceErr_NoError) {
+		MainEventLoop::postEvent(boost::ref(onError), shared_from_this());
+		return;
+	}
 
+	// Run the main loop
+	while (!stopRequested) {
 		fd_set fdSet;
-		DNSServiceErrorType result;
-
-		DNSServiceRef registerSDRef;
-		result = DNSServiceRegister(&registerSDRef, 0, 0, "eemi", "_presence._tcp", NULL, NULL, 5269, 0, NULL, handleServiceRegistered, NULL);
-		if (result != kDNSServiceErr_NoError) {
-			std::cerr << "Error 1" << std::endl;
-		}
-		int registerSocket = DNSServiceRefSockFD(registerSDRef);
 		FD_ZERO(&fdSet);
-		FD_SET(registerSocket, &fdSet);
-		select(registerSocket+1, &fdSet, &fdSet, &fdSet, 0);
-		DNSServiceProcessResult(registerSDRef);
-
-		DNSServiceRef browseSDRef;
-		result = DNSServiceBrowse(&browseSDRef, 0, 0, "_presence._tcp", 0, handleServiceDiscovered , 0);
-		if (result != kDNSServiceErr_NoError) {
-			std::cerr << "Error 2" << std::endl;
-		}
-		int browseSocket = DNSServiceRefSockFD(browseSDRef);
-		//while(true) {
-			FD_ZERO(&fdSet);
+		int maxSocket = 0;
+
+		{
+			boost::lock_guard<boost::mutex> lock(sdRefsMutex);
+
+			// Browsing
+			int browseSocket = DNSServiceRefSockFD(browseSDRef);
+			maxSocket = browseSocket;
 			FD_SET(browseSocket, &fdSet);
-			select(browseSocket+1, &fdSet, &fdSet, &fdSet, 0);
-			DNSServiceProcessResult(browseSDRef);
-		//}
 
+			// Registration
+			if (registerSDRef) {
+				int registerSocket = DNSServiceRefSockFD(registerSDRef);
+				maxSocket = std::max(maxSocket, registerSocket);
+				FD_SET(registerSocket, &fdSet);
+			}
+		}
 
-		DNSServiceRef resolveSDRef;
-		result = DNSServiceResolve(&resolveSDRef, 0, 6, "Remko@Micro", "_presence._tcp.", "local.", handleServiceResolved , 0);
-		if (result != kDNSServiceErr_NoError) {
-			std::cerr << "Error 3" << std::endl;
+		int selectResult = select(maxSocket+1, &fdSet, NULL, NULL, 0);
+
+		{
+			boost::lock_guard<boost::mutex> lock(sdRefsMutex);
+
+			if (selectResult == -1) {
+				MainEventLoop::postEvent(boost::ref(onError), shared_from_this());
+				return;
+			}
+			if (selectResult == 0) {
+				continue;
+			}
+			if (FD_ISSET(DNSServiceRefSockFD(browseSDRef), &fdSet)) {
+				DNSServiceProcessResult(browseSDRef);
+			}
+			if (registerSDRef && FD_ISSET(DNSServiceRefSockFD(registerSDRef), &fdSet)) {
+				DNSServiceProcessResult(registerSDRef);
+			}
 		}
-		int resolveSocket = DNSServiceRefSockFD(resolveSDRef);
-		//while(true) {
-			FD_ZERO(&fdSet);
-			FD_SET(resolveSocket, &fdSet);
-			select(resolveSocket+1, &fdSet, &fdSet, &fdSet, 0);
-			DNSServiceProcessResult(resolveSDRef);
-		//}
+	}
 
+	if (registerSDRef) {
+		DNSServiceRefDeallocate(registerSDRef);
+		registerSDRef = NULL;
+	}
 
-		DNSServiceRef addressSDRef;
-		result = DNSServiceGetAddrInfo(&addressSDRef, 0, 6, kDNSServiceProtocol_IPv4, "Micro.local.", handleAddressInfoReceived, 0);
-		if (result != kDNSServiceErr_NoError) {
-			std::cerr << "Error 4" << std::endl;
+	DNSServiceRefDeallocate(browseSDRef);
+	browseSDRef = NULL;
+}
+
+void AppleDNSSDService::interruptSelect() {
+	char c = 0;
+	write(interruptSelectWriteSocket, &c, 1);
+}
+
+void AppleDNSSDService::handleServiceDiscoveredGlobal(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, const char *replyDomain, void *context ) {
+	static_cast<AppleDNSSDService*>(context)->handleServiceDiscovered(sdRef, flags, interfaceIndex, errorCode, serviceName, regtype, replyDomain);
+}
+
+void AppleDNSSDService::handleServiceDiscovered(DNSServiceRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, const char *replyDomain) {
+	if (errorCode != kDNSServiceErr_NoError) {
+		MainEventLoop::postEvent(boost::ref(onError), shared_from_this());
+	}
+	else {
+		Service service(serviceName, regtype, replyDomain, interfaceIndex);
+		if (flags & kDNSServiceFlagsAdd) {
+			MainEventLoop::postEvent(boost::bind(boost::ref(onServiceAdded), service), shared_from_this());
 		}
-		int addressSocket = DNSServiceRefSockFD(addressSDRef);
-		//while(true) {
-		std::cout << "GetAddrInfo2" << std::endl;
-			FD_ZERO(&fdSet);
-			FD_SET(addressSocket, &fdSet);
-			select(addressSocket+1, &fdSet, &fdSet, &fdSet, 0);
-			DNSServiceProcessResult(addressSDRef);
-		//}
+		else {
+			MainEventLoop::postEvent(boost::bind(boost::ref(onServiceRemoved), service), shared_from_this());
+		}
+	}
+}
 
-		// DNSServiceRefDeallocate
+void AppleDNSSDService::handleServiceRegisteredGlobal(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, void *context) {
+	static_cast<AppleDNSSDService*>(context)->handleServiceRegistered(sdRef, flags, errorCode, name, regtype, domain);
+}
 
+void AppleDNSSDService::handleServiceRegistered(DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain) {
+	if (errorCode != kDNSServiceErr_NoError) {
+		MainEventLoop::postEvent(boost::ref(onError), shared_from_this());
+	}
+	else {
+		MainEventLoop::postEvent(boost::bind(boost::ref(onServiceRegistered), Service(name, regtype, domain, 0)), shared_from_this());
 	}
 }
 
diff --git a/Swiften/LinkLocal/AppleDNSSDService.h b/Swiften/LinkLocal/AppleDNSSDService.h
index e6bedf7..3607ce0 100644
--- a/Swiften/LinkLocal/AppleDNSSDService.h
+++ b/Swiften/LinkLocal/AppleDNSSDService.h
@@ -2,6 +2,8 @@
 
 #include <boost/enable_shared_from_this.hpp>
 #include <boost/thread.hpp>
+#include <boost/thread/mutex.hpp>
+#include <dns_sd.h>
 
 #include "Swiften/LinkLocal/DNSSDService.h"
 #include "Swiften/EventLoop/EventOwner.h"
@@ -12,15 +14,27 @@ namespace Swift {
 			AppleDNSSDService();
 			~AppleDNSSDService();
 
-			virtual void publishService(const std::map<String,String> properties);
+			virtual void registerService(const String& name, int port, const std::map<String,String>& properties);
+			virtual void unregisterService();
 			virtual void start();
 			virtual void stop();
 
 		private:
 			void doStart();
+			void interruptSelect();
+
+			static void handleServiceDiscoveredGlobal(DNSServiceRef, DNSServiceFlags, uint32_t, DNSServiceErrorType, const char *, const char *, const char *, void*);
+			void handleServiceDiscovered(DNSServiceRef, DNSServiceFlags, uint32_t, DNSServiceErrorType, const char *, const char *, const char *);
+			static void handleServiceRegisteredGlobal(DNSServiceRef, DNSServiceFlags, DNSServiceErrorType, const char *, const char *, const char *, void *);
+			void handleServiceRegistered(DNSServiceRef, DNSServiceFlags, DNSServiceErrorType, const char *, const char *, const char *);
 
 		private:
 			boost::thread* thread;
 			bool stopRequested;
+			int interruptSelectReadSocket;
+			int interruptSelectWriteSocket;
+			boost::mutex sdRefsMutex;
+			DNSServiceRef browseSDRef;
+			DNSServiceRef registerSDRef;
 	};
 }
diff --git a/Swiften/LinkLocal/DNSSDService.h b/Swiften/LinkLocal/DNSSDService.h
index 0899ffd..3437c58 100644
--- a/Swiften/LinkLocal/DNSSDService.h
+++ b/Swiften/LinkLocal/DNSSDService.h
@@ -8,7 +8,8 @@
 namespace Swift {
 	class DNSSDService {
 		public:
-			struct DiscoveredService {
+			struct Service {
+				Service(const String& name, const String& type, const String& domain, int networkInterface) : name(name), type(type), domain(domain), networkInterface(networkInterface) {}
 				String name;
 				String type;
 				String domain;
@@ -17,11 +18,13 @@ namespace Swift {
 
 			virtual ~DNSSDService();
 
-			virtual void publishService(const std::map<String,String> properties) = 0;
+			virtual void registerService(const String& name, int port, const std::map<String,String>& properties) = 0;
+			virtual void unregisterService() = 0;
 			virtual void start() = 0;
 
-			boost::signal<void (const DiscoveredService&)> onServiceAdded;
-			boost::signal<void (const DiscoveredService&)> onServiceRemoved;
-			boost::signal<void ()> onServiceRegistered;
+			boost::signal<void (const Service&)> onServiceAdded;
+			boost::signal<void (const Service&)> onServiceRemoved;
+			boost::signal<void (const Service&)> onServiceRegistered;
+			boost::signal<void ()> onError;
 	};
 }
diff --git a/Swiften/LinkLocal/LinkLocalRoster.cpp b/Swiften/LinkLocal/LinkLocalRoster.cpp
index 6773d51..9f7e4ad 100644
--- a/Swiften/LinkLocal/LinkLocalRoster.cpp
+++ b/Swiften/LinkLocal/LinkLocalRoster.cpp
@@ -10,11 +10,11 @@ LinkLocalRoster::LinkLocalRoster(boost::shared_ptr<DNSSDService> service) : dnsS
 	service->onServiceRemoved.connect(boost::bind(&LinkLocalRoster::handleServiceRemoved, this, _1));
 }
 
-void LinkLocalRoster::handleServiceAdded(const DNSSDService::DiscoveredService& service) {
+void LinkLocalRoster::handleServiceAdded(const DNSSDService::Service& service) {
 	std::cout << "Service added " << service.name << " " << service.type << " " << service.domain << std::endl;
 }
 
-void LinkLocalRoster::handleServiceRemoved(const DNSSDService::DiscoveredService& service) {
+void LinkLocalRoster::handleServiceRemoved(const DNSSDService::Service& service) {
 	std::cout << "Service removed " << service.name << " " << service.type << " " << service.domain << std::endl;
 }
 
diff --git a/Swiften/LinkLocal/LinkLocalRoster.h b/Swiften/LinkLocal/LinkLocalRoster.h
index 625bf8b..5b66b84 100644
--- a/Swiften/LinkLocal/LinkLocalRoster.h
+++ b/Swiften/LinkLocal/LinkLocalRoster.h
@@ -10,8 +10,8 @@ namespace Swift {
 			LinkLocalRoster(boost::shared_ptr<DNSSDService> service);
 
 		private:
-			void handleServiceAdded(const DNSSDService::DiscoveredService&);
-			void handleServiceRemoved(const DNSSDService::DiscoveredService&);
+			void handleServiceAdded(const DNSSDService::Service&);
+			void handleServiceRemoved(const DNSSDService::Service&);
 
 		private:
 			boost::shared_ptr<DNSSDService> dnsSDService;
diff --git a/Swiften/Server/ServerFromClientSession.cpp b/Swiften/Server/ServerFromClientSession.cpp
index 67067c5..fe4388e 100644
--- a/Swiften/Server/ServerFromClientSession.cpp
+++ b/Swiften/Server/ServerFromClientSession.cpp
@@ -89,6 +89,7 @@ void ServerFromClientSession::handleElement(boost::shared_ptr<Element> element)
 			else if (iq->getPayload<StartSession>()) {
 				initialized_ = true;
 				xmppLayer_->writeElement(IQ::createResult(jid_, iq->getID()));
+				onSessionStarted();
 			}
 		}
 	}
diff --git a/Swiften/Server/ServerFromClientSession.h b/Swiften/Server/ServerFromClientSession.h
index 62e89ef..0110d5d 100644
--- a/Swiften/Server/ServerFromClientSession.h
+++ b/Swiften/Server/ServerFromClientSession.h
@@ -40,6 +40,7 @@ namespace Swift {
 
 			boost::signal<void (boost::shared_ptr<Stanza>)> onStanzaReceived;
 			boost::signal<void ()> onSessionFinished;
+			boost::signal<void ()> onSessionStarted;
 			boost::signal<void (const ByteArray&)> onDataWritten;
 			boost::signal<void (const ByteArray&)> onDataRead;
 
-- 
cgit v0.10.2-6-g49f6