From a4f3cfa0e9932d123f115de76172128bc69cc255 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Sun, 19 Jul 2009 22:41:07 +0200
Subject: Added beginnings of AvahiDNSSDService.


diff --git a/Nim/main.cpp b/Nim/main.cpp
index a9e9934..a048312 100644
--- a/Nim/main.cpp
+++ b/Nim/main.cpp
@@ -2,6 +2,7 @@
 #include <boost/bind.hpp>
 #include <boost/shared_ptr.hpp>
 
+#include "Swiften/Base/Platform.h"
 #include "Swiften/Session/SessionTracer.h"
 #include "Swiften/Network/BoostConnectionFactory.h"
 #include "Swiften/Elements/IQ.h"
@@ -23,7 +24,6 @@
 #include "Swiften/LinkLocal/OutgoingLinkLocalSession.h"
 #include "Swiften/LinkLocal/IncomingLinkLocalSession.h"
 #include "Swiften/LinkLocal/DNSSDService.h"
-#include "Swiften/LinkLocal/AppleDNSSDService.h"
 #include "Swiften/Network/ConnectionServer.h"
 #include "Swiften/Network/BoostConnection.h"
 #include "Swiften/Network/BoostIOServiceThread.h"
@@ -32,6 +32,12 @@
 #include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h"
 #include "Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h"
 
+#if defined(SWIFTEN_PLATFORM_MACOSX) || defined(SWIFTEN_PLATFORM_WINDOWS)
+#include "Swiften/LinkLocal/AppleDNSSDService.h"
+#else
+#include "Swiften/LinkLocal/AvahiDNSSDService.h"
+#endif
+
 using namespace Swift;
 
 
@@ -66,8 +72,13 @@ class Server {
 					boost::bind(&Server::handleNewLinkLocalConnection, this, _1));
 			serverFromNetworkConnectionServer_->start();
 
+#if defined(SWIFTEN_PLATFORM_MACOSX) || defined(SWIFTEN_PLATFORM_WINDOWS)
 			dnsSDService_ = boost::shared_ptr<AppleDNSSDService>(
 					new AppleDNSSDService());
+#else
+			dnsSDService_ = boost::shared_ptr<AvahiDNSSDService>(
+					new AvahiDNSSDService());
+#endif
 			dnsSDService_->onServiceRegistered.connect
 					(boost::bind(&Server::handleServiceRegistered, this, _1));
 			linkLocalRoster_ = boost::shared_ptr<LinkLocalRoster>(
@@ -312,7 +323,7 @@ class Server {
 		IDGenerator idGenerator_;
 		BoostIOServiceThread boostIOServiceThread_;
 		DummyUserRegistry userRegistry_;
-		boost::shared_ptr<AppleDNSSDService> dnsSDService_;
+		boost::shared_ptr<DNSSDService> dnsSDService_;
 		boost::shared_ptr<LinkLocalRoster> linkLocalRoster_;
 		boost::shared_ptr<BoostConnectionServer> serverFromClientConnectionServer_;
 		boost::shared_ptr<ServerFromClientSession> serverFromClientSession_;
diff --git a/Swiften/LinkLocal/AppleDNSSDService.cpp b/Swiften/LinkLocal/AppleDNSSDService.cpp
index 521c584..969cbb2 100644
--- a/Swiften/LinkLocal/AppleDNSSDService.cpp
+++ b/Swiften/LinkLocal/AppleDNSSDService.cpp
@@ -268,6 +268,7 @@ void AppleDNSSDService::handleServiceResolvedGlobal(DNSServiceRef sdRef, DNSServ
 
 void AppleDNSSDService::handleServiceResolved(DNSServiceRef sdRef, DNSServiceFlags, uint32_t, DNSServiceErrorType errorCode, const char *, const char *hosttarget, uint16_t port, uint16_t txtLen, const unsigned char *txtRecord) {
 	if (errorCode != kDNSServiceErr_NoError) {
+		// TODO
 		std::cerr << "Resolve error " << hosttarget << std::endl;
 		return;
 	}
diff --git a/Swiften/LinkLocal/AvahiDNSSDService.cpp b/Swiften/LinkLocal/AvahiDNSSDService.cpp
new file mode 100644
index 0000000..62ea172
--- /dev/null
+++ b/Swiften/LinkLocal/AvahiDNSSDService.cpp
@@ -0,0 +1,157 @@
+#include "Swiften/LinkLocal/AvahiDNSSDService.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/EventLoop/MainEventLoop.h"
+#include "Swiften/LinkLocal/LinkLocalServiceInfo.h"
+#include "Swiften/Network/HostAddress.h"
+
+namespace Swift {
+
+AvahiDNSSDService::AvahiDNSSDService() : client(NULL), threadedPoll(0), serviceBrowser(0) {
+}
+
+AvahiDNSSDService::~AvahiDNSSDService() {
+}
+
+void AvahiDNSSDService::start() {
+	threadedPoll = avahi_threaded_poll_new();
+
+	int error;
+	client = avahi_client_new(
+			avahi_threaded_poll_get(threadedPoll), 
+			static_cast<AvahiClientFlags>(0), NULL, this, &error); // TODO
+	if (!client) {
+		// TODO
+		std::cerr << "Error 1" << std::endl;
+		return;
+	}
+
+	serviceBrowser = avahi_service_browser_new(
+			client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 
+			"_presence._tcp", NULL, static_cast<AvahiLookupFlags>(0), 
+			handleServiceDiscoveredGlobal, this);
+	if (!serviceBrowser) {
+		// TODO
+		std::cerr << "Error 2" << std::endl;
+		return;
+	}
+
+	avahi_threaded_poll_start(threadedPoll);
+}
+
+void AvahiDNSSDService::stop() {
+	avahi_threaded_poll_stop(threadedPoll);
+	avahi_service_browser_free(serviceBrowser);
+	avahi_client_free(client);
+	avahi_threaded_poll_free(threadedPoll);
+}
+
+void AvahiDNSSDService::registerService(const String& name, int port, const LinkLocalServiceInfo& info) {
+	avahi_threaded_poll_lock(threadedPoll);
+	avahi_threaded_poll_unlock(threadedPoll);
+}
+
+void AvahiDNSSDService::updateService(const LinkLocalServiceInfo& info) {
+	avahi_threaded_poll_lock(threadedPoll);
+	avahi_threaded_poll_unlock(threadedPoll);
+}
+
+void AvahiDNSSDService::unregisterService() {
+	avahi_threaded_poll_lock(threadedPoll);
+	avahi_threaded_poll_unlock(threadedPoll);
+}
+
+void AvahiDNSSDService::startResolvingService(const Service& service) {
+	avahi_threaded_poll_lock(threadedPoll);
+
+	AvahiServiceResolver* resolver = avahi_service_resolver_new(
+			client, 
+			service.networkInterface, 
+			AVAHI_PROTO_INET,
+			service.name.getUTF8Data(), 
+			service.type.getUTF8Data(), 
+			service.domain.getUTF8Data(), 
+			AVAHI_PROTO_UNSPEC, 
+			static_cast<AvahiLookupFlags>(0), 
+			&AvahiDNSSDService::handleServiceResolvedGlobal, 
+			this);
+	assert(serviceResolvers.find(service) == serviceResolvers.end());
+	serviceResolvers[service] = resolver;
+
+	avahi_threaded_poll_unlock(threadedPoll);
+}
+
+void AvahiDNSSDService::stopResolvingService(const Service& service) {
+	avahi_threaded_poll_lock(threadedPoll);
+
+	ServiceResolverMap::iterator i = serviceResolvers.find(service);
+	assert(i != serviceResolvers.end());
+	avahi_service_resolver_free(i->second);
+	serviceResolvers.erase(i);
+
+	avahi_threaded_poll_unlock(threadedPoll);
+}
+
+// TODO: Take interfaceIndex into account
+void AvahiDNSSDService::resolveHostname(const String& hostname, int) {
+	HostnameAddressMap::const_iterator i = hostnameAddresses.find(hostname);
+	if (i == hostnameAddresses.end()) {
+		MainEventLoop::postEvent(boost::bind(boost::ref(onHostnameResolved), hostname, boost::optional<HostAddress>()), shared_from_this());
+	}
+	else {
+		MainEventLoop::postEvent(boost::bind(boost::ref(onHostnameResolved), hostname, boost::optional<HostAddress>(i->second)), shared_from_this());
+	}
+}
+
+void AvahiDNSSDService::handleServiceDiscovered(AvahiServiceBrowser *, AvahiIfIndex interfaceIndex, AvahiProtocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AvahiLookupResultFlags) {
+	switch(event) {
+		case AVAHI_BROWSER_FAILURE:
+			std::cerr << "Browse error" << std::endl;
+			// TODO	
+			return;
+		case AVAHI_BROWSER_NEW: {
+				std::cerr << "Service added: " << name << " " << type << " " << domain << std::endl;
+				Service service(name, type, domain, interfaceIndex);
+				MainEventLoop::postEvent(boost::bind(boost::ref(onServiceAdded), service), shared_from_this());
+			}
+			break;
+		case AVAHI_BROWSER_REMOVE: {
+				std::cerr << "Service removed: " << name << " " << type << " " << domain << std::endl;
+				Service service(name, type, domain, interfaceIndex);
+				MainEventLoop::postEvent(boost::bind(boost::ref(onServiceRemoved), service), shared_from_this());
+			}
+			break;
+		case AVAHI_BROWSER_ALL_FOR_NOW:
+		case AVAHI_BROWSER_CACHE_EXHAUSTED:
+			break;
+	}
+}
+
+void AvahiDNSSDService::handleServiceResolved(AvahiServiceResolver *, AvahiIfIndex interfaceIndex, AvahiProtocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *hostname, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags) {
+	switch(event) {
+		case AVAHI_RESOLVER_FAILURE:
+			//TODO
+			std::cerr << "Resolve error" << std::endl;
+			break;
+		case AVAHI_RESOLVER_FOUND:
+			ByteArray data;
+			for(AvahiStringList* i = txt; i; i = avahi_string_list_get_next(i)) {
+				char size = i->size;
+				data += ByteArray(&size, 1);
+				data += ByteArray(reinterpret_cast<char*>(avahi_string_list_get_text(i)), avahi_string_list_get_size(i));
+			}
+			
+			assert(address->proto == AVAHI_PROTO_INET);
+			HostAddress hostAddress(reinterpret_cast<const unsigned char*>(&address->data.ipv4.address), 4);
+			hostnameAddresses[String(hostname)] = hostAddress;
+			MainEventLoop::postEvent(boost::bind(boost::ref(onServiceResolved), 
+					Service(name, type, domain, interfaceIndex), 
+					ResolveResult(hostname, port, 
+						LinkLocalServiceInfo::createFromTXTRecord(data))),
+					shared_from_this());
+			break;
+	}
+}
+
+}
diff --git a/Swiften/LinkLocal/AvahiDNSSDService.h b/Swiften/LinkLocal/AvahiDNSSDService.h
new file mode 100644
index 0000000..8d31e41
--- /dev/null
+++ b/Swiften/LinkLocal/AvahiDNSSDService.h
@@ -0,0 +1,55 @@
+#pragma once
+
+#include <map>
+#include <boost/enable_shared_from_this.hpp>
+#include <avahi-client/client.h>
+#include <avahi-client/lookup.h>
+#include <avahi-common/thread-watch.h>
+#include <avahi-common/watch.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+
+
+#include "Swiften/EventLoop/EventOwner.h"
+#include "Swiften/LinkLocal/DNSSDService.h"
+
+namespace Swift {
+	class AvahiDNSSDService : public DNSSDService, public EventOwner, public boost::enable_shared_from_this<AvahiDNSSDService> {
+		public:
+			AvahiDNSSDService();
+			~AvahiDNSSDService();
+
+			virtual void start();
+			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&);
+			virtual void stopResolvingService(const Service&);
+
+			virtual void resolveHostname(const String& hostname, int interfaceIndex = 0);
+		
+		private:
+			static void handleServiceDiscoveredGlobal(AvahiServiceBrowser *b, AvahiIfIndex networkInterface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AvahiLookupResultFlags flags, void* userdata) {
+				static_cast<AvahiDNSSDService*>(userdata)->handleServiceDiscovered(b, networkInterface, protocol, event, name, type, domain, flags);
+			}
+			void handleServiceDiscovered(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AVAHI_GCC_UNUSED AvahiLookupResultFlags flags);
+
+			static void handleServiceResolvedGlobal(AvahiServiceResolver *r, AvahiIfIndex interfaceIndex, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *hostname, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void* userdata) {
+				static_cast<AvahiDNSSDService*>(userdata)->handleServiceResolved(r, interfaceIndex, protocol, event, name, type, domain, hostname, address, port, txt, flags);
+			}
+			void handleServiceResolved(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *hostname, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags);
+
+
+		private:
+			AvahiClient* client;
+			AvahiThreadedPoll* threadedPoll;
+			AvahiServiceBrowser* serviceBrowser;
+			typedef std::map<Service, AvahiServiceResolver*> ServiceResolverMap;
+			ServiceResolverMap serviceResolvers;
+			typedef std::map<String, HostAddress> HostnameAddressMap;
+			HostnameAddressMap hostnameAddresses;
+	};
+}
diff --git a/Swiften/LinkLocal/Makefile.inc b/Swiften/LinkLocal/Makefile.inc
index c24f2b8..788c000 100644
--- a/Swiften/LinkLocal/Makefile.inc
+++ b/Swiften/LinkLocal/Makefile.inc
@@ -10,5 +10,9 @@ ifeq ($(MACOSX),1)
 SWIFTEN_SOURCES += \
 	Swiften/LinkLocal/AppleDNSSDService.cpp
 endif
+ifeq ($(HAVE_AVAHI),yes)
+SWIFTEN_SOURCES += \
+	Swiften/LinkLocal/AvahiDNSSDService.cpp
+endif
 
 include Swiften/LinkLocal/UnitTest/Makefile.inc
-- 
cgit v0.10.2-6-g49f6