From 936f7ff9b80aca95040301a4b3cfcd2a248e3334 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Mon, 8 Feb 2010 17:48:54 +0100
Subject: Added beginnings of Avahi support.


diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiBrowseQuery.h b/Swiften/LinkLocal/DNSSD/Avahi/AvahiBrowseQuery.h
new file mode 100644
index 0000000..229f97e
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiBrowseQuery.h
@@ -0,0 +1,64 @@
+#pragma once
+
+#include <boost/bind.hpp>
+
+#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+
+namespace Swift {
+	class AvahiQuerier;
+
+	class AvahiBrowseQuery : public DNSSDBrowseQuery, public AvahiQuery {
+		public:	
+			AvahiBrowseQuery(boost::shared_ptr<AvahiQuerier> q) : AvahiQuery(q) {
+			}
+
+			void startBrowsing() {
+				std::cout << "Start browsing" << std::endl;
+				avahi_threaded_poll_lock(querier->getThreadedPoll());
+				std::cout << "Creating browser" << std::endl;
+				AvahiServiceBrowser* browser = avahi_service_browser_new(querier->getClient(), AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_presence._tcp", NULL, (AvahiLookupFlags) 0, &handleServiceDiscoveredStatic, this);
+				if (!browser) {
+					std::cout << "Error" << std::endl;
+					MainEventLoop::postEvent(boost::bind(boost::ref(onError)), shared_from_this());
+				}
+				std::cout << "Unlocking" << std::endl;
+				avahi_threaded_poll_unlock(querier->getThreadedPoll());
+				std::cout << "Browse started" << std::endl;
+			}
+
+			void stopBrowsing() {
+				// TODO
+			}
+
+		private:
+			static void handleServiceDiscoveredStatic(AvahiServiceBrowser *b, AvahiIfIndex interfaceIndex, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AvahiLookupResultFlags flags, void* context) {
+				static_cast<AvahiBrowseQuery*>(context)->handleServiceDiscovered(b, interfaceIndex, protocol, event, name, type, domain, flags);
+			}
+
+			void handleServiceDiscovered(AvahiServiceBrowser *, AvahiIfIndex interfaceIndex, AvahiProtocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AvahiLookupResultFlags) {
+				switch (event) {
+					case AVAHI_BROWSER_FAILURE:	
+						std::cout << "Service browse error" << std::endl;
+						MainEventLoop::postEvent(boost::bind(boost::ref(onError)), shared_from_this());
+						break;
+					case AVAHI_BROWSER_NEW: {
+						DNSSDServiceID service(name, domain, type, interfaceIndex);
+						std::cout << "Service discovered " << name << " " << type << " " << domain << std::endl;
+						MainEventLoop::postEvent(boost::bind(boost::ref(onServiceAdded), service), shared_from_this());
+						break;
+					}
+					case AVAHI_BROWSER_REMOVE: {
+						std::cout << "Service went away " << name << " " << type << " " << domain << std::endl;
+						DNSSDServiceID service(name, domain, type, 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;
+				}
+			}
+	};
+}
diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.cpp b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.cpp
index 55ccede..c4bfcb4 100644
--- a/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.cpp
+++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.cpp
@@ -2,11 +2,10 @@
 
 #include <iostream>
 
-//#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiBrowseQuery.h"
-//#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiRegisterQuery.h"
-//#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveServiceQuery.h"
-//#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveHostnameQuery.h"
-//#include "Swiften/Base/foreach.h"
+#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiBrowseQuery.h"
+#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveServiceQuery.h"
+#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveHostnameQuery.h"
+#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiRegisterQuery.h"
 
 namespace Swift {
 
@@ -17,22 +16,23 @@ AvahiQuerier::~AvahiQuerier() {
 }
 
 boost::shared_ptr<DNSSDBrowseQuery> AvahiQuerier::createBrowseQuery() {
-	//return boost::shared_ptr<DNSSDBrowseQuery>(new AvahiBrowseQuery(shared_from_this()));
+	return boost::shared_ptr<DNSSDBrowseQuery>(new AvahiBrowseQuery(shared_from_this()));
 }
 
 boost::shared_ptr<DNSSDRegisterQuery> AvahiQuerier::createRegisterQuery(const String& name, int port, const ByteArray& info) {
-	//return boost::shared_ptr<DNSSDRegisterQuery>(new AvahiRegisterQuery(name, port, info, shared_from_this()));
+	return boost::shared_ptr<DNSSDRegisterQuery>(new AvahiRegisterQuery(name, port, info, shared_from_this()));
 }
 
 boost::shared_ptr<DNSSDResolveServiceQuery> AvahiQuerier::createResolveServiceQuery(const DNSSDServiceID& service) {
-	//return boost::shared_ptr<DNSSDResolveServiceQuery>(new AvahiResolveServiceQuery(service, shared_from_this()));
+	return boost::shared_ptr<DNSSDResolveServiceQuery>(new AvahiResolveServiceQuery(service, shared_from_this()));
 }
 
 boost::shared_ptr<DNSSDResolveHostnameQuery> AvahiQuerier::createResolveHostnameQuery(const String& hostname, int interfaceIndex) {
-	//return boost::shared_ptr<DNSSDResolveHostnameQuery>(new AvahiResolveHostnameQuery(hostname, interfaceIndex, shared_from_this()));
+	return boost::shared_ptr<DNSSDResolveHostnameQuery>(new AvahiResolveHostnameQuery(hostname, interfaceIndex, shared_from_this()));
 }
 
 void AvahiQuerier::start() {
+	std::cout << "Starrting querier" << std::endl;
 	assert(!threadedPoll);
 	threadedPoll = avahi_threaded_poll_new();
 	int error;
@@ -45,6 +45,7 @@ void AvahiQuerier::start() {
 		std::cerr << "Avahi Error: " << avahi_strerror(error) << std::endl;
 		return;
 	}
+	std::cout << "Starrting event loop" << std::endl;
 	avahi_threaded_poll_start(threadedPoll);
 }
 
diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.h b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.h
index ca45384..ffb8441 100644
--- a/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.h
+++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.h
@@ -31,6 +31,14 @@ namespace Swift {
 
 			void start();
 			void stop();
+			
+			AvahiThreadedPoll* getThreadedPoll() const {
+				return threadedPoll;
+			}
+
+			AvahiClient* getClient() const {
+				return client;
+			}
 
 		private:
 			AvahiClient* client;
diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.cpp b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.cpp
new file mode 100644
index 0000000..6b7c7f9
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.cpp
@@ -0,0 +1,13 @@
+#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h"
+#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.h"
+
+namespace Swift {
+
+AvahiQuery::AvahiQuery(boost::shared_ptr<AvahiQuerier> q) : querier(q) {
+}
+
+AvahiQuery::~AvahiQuery() {
+}
+
+}
+
diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h
new file mode 100644
index 0000000..847f3f7
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+#include "Swiften/EventLoop/EventOwner.h"
+
+namespace Swift {
+	class AvahiQuerier;
+
+	class AvahiQuery : 
+			public EventOwner,
+			public boost::enable_shared_from_this<AvahiQuery> {
+		public:
+			AvahiQuery(boost::shared_ptr<AvahiQuerier>);
+			virtual ~AvahiQuery();
+			
+		protected:
+			boost::shared_ptr<AvahiQuerier> querier;
+	};
+}
+
diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiRegisterQuery.h b/Swiften/LinkLocal/DNSSD/Avahi/AvahiRegisterQuery.h
new file mode 100644
index 0000000..102db1b
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiRegisterQuery.h
@@ -0,0 +1,123 @@
+#pragma once
+
+#include <avahi-client/publish.h>
+
+#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h"
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+
+namespace Swift {
+	class AvahiQuerier;
+
+	class AvahiRegisterQuery : public DNSSDRegisterQuery, public AvahiQuery {
+		public:	
+			AvahiRegisterQuery(const String& name, int port, const ByteArray& txtRecord, boost::shared_ptr<AvahiQuerier> querier) : AvahiQuery(querier), name(name), port(port), txtRecord(txtRecord), group(0) {
+			}
+
+			void registerService() {
+				std::cout << "Registering service " << name << ":" << port << std::endl;
+				avahi_threaded_poll_lock(querier->getThreadedPoll());
+				if (!group) {
+					std::cout << "Creating entry group" << std::endl;
+					group = avahi_entry_group_new(querier->getClient(), handleEntryGroupChange, this);
+					if (!group) {
+						std::cout << "Error ceating entry group" << std::endl;
+						MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this());
+					}
+				}
+
+				doRegisterService();
+				avahi_threaded_poll_unlock(querier->getThreadedPoll());
+			}
+
+			void unregisterService() {
+			}
+
+			void updateServiceInfo(const ByteArray& txtRecord) {
+				this->txtRecord = txtRecord;
+				avahi_threaded_poll_lock(querier->getThreadedPoll());
+				assert(group);
+				avahi_entry_group_reset(group);
+				doRegisterService();
+				avahi_threaded_poll_unlock(querier->getThreadedPoll());
+			}
+
+		private:
+			void doRegisterService() {
+				AvahiStringList* txtList;
+				avahi_string_list_parse(txtRecord.getData(), txtRecord.getSize(), &txtList);
+
+				int result = avahi_entry_group_add_service_strlst(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, (AvahiPublishFlags) 0, name.getUTF8Data(), "_presence._tcp", NULL, NULL, port, txtList);
+				if (result < 0) {
+					std::cout << "Error registering service: " << avahi_strerror(result) << std::endl;
+					MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this());
+				}
+				result = avahi_entry_group_commit(group);
+				if (result < 0) {
+					std::cout << "Error registering service: " << avahi_strerror(result) << std::endl;
+				}
+			}
+
+			static void handleEntryGroupChange(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
+				static_cast<AvahiRegisterQuery*>(userdata)->handleEntryGroupChange(g, state);
+			}
+
+			void handleEntryGroupChange(AvahiEntryGroup* g, AvahiEntryGroupState state) {
+				std::cout << "ENtry group callback: " << state << std::endl;
+				switch (state) {
+					case AVAHI_ENTRY_GROUP_ESTABLISHED :
+						// Domain is a hack!
+						MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>(DNSSDServiceID(name, "local", "_presence._tcp", 0))), shared_from_this());
+						std::cout << "Entry group established" << std::endl;
+            break;
+        case AVAHI_ENTRY_GROUP_COLLISION : {
+						std::cout << "Entry group collision" << std::endl;
+            /*char *n;
+            n = avahi_alternative_service_name(name);
+            avahi_free(name);
+            name = n;*/
+            break;
+        }
+
+        case AVAHI_ENTRY_GROUP_FAILURE :
+						std::cout << "Entry group failure " << avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g))) << std::endl;
+            break;
+
+        case AVAHI_ENTRY_GROUP_UNCOMMITED:
+        case AVAHI_ENTRY_GROUP_REGISTERING:
+            ;
+
+				/*
+				DNSServiceErrorType result = DNSServiceRegister(
+						&sdRef, 0, 0, name.getUTF8Data(), "_presence._tcp", NULL, NULL, port, 
+						txtRecord.getSize(), txtRecord.getData(), 
+						&AvahiRegisterQuery::handleServiceRegisteredStatic, this);
+				if (result != kDNSServiceErr_NoError) {
+					sdRef = NULL;
+				}*/
+				//MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this());
+			}
+		}
+
+/*
+			static void handleServiceRegisteredStatic(DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, void *context) {
+				static_cast<AvahiRegisterQuery*>(context)->handleServiceRegistered(errorCode, name, regtype, domain);
+			}
+
+			void handleServiceRegistered(DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain) {
+				if (errorCode != kDNSServiceErr_NoError) {
+					MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this());
+				}
+				else {
+				}
+			}
+			*/
+
+		private:
+			String name;
+			int port;
+			ByteArray txtRecord;
+			AvahiEntryGroup* group;
+	};
+}
diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveHostnameQuery.h b/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveHostnameQuery.h
new file mode 100644
index 0000000..ee0e837
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveHostnameQuery.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include "Swiften/Base/String.h"
+#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+#include "Swiften/Network/HostAddress.h"
+
+#include <netinet/in.h>
+
+namespace Swift {
+	class AvahiQuerier;
+
+	class AvahiResolveHostnameQuery : public DNSSDResolveHostnameQuery, public AvahiQuery {
+		public: 
+			AvahiResolveHostnameQuery(const String& hostname, int, boost::shared_ptr<AvahiQuerier> querier) : AvahiQuery(querier), hostname(hostname) {
+				std::cout << "Resolving hostname " << hostname << std::endl;
+			}
+
+			void run() {
+					MainEventLoop::postEvent(boost::bind(boost::ref(onHostnameResolved), boost::optional<HostAddress>(HostAddress(hostname))), shared_from_this());
+			}
+
+			void finish() {
+			}
+
+		private:
+			HostAddress hostAddress;
+			String hostname;
+	};
+}
diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveServiceQuery.h b/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveServiceQuery.h
new file mode 100644
index 0000000..8577837
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveServiceQuery.h
@@ -0,0 +1,73 @@
+#pragma once
+
+#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h"
+#include "Swiften/LinkLocal/LinkLocalServiceInfo.h"
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+
+namespace Swift {
+	class AvahiQuerier;
+
+	class AvahiResolveServiceQuery : public DNSSDResolveServiceQuery, public AvahiQuery {
+		public:	
+			AvahiResolveServiceQuery(const DNSSDServiceID& service, boost::shared_ptr<AvahiQuerier> querier) : AvahiQuery(querier), service(service), resolver(NULL) {
+			}
+
+			void start() {
+				std::cout << "Resolving " << service.getName() << std::endl;
+				avahi_threaded_poll_lock(querier->getThreadedPoll());
+				assert(!resolver);
+				resolver = avahi_service_resolver_new(querier->getClient(), service.getNetworkInterfaceID(), AVAHI_PROTO_UNSPEC, service.getName().getUTF8Data(), service.getType().getUTF8Data(), service.getDomain().getUTF8Data(), AVAHI_PROTO_UNSPEC, (AvahiLookupFlags) 0, handleServiceResolvedStatic, this);
+				if (!resolver) {
+					std::cout << "Error starting resolver" << std::endl;
+					MainEventLoop::postEvent(boost::bind(boost::ref(onServiceResolved), boost::optional<Result>()), shared_from_this());
+				}
+				avahi_threaded_poll_unlock(querier->getThreadedPoll());
+			}
+
+			void stop() {
+				avahi_threaded_poll_lock(querier->getThreadedPoll());
+				avahi_service_resolver_free(resolver);
+				resolver = NULL;
+				avahi_threaded_poll_unlock(querier->getThreadedPoll());
+			}
+
+		private:
+			static void handleServiceResolvedStatic(AvahiServiceResolver* resolver, AvahiIfIndex interfaceIndex, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void* context) {
+				static_cast<AvahiResolveServiceQuery*>(context)->handleServiceResolved(resolver, interfaceIndex, protocol, event, name, type, domain, host_name, address, port, txt, flags);
+			}
+
+			void handleServiceResolved(AvahiServiceResolver* resolver, AvahiIfIndex, AvahiProtocol, AvahiResolverEvent event, const char *name, const char * type, const char* domain, const char * /*host_name*/, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags) {
+				std::cout << "Resolve finished" << std::endl;
+				switch(event) {
+					case AVAHI_RESOLVER_FAILURE:
+						std::cout << "Resolve error " << avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(resolver))) << std::endl;
+						MainEventLoop::postEvent(boost::bind(boost::ref(onServiceResolved), boost::optional<Result>()), shared_from_this());
+						break;
+					case AVAHI_RESOLVER_FOUND: {
+						char a[AVAHI_ADDRESS_STR_MAX];
+						avahi_address_snprint(a, sizeof(a), address);
+
+						ByteArray txtRecord;
+						txtRecord.resize(1024);
+						avahi_string_list_serialize(txt, txtRecord.getData(), txtRecord.getSize());
+
+						// FIXME: Probably not accurate
+						String fullname = String(name) + "." + String(type) + "." + String(domain) + ".";
+						std::cout << "Result: " << fullname << "->" << String(a) << ":" << port << std::endl;
+						MainEventLoop::postEvent(
+								boost::bind(
+									boost::ref(onServiceResolved), 
+									Result(fullname, String(a), port, txtRecord)),
+								shared_from_this());
+						break;
+					}
+				}
+			}
+
+		private:
+			DNSSDServiceID service;
+			AvahiServiceResolver* resolver;
+	};
+}
diff --git a/Swiften/LinkLocal/SConscript b/Swiften/LinkLocal/SConscript
index f36d467..b929db1 100644
--- a/Swiften/LinkLocal/SConscript
+++ b/Swiften/LinkLocal/SConscript
@@ -28,7 +28,10 @@ if myenv.get("HAVE_BONJOUR", 0) :
 			"DNSSD/Bonjour/BonjourQuery.cpp",
 		]
 elif myenv.get("HAVE_AVAHI", 0) :
-	sources += ["DNSSD/Avahi/AvahiQuerier.cpp"]
+	sources += [
+			"DNSSD/Avahi/AvahiQuerier.cpp",
+			"DNSSD/Avahi/AvahiQuery.cpp"
+		]
 
 objects = myenv.StaticObject(sources)
 swiften_env.Append(SWIFTEN_OBJECTS = [objects])
-- 
cgit v0.10.2-6-g49f6