From 26d623d3cfd8937fb52acf76ef33d230f5010538 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Sun, 26 Jul 2009 10:21:20 +0200
Subject: Implement fake DNSSD querier.


diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourBrowseQuery.h b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourBrowseQuery.h
index 62d8606..8e3181e 100644
--- a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourBrowseQuery.h
+++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourBrowseQuery.h
@@ -14,14 +14,17 @@ namespace Swift {
 						&sdRef, 0, 0, "_presence._tcp", 0, 
 						&BonjourBrowseQuery::handleServiceDiscoveredStatic, this);
 				if (result != kDNSServiceErr_NoError) {
-					std::cout << "Error" << std::endl;
-					// TODO
+					sdRef = NULL;
 				}
 			}
 
 			void startBrowsing() {
-				assert(sdRef);
-				run();
+				if (!sdRef) {
+					MainEventLoop::postEvent(boost::bind(boost::ref(onError)), shared_from_this());
+				}
+				else {
+					run();
+				}
 			}
 
 			void stopBrowsing() {
@@ -35,7 +38,7 @@ namespace Swift {
 
 			void handleServiceDiscovered(DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *name, const char *type, const char *domain) {
 				if (errorCode != kDNSServiceErr_NoError) {
-					return;
+					MainEventLoop::postEvent(boost::bind(boost::ref(onError)), shared_from_this());
 				}
 				else {
 					DNSSDServiceID service(name, type, domain, interfaceIndex);
diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.cpp b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.cpp
index c065d4d..70fbc7c 100644
--- a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.cpp
+++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.cpp
@@ -22,7 +22,7 @@ BonjourQuerier::BonjourQuerier() : stopRequested(false), thread(0) {
 }
 
 BonjourQuerier::~BonjourQuerier() {
-	stop();
+	assert(!thread);
 }
 
 boost::shared_ptr<DNSSDBrowseQuery> BonjourQuerier::createBrowseQuery() {
@@ -64,18 +64,19 @@ void BonjourQuerier::interruptSelect() {
 }
 
 void BonjourQuerier::start() {
-	stop();
+	assert(!thread);
 	thread = new boost::thread(boost::bind(&BonjourQuerier::run, shared_from_this()));
 }
 
 void BonjourQuerier::stop() {
 	if (thread) {
 		stopRequested = true;
-		runningQueries.clear(); // TODO: Is this the right thing to do?
+		assert(runningQueries.empty());
 		runningQueriesAvailableEvent.notify_one();
 		interruptSelect();
 		thread->join();
 		delete thread;
+		thread = NULL;
 		stopRequested = false;
 	}
 }
diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourRegisterQuery.h b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourRegisterQuery.h
index 9c4db13..41e5831 100644
--- a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourRegisterQuery.h
+++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourRegisterQuery.h
@@ -18,13 +18,21 @@ namespace Swift {
 						txtRecord.getSize(), txtRecord.getData(), 
 						&BonjourRegisterQuery::handleServiceRegisteredStatic, this);
 				if (result != kDNSServiceErr_NoError) {
-					// TODO
-					std::cerr << "Error creating service registration" << std::endl;
+					sdRef = NULL;
 				}
 			}
 
 			void registerService() {
-				run();
+				if (sdRef) {
+					run();
+				}
+				else {
+					MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this());
+				}
+			}
+
+			void unregisterService() {
+				stop();
 			}
 
 			void updateServiceInfo(const LinkLocalServiceInfo& info) {
diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveHostnameQuery.h b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveHostnameQuery.h
index 58c6588..6e2a852 100644
--- a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveHostnameQuery.h
+++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveHostnameQuery.h
@@ -19,13 +19,18 @@ namespace Swift {
 						hostname.getUTF8Data(), 
 						&BonjourResolveHostnameQuery::handleHostnameResolvedStatic, this);
 				if (result != kDNSServiceErr_NoError) {
-					MainEventLoop::postEvent(boost::bind(boost::ref(onHostnameResolved), boost::optional<HostAddress>()), shared_from_this());
+					sdRef = NULL;
 				}
 			}
 
 			//void DNSSDResolveHostnameQuery::run() {
 			void run() {
-				BonjourQuery::run();
+				if (sdRef) {
+					BonjourQuery::run();
+				}
+				else {
+					MainEventLoop::postEvent(boost::bind(boost::ref(onHostnameResolved), boost::optional<HostAddress>()), shared_from_this());
+				}
 			}
 
 		private:
diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveServiceQuery.h b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveServiceQuery.h
index 1ebd487..8b3f7ec 100644
--- a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveServiceQuery.h
+++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveServiceQuery.h
@@ -18,12 +18,17 @@ namespace Swift {
 						service.getDomain().getUTF8Data(), 
 						&BonjourResolveServiceQuery::handleServiceResolvedStatic, this);
 				if (result != kDNSServiceErr_NoError) {
-					MainEventLoop::postEvent(boost::bind(boost::ref(onServiceResolved), boost::optional<Result>()), shared_from_this());
+					sdRef = NULL;
 				}
 			}
 
 			void start() {
-				run();
+				if (sdRef) {
+					run();
+				}
+				else {
+					MainEventLoop::postEvent(boost::bind(boost::ref(onServiceResolved), boost::optional<Result>()), shared_from_this());
+				}
 			}
 
 			void stop() {
diff --git a/Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h b/Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h
index 86967ed..9b30858 100644
--- a/Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h
+++ b/Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h
@@ -14,5 +14,6 @@ namespace Swift {
 
 			boost::signal<void (const DNSSDServiceID&)> onServiceAdded;
 			boost::signal<void (const DNSSDServiceID&)> onServiceRemoved;
+			boost::signal<void ()> onError;
 	};
 }
diff --git a/Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h b/Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h
index a643880..627cc6b 100644
--- a/Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h
+++ b/Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h
@@ -13,6 +13,7 @@ namespace Swift {
 			virtual ~DNSSDRegisterQuery();
 
 			virtual void registerService() = 0;
+			virtual void unregisterService() = 0;
 			virtual void updateServiceInfo(const LinkLocalServiceInfo& info) = 0;
 
 			boost::signal<void (boost::optional<DNSSDServiceID>)> onRegisterFinished;
diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDBrowseQuery.h b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDBrowseQuery.h
new file mode 100644
index 0000000..5a0b93b
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDBrowseQuery.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h"
+
+namespace Swift {
+	class FakeDNSSDQuerier;
+
+	class FakeDNSSDBrowseQuery : public DNSSDBrowseQuery, public FakeDNSSDQuery {
+		public:	
+			FakeDNSSDBrowseQuery(boost::shared_ptr<FakeDNSSDQuerier> querier) : FakeDNSSDQuery(querier) {
+			}
+
+			void startBrowsing() {
+				run();
+			}
+
+			void stopBrowsing() {
+				finish();
+			}
+	};
+}
diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.cpp b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.cpp
new file mode 100644
index 0000000..222500a
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.cpp
@@ -0,0 +1,92 @@
+#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDBrowseQuery.h"
+#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDRegisterQuery.h"
+#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveServiceQuery.h"
+#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveHostnameQuery.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+
+namespace Swift {
+
+FakeDNSSDQuerier::FakeDNSSDQuerier() {
+}
+
+boost::shared_ptr<DNSSDBrowseQuery> FakeDNSSDQuerier::createBrowseQuery() {
+	return boost::shared_ptr<DNSSDBrowseQuery>(new FakeDNSSDBrowseQuery(shared_from_this()));
+}
+
+boost::shared_ptr<DNSSDRegisterQuery> FakeDNSSDQuerier::createRegisterQuery(const String& name, int port, const LinkLocalServiceInfo& info) {
+	return boost::shared_ptr<DNSSDRegisterQuery>(new FakeDNSSDRegisterQuery(name, port, info, shared_from_this()));
+}
+
+boost::shared_ptr<DNSSDResolveServiceQuery> FakeDNSSDQuerier::createResolveServiceQuery(const DNSSDServiceID& service) {
+	return boost::shared_ptr<DNSSDResolveServiceQuery>(new FakeDNSSDResolveServiceQuery(service, shared_from_this()));
+}
+
+boost::shared_ptr<DNSSDResolveHostnameQuery> FakeDNSSDQuerier::createResolveHostnameQuery(const String& hostname, int interfaceIndex) {
+	return boost::shared_ptr<DNSSDResolveHostnameQuery>(new FakeDNSSDResolveHostnameQuery(hostname, interfaceIndex, shared_from_this()));
+}
+
+void FakeDNSSDQuerier::addRunningQuery(boost::shared_ptr<FakeDNSSDQuery> query) {
+	runningQueries.push_back(query);
+	if (boost::shared_ptr<FakeDNSSDBrowseQuery> browseQuery = boost::dynamic_pointer_cast<FakeDNSSDBrowseQuery>(query)) {
+		foreach(const DNSSDServiceID& service, services) {
+			MainEventLoop::postEvent(boost::bind(boost::ref(browseQuery->onServiceAdded), service), shared_from_this());
+		}
+	}
+	else if (boost::shared_ptr<FakeDNSSDResolveServiceQuery> resolveQuery = boost::dynamic_pointer_cast<FakeDNSSDResolveServiceQuery>(query)) {
+		for(ServiceInfoMap::const_iterator i = serviceInfo.begin(); i != serviceInfo.end(); ++i) {
+			if (i->first == resolveQuery->service) {
+				MainEventLoop::postEvent(boost::bind(boost::ref(resolveQuery->onServiceResolved), i->second), shared_from_this());
+			}
+		}
+	}
+}
+
+void FakeDNSSDQuerier::removeRunningQuery(boost::shared_ptr<FakeDNSSDQuery> query) {
+	runningQueries.erase(std::remove(
+		runningQueries.begin(), runningQueries.end(), query), runningQueries.end());
+}
+
+void FakeDNSSDQuerier::addService(const DNSSDServiceID& id) {
+	services.insert(id);
+	foreach(const boost::shared_ptr<FakeDNSSDBrowseQuery>& query, getQueries<FakeDNSSDBrowseQuery>()) {
+		MainEventLoop::postEvent(boost::bind(boost::ref(query->onServiceAdded), id), shared_from_this());
+	}
+}
+
+void FakeDNSSDQuerier::removeService(const DNSSDServiceID& id) {
+	services.erase(id);
+	serviceInfo.erase(id);
+	foreach(const boost::shared_ptr<FakeDNSSDBrowseQuery>& query, getQueries<FakeDNSSDBrowseQuery>()) {
+		MainEventLoop::postEvent(boost::bind(boost::ref(query->onServiceRemoved), id), shared_from_this());
+	}
+}
+
+void FakeDNSSDQuerier::setServiceInfo(const DNSSDServiceID& id, const DNSSDResolveServiceQuery::Result& info) {
+	std::pair<ServiceInfoMap::iterator, bool> r = serviceInfo.insert(std::make_pair(id, info));
+	if (!r.second) {
+		r.first->second = info;
+	}
+	foreach(const boost::shared_ptr<FakeDNSSDResolveServiceQuery>& query, getQueries<FakeDNSSDResolveServiceQuery>()) {
+		if (query->service == id) {
+			MainEventLoop::postEvent(boost::bind(boost::ref(query->onServiceResolved), info), shared_from_this());
+		}
+	}
+}
+
+void FakeDNSSDQuerier::setBrowseError() {
+	foreach(const boost::shared_ptr<FakeDNSSDBrowseQuery>& query, getQueries<FakeDNSSDBrowseQuery>()) {
+		MainEventLoop::postEvent(boost::ref(query->onError), shared_from_this());
+	}
+}
+
+void FakeDNSSDQuerier::setRegisterError() {
+	foreach(const boost::shared_ptr<FakeDNSSDRegisterQuery>& query, getQueries<FakeDNSSDRegisterQuery>()) {
+		MainEventLoop::postEvent(boost::bind(boost::ref(query->onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this());
+	}
+}
+
+}
diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h
new file mode 100644
index 0000000..6c5f343
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h
@@ -0,0 +1,61 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <list>
+#include <set>
+
+#include "Swiften/Base/foreach.h"
+#include "Swiften/EventLoop/EventOwner.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDQuerier.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h"
+
+namespace Swift {
+	class LinkLocalServiceInfo;
+	class FakeDNSSDQuery;
+	class FakeDNSSDBrowseQuery;
+
+	class FakeDNSSDQuerier : 
+			public DNSSDQuerier, 
+			public EventOwner,
+			public boost::enable_shared_from_this<FakeDNSSDQuerier> {
+		public:
+			FakeDNSSDQuerier();
+
+			boost::shared_ptr<DNSSDBrowseQuery> createBrowseQuery();
+			boost::shared_ptr<DNSSDRegisterQuery> createRegisterQuery(
+					const String& name, int port, const LinkLocalServiceInfo& info);
+			boost::shared_ptr<DNSSDResolveServiceQuery> createResolveServiceQuery(
+					const DNSSDServiceID&);
+			boost::shared_ptr<DNSSDResolveHostnameQuery> createResolveHostnameQuery(
+					const String& hostname, int interfaceIndex);
+
+			void addRunningQuery(boost::shared_ptr<FakeDNSSDQuery>);
+			void removeRunningQuery(boost::shared_ptr<FakeDNSSDQuery>);
+
+			void addService(const DNSSDServiceID& id);
+			void removeService(const DNSSDServiceID& id);
+			void setServiceInfo(const DNSSDServiceID& id, const DNSSDResolveServiceQuery::Result& info);
+
+			void setBrowseError();
+			void setRegisterError();
+
+		private:
+			template<typename T>
+			std::vector< boost::shared_ptr<T> > getQueries() const {
+				std::vector< boost::shared_ptr<T> > result;
+				foreach(const boost::shared_ptr<FakeDNSSDQuery>& query, runningQueries) {
+					if (boost::shared_ptr<T> resultQuery = boost::dynamic_pointer_cast<T>(query)) {
+						result.push_back(resultQuery);
+					}
+				}
+				return result;
+			}
+
+		private:
+			std::list< boost::shared_ptr<FakeDNSSDQuery> > runningQueries;
+			std::set<DNSSDServiceID> services;
+			typedef std::map<DNSSDServiceID,DNSSDResolveServiceQuery::Result> ServiceInfoMap;
+			ServiceInfoMap serviceInfo;
+	};
+}
diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.cpp b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.cpp
new file mode 100644
index 0000000..ced7850
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.cpp
@@ -0,0 +1,20 @@
+#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h"
+#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h"
+
+namespace Swift {
+
+FakeDNSSDQuery::FakeDNSSDQuery(boost::shared_ptr<FakeDNSSDQuerier> querier) : querier(querier) {
+}
+
+FakeDNSSDQuery::~FakeDNSSDQuery() {
+}
+
+void FakeDNSSDQuery::run() {
+	querier->addRunningQuery(shared_from_this());
+}
+
+void FakeDNSSDQuery::finish() {
+	querier->removeRunningQuery(shared_from_this());
+}
+
+}
diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h
new file mode 100644
index 0000000..9fca1d4
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+#include "Swiften/EventLoop/EventOwner.h"
+
+namespace Swift {
+	class FakeDNSSDQuerier;
+
+	class FakeDNSSDQuery : 
+			public EventOwner,
+			public boost::enable_shared_from_this<FakeDNSSDQuery> {
+		public:
+			FakeDNSSDQuery(boost::shared_ptr<FakeDNSSDQuerier>);
+			virtual ~FakeDNSSDQuery();
+			
+		protected:
+			void run();
+			void finish();
+		
+		protected:
+			boost::shared_ptr<FakeDNSSDQuerier> querier;
+	};
+}
diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDRegisterQuery.h b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDRegisterQuery.h
new file mode 100644
index 0000000..b4646fd
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDRegisterQuery.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h"
+#include "Swiften/LinkLocal/LinkLocalServiceInfo.h"
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+	class FakeDNSSDQuerier;
+
+	class FakeDNSSDRegisterQuery : public DNSSDRegisterQuery, public FakeDNSSDQuery {
+		public:	
+			FakeDNSSDRegisterQuery(const String& name, int port, const LinkLocalServiceInfo& info, boost::shared_ptr<FakeDNSSDQuerier> querier) : FakeDNSSDQuery(querier), name(name), port(port), info(info) {
+			}
+
+			void registerService() {
+				run();
+			}
+
+			void updateServiceInfo(const LinkLocalServiceInfo&) {
+			}
+
+			void unregisterService() {
+				finish();
+			}
+
+			String name;
+			int port;
+			LinkLocalServiceInfo info;
+	};
+}
diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveHostnameQuery.h b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveHostnameQuery.h
new file mode 100644
index 0000000..2ff84d3
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveHostnameQuery.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "Swiften/Base/String.h"
+#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h"
+#include "Swiften/Network/HostAddress.h"
+
+#include <netinet/in.h>
+
+namespace Swift {
+	class FakeDNSSDQuerier;
+
+	class FakeDNSSDResolveHostnameQuery : public DNSSDResolveHostnameQuery, public FakeDNSSDQuery {
+		public:	
+			FakeDNSSDResolveHostnameQuery(const String& hostname, int interfaceIndex, boost::shared_ptr<FakeDNSSDQuerier> querier) : FakeDNSSDQuery(querier), hostname(hostname), interfaceIndex(interfaceIndex) {
+			}
+
+			void run() {
+				FakeDNSSDQuery::run();
+			}
+
+			String hostname;
+			int interfaceIndex;
+	};
+}
diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveServiceQuery.h b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveServiceQuery.h
new file mode 100644
index 0000000..60d35e5
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveServiceQuery.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h"
+#include "Swiften/LinkLocal/LinkLocalServiceInfo.h"
+
+namespace Swift {
+	class FakeDNSSDQuerier;
+
+	class FakeDNSSDResolveServiceQuery : public DNSSDResolveServiceQuery, public FakeDNSSDQuery {
+		public:	
+			FakeDNSSDResolveServiceQuery(const DNSSDServiceID& service, boost::shared_ptr<FakeDNSSDQuerier> querier) : FakeDNSSDQuery(querier), service(service) {
+			}
+
+			void start() {
+				run();
+			}
+
+			void stop() {
+				finish();
+			}
+
+			DNSSDServiceID service;
+	};
+}
diff --git a/Swiften/LinkLocal/DNSSD/Fake/Makefile.inc b/Swiften/LinkLocal/DNSSD/Fake/Makefile.inc
new file mode 100644
index 0000000..316d061
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Fake/Makefile.inc
@@ -0,0 +1,3 @@
+SWIFTEN_SOURCES += \
+	Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.cpp \
+	Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.cpp
diff --git a/Swiften/LinkLocal/DNSSD/Makefile.inc b/Swiften/LinkLocal/DNSSD/Makefile.inc
index 8612fee..3d2c969 100644
--- a/Swiften/LinkLocal/DNSSD/Makefile.inc
+++ b/Swiften/LinkLocal/DNSSD/Makefile.inc
@@ -11,3 +11,5 @@ include Swiften/LinkLocal/DNSSD/Bonjour/Makefile.inc
 endif
 ifeq ($(HAVE_AVAHI),yes)
 endif
+
+include Swiften/LinkLocal/DNSSD/Fake/Makefile.inc
diff --git a/Swiften/LinkLocal/LinkLocalServiceBrowser.cpp b/Swiften/LinkLocal/LinkLocalServiceBrowser.cpp
index 4e9c2de..aed917a 100644
--- a/Swiften/LinkLocal/LinkLocalServiceBrowser.cpp
+++ b/Swiften/LinkLocal/LinkLocalServiceBrowser.cpp
@@ -7,17 +7,66 @@
 
 namespace Swift {
 
-LinkLocalServiceBrowser::LinkLocalServiceBrowser(boost::shared_ptr<DNSSDQuerier> querier) : querier(querier) {
+LinkLocalServiceBrowser::LinkLocalServiceBrowser(boost::shared_ptr<DNSSDQuerier> querier) : querier(querier), haveError(false) {
+}
+
+LinkLocalServiceBrowser::~LinkLocalServiceBrowser() {
+	assert(!isRunning());
+}
+
+
+void LinkLocalServiceBrowser::start() {
+	assert(!isRunning());
+	haveError = false;
 	browseQuery = querier->createBrowseQuery();
 	browseQuery->onServiceAdded.connect(
 			boost::bind(&LinkLocalServiceBrowser::handleServiceAdded, this, _1));
 	browseQuery->onServiceRemoved.connect(
 			boost::bind(&LinkLocalServiceBrowser::handleServiceRemoved, this, _1));
+	browseQuery->onError.connect(
+			boost::bind(&LinkLocalServiceBrowser::handleBrowseError, this));
 	browseQuery->startBrowsing();
 }
 
-LinkLocalServiceBrowser::~LinkLocalServiceBrowser() {
+void LinkLocalServiceBrowser::stop() {
+	assert(isRunning());
+	if (isRegistered()) {
+		unregisterService();
+	}
+	for (ResolveQueryMap::const_iterator i = resolveQueries.begin(); i != resolveQueries.end(); ++i) {
+		i->second->stop();
+	}
+	resolveQueries.clear();
+	services.clear();
 	browseQuery->stopBrowsing();
+	browseQuery.reset();
+	onStopped(haveError);
+}
+
+bool LinkLocalServiceBrowser::isRunning() const {
+	return browseQuery;
+}
+
+bool LinkLocalServiceBrowser::hasError() const {
+	return haveError;
+}
+
+bool LinkLocalServiceBrowser::isRegistered() const {
+	return registerQuery;
+}
+
+void LinkLocalServiceBrowser::registerService(const String& name, int port, const LinkLocalServiceInfo& info) {
+	assert(!registerQuery);
+	registerQuery = querier->createRegisterQuery(name, port, info);
+	registerQuery->onRegisterFinished.connect(
+		boost::bind(&LinkLocalServiceBrowser::handleRegisterFinished, this, _1));
+	registerQuery->registerService();
+}
+
+void LinkLocalServiceBrowser::unregisterService() {
+	assert(registerQuery);
+	registerQuery->unregisterService();
+	registerQuery.reset();
 }
 
 std::vector<LinkLocalService> LinkLocalServiceBrowser::getServices() const {
@@ -44,6 +93,7 @@ void LinkLocalServiceBrowser::handleServiceRemoved(const DNSSDServiceID& service
 	assert(i != resolveQueries.end());
 	i->second->stop();
 	resolveQueries.erase(i);
+	services.erase(service);
 	onServiceRemoved(service);
 }
 
@@ -60,4 +110,16 @@ void LinkLocalServiceBrowser::handleServiceResolved(const DNSSDServiceID& servic
 	}
 }
 
+void LinkLocalServiceBrowser::handleRegisterFinished(const boost::optional<DNSSDServiceID>& result) {
+	if (!result) {
+		haveError = true;
+		stop();
+	}
+}
+
+void LinkLocalServiceBrowser::handleBrowseError() {
+	haveError = true;
+	stop();
+}
+
 }
diff --git a/Swiften/LinkLocal/LinkLocalServiceBrowser.h b/Swiften/LinkLocal/LinkLocalServiceBrowser.h
index 7ec796a..33c9858 100644
--- a/Swiften/LinkLocal/LinkLocalServiceBrowser.h
+++ b/Swiften/LinkLocal/LinkLocalServiceBrowser.h
@@ -9,8 +9,10 @@
 #include "Swiften/Base/String.h"
 #include "Swiften/LinkLocal/DNSSD/DNSSDQuerier.h"
 #include "Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h"
 #include "Swiften/LinkLocal/DNSSD/DNSSDServiceID.h"
 #include "Swiften/LinkLocal/LinkLocalService.h"
+#include "Swiften/LinkLocal/LinkLocalServiceInfo.h"
 
 namespace Swift {
 	class LinkLocalServiceBrowser {
@@ -18,23 +20,37 @@ namespace Swift {
 			LinkLocalServiceBrowser(boost::shared_ptr<DNSSDQuerier> querier);
 			~LinkLocalServiceBrowser();
 
+			void start();
+			void stop();
+			bool isRunning() const;
+			bool hasError() const;
+
+			void registerService(const String& name, int port, const LinkLocalServiceInfo& info = LinkLocalServiceInfo());
+			void unregisterService();
+			bool isRegistered() const;
+
 			std::vector<LinkLocalService> getServices() const;
 
 			boost::signal<void (const DNSSDServiceID&)> onServiceAdded;
 			boost::signal<void (const DNSSDServiceID&)> onServiceChanged;
 			boost::signal<void (const DNSSDServiceID&)> onServiceRemoved;
+			boost::signal<void (bool)> onStopped;
 
 		private:
 			void handleServiceAdded(const DNSSDServiceID&);
 			void handleServiceRemoved(const DNSSDServiceID&);
 			void handleServiceResolved(const DNSSDServiceID& service, const boost::optional<DNSSDResolveServiceQuery::Result>& result);
+			void handleRegisterFinished(const boost::optional<DNSSDServiceID>&);
+			void handleBrowseError();
 
 		private:
 			boost::shared_ptr<DNSSDQuerier> querier;
 			boost::shared_ptr<DNSSDBrowseQuery> browseQuery;
+			boost::shared_ptr<DNSSDRegisterQuery> registerQuery;
 			typedef std::map<DNSSDServiceID, boost::shared_ptr<DNSSDResolveServiceQuery> > ResolveQueryMap;
 			ResolveQueryMap resolveQueries;
 			typedef std::map<DNSSDServiceID, DNSSDResolveServiceQuery::Result> ServiceMap;
 			ServiceMap services;
+			bool haveError;
 	};
 }
diff --git a/Swiften/LinkLocal/UnitTest/LinkLocalServiceBrowserTest.cpp b/Swiften/LinkLocal/UnitTest/LinkLocalServiceBrowserTest.cpp
index 34f797c..7e2dd26 100644
--- a/Swiften/LinkLocal/UnitTest/LinkLocalServiceBrowserTest.cpp
+++ b/Swiften/LinkLocal/UnitTest/LinkLocalServiceBrowserTest.cpp
@@ -5,20 +5,26 @@
 
 #include "Swiften/LinkLocal/LinkLocalServiceBrowser.h"
 #include "Swiften/LinkLocal/LinkLocalService.h"
-#include "Swiften/LinkLocal/UnitTest/MockDNSSDService.h"
-#include "Swiften/LinkLocal/DNSSDService.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDServiceID.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h"
+#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h"
 #include "Swiften/EventLoop/DummyEventLoop.h"
 
-// Test canCreate() == false
-
 using namespace Swift;
 
 class LinkLocalServiceBrowserTest : public CppUnit::TestFixture {
 		CPPUNIT_TEST_SUITE(LinkLocalServiceBrowserTest);
+		CPPUNIT_TEST(testConstructor);
+		CPPUNIT_TEST(testStart);
 		CPPUNIT_TEST(testServiceAdded);
 		CPPUNIT_TEST(testServiceAdded_NoServiceInfo);
 		CPPUNIT_TEST(testServiceChanged);
 		CPPUNIT_TEST(testServiceRemoved);
+		CPPUNIT_TEST(testError_BrowseErrorAfterStart);
+		CPPUNIT_TEST(testError_BrowseErrorAfterResolve);
+		CPPUNIT_TEST(testRegisterService);
+		CPPUNIT_TEST(testRegisterService_Error);
+		CPPUNIT_TEST(testRegisterService_Reregister);
 		CPPUNIT_TEST_SUITE_END();
 
 	public:
@@ -26,10 +32,10 @@ class LinkLocalServiceBrowserTest : public CppUnit::TestFixture {
 
 		void setUp() {
 			eventLoop = new DummyEventLoop();
-			dnsSDServiceFactory = new MockDNSSDServiceFactory();
-			testServiceID = new LinkLocalServiceID("foo", "bar.local");
-			testServiceInfo = new DNSSDService::ResolveResult("xmpp.bar.local", 1234, LinkLocalServiceInfo());
-			testServiceInfo2 = new DNSSDService::ResolveResult("xmpp.foo.local", 2345, LinkLocalServiceInfo());
+			querier = boost::shared_ptr<FakeDNSSDQuerier>(new FakeDNSSDQuerier());
+			testServiceID = new DNSSDServiceID("foo", "bar.local");
+			testServiceInfo = new DNSSDResolveServiceQuery::Result("_presence._tcp.bar.local", "xmpp.bar.local", 1234, LinkLocalServiceInfo());
+			testServiceInfo2 = new DNSSDResolveServiceQuery::Result("_presence.tcp.bar.local", "xmpp.foo.local", 2345, LinkLocalServiceInfo());
 		}
 
 		void tearDown() {
@@ -40,15 +46,33 @@ class LinkLocalServiceBrowserTest : public CppUnit::TestFixture {
 			delete testServiceInfo2;
 			delete testServiceInfo;
 			delete testServiceID;
-      delete dnsSDServiceFactory;
 			delete eventLoop;
 		}
 
+		void testConstructor() {
+			boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling();
+
+			CPPUNIT_ASSERT(!testling->isRunning());
+			CPPUNIT_ASSERT(!testling->hasError());
+		}
+
+		void testStart() {
+			boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling();
+			testling->start();
+
+			CPPUNIT_ASSERT(testling->isRunning());
+			CPPUNIT_ASSERT(!testling->hasError());
+
+			testling->stop();
+		}
+
 		void testServiceAdded() {
 			boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling();
+			testling->start();
+			eventLoop->processEvents();
 
-			dnsSDService()->setServiceInfo(*testServiceID,*testServiceInfo);
-			dnsSDService()->addService(*testServiceID);
+			querier->setServiceInfo(*testServiceID,*testServiceInfo);
+			querier->addService(*testServiceID);
 			eventLoop->processEvents();
 
 			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(addedServices.size()));
@@ -60,26 +84,33 @@ class LinkLocalServiceBrowserTest : public CppUnit::TestFixture {
 			CPPUNIT_ASSERT(*testServiceID == services[0].getID());
 			CPPUNIT_ASSERT(testServiceInfo->port == services[0].getPort());
 			CPPUNIT_ASSERT(testServiceInfo->host == services[0].getHostname());
+
+			testling->stop();
 		}
 
 		void testServiceAdded_NoServiceInfo() {
 			boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling();
+			testling->start();
+			eventLoop->processEvents();
 
-			dnsSDService()->addService(*testServiceID);
+			querier->addService(*testServiceID);
 			eventLoop->processEvents();
 
 			CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(addedServices.size()));
 			std::vector<LinkLocalService> services = testling->getServices();
 			CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(services.size()));
+
+			testling->stop();
 		}
 
 		void testServiceChanged() {
 			boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling();
-			dnsSDService()->setServiceInfo(*testServiceID,*testServiceInfo);
-			dnsSDService()->addService(*testServiceID);
+			testling->start();
+			querier->setServiceInfo(*testServiceID,*testServiceInfo);
+			querier->addService(*testServiceID);
 			eventLoop->processEvents();
 
-			dnsSDService()->setServiceInfo(*testServiceID,*testServiceInfo2);
+			querier->setServiceInfo(*testServiceID,*testServiceInfo2);
 			eventLoop->processEvents();
 
 			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(addedServices.size()));
@@ -91,31 +122,93 @@ class LinkLocalServiceBrowserTest : public CppUnit::TestFixture {
 			CPPUNIT_ASSERT(*testServiceID == services[0].getID());
 			CPPUNIT_ASSERT(testServiceInfo2->port == services[0].getPort());
 			CPPUNIT_ASSERT(testServiceInfo2->host == services[0].getHostname());
+
+			testling->stop();
 		}
 
 		void testServiceRemoved() {
 			boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling();
-			dnsSDService()->setServiceInfo(*testServiceID,*testServiceInfo);
-			dnsSDService()->addService(*testServiceID);
+			testling->start();
+			querier->setServiceInfo(*testServiceID,*testServiceInfo);
+			querier->addService(*testServiceID);
 			eventLoop->processEvents();
 
-			dnsSDService()->removeService(*testServiceID);
+			querier->removeService(*testServiceID);
 			eventLoop->processEvents();
-			dnsSDService()->setServiceInfo(*testServiceID,*testServiceInfo2);
+			querier->setServiceInfo(*testServiceID,*testServiceInfo2);
 			eventLoop->processEvents();
 
 			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(addedServices.size()));
 			CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(changedServices.size()));
 			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(removedServices.size()));
 			CPPUNIT_ASSERT(removedServices[0] == *testServiceID);
-			std::vector<LinkLocalService> services = testling->getServices();
-			CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(services.size()));
+			CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(testling->getServices().size()));
+
+			testling->stop();
+		}
+
+		void testError_BrowseErrorAfterStart() {
+			boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling();
+			testling->start();
+
+			querier->setBrowseError();
+			eventLoop->processEvents();
+
+			CPPUNIT_ASSERT(!testling->isRunning());
+			CPPUNIT_ASSERT(testling->hasError());
+		}
+
+		void testError_BrowseErrorAfterResolve() {
+			boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling();
+			testling->start();
+			querier->setServiceInfo(*testServiceID,*testServiceInfo);
+			querier->addService(*testServiceID);
+			eventLoop->processEvents();
+
+			querier->setBrowseError();
+			eventLoop->processEvents();
+			querier->setServiceInfo(*testServiceID,*testServiceInfo2);
+			eventLoop->processEvents();
+
+			CPPUNIT_ASSERT(!testling->isRunning());
+			CPPUNIT_ASSERT(testling->hasError());
+			CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(testling->getServices().size()));
+			CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(changedServices.size()));
+		}
+
+		void testRegisterService() {
+			boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling();
+			testling->start();
+			eventLoop->processEvents();
+
+			testling->stop();
+		}
+
+		void testRegisterService_Error() {
+			boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling();
+			testling->start();
+			testling->registerService("", 1234);
+			eventLoop->processEvents();
+
+			querier->setRegisterError();
+			eventLoop->processEvents();
+
+			CPPUNIT_ASSERT(!testling->isRunning());
+			CPPUNIT_ASSERT(testling->hasError());
+		}
+
+		void testRegisterService_Reregister() {
+			boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling();
+			testling->start();
+			eventLoop->processEvents();
+
+			testling->stop();
 		}
 
 	private:
 		boost::shared_ptr<LinkLocalServiceBrowser> createTestling() {
 			boost::shared_ptr<LinkLocalServiceBrowser> testling(
-					new LinkLocalServiceBrowser(dnsSDServiceFactory));
+					new LinkLocalServiceBrowser(querier));
 			testling->onServiceAdded.connect(boost::bind(
 					&LinkLocalServiceBrowserTest::handleServiceAdded, this, _1));
 			testling->onServiceChanged.connect(boost::bind(
@@ -125,32 +218,27 @@ class LinkLocalServiceBrowserTest : public CppUnit::TestFixture {
 			return testling;
 		}
 
-		void handleServiceAdded(const LinkLocalServiceID& service) {
+		void handleServiceAdded(const DNSSDServiceID& service) {
 			addedServices.push_back(service);
 		}
 
-		void handleServiceRemoved(const LinkLocalServiceID& service) {
+		void handleServiceRemoved(const DNSSDServiceID& service) {
 			removedServices.push_back(service);
 		}
 
-		void handleServiceChanged(const LinkLocalServiceID& service) {
+		void handleServiceChanged(const DNSSDServiceID& service) {
 			changedServices.push_back(service);
 		}
 
-    boost::shared_ptr<MockDNSSDService> dnsSDService() const {
-      CPPUNIT_ASSERT(dnsSDServiceFactory->services.size() > 0);
-      return dnsSDServiceFactory->services[0];
-    }
-
 	private:
 		DummyEventLoop* eventLoop;
-		MockDNSSDServiceFactory* dnsSDServiceFactory;
-		std::vector<LinkLocalServiceID> addedServices;
-		std::vector<LinkLocalServiceID> changedServices;
-		std::vector<LinkLocalServiceID> removedServices;
-		LinkLocalServiceID* testServiceID;
-		DNSSDService::ResolveResult* testServiceInfo;
-		DNSSDService::ResolveResult* testServiceInfo2;
+		boost::shared_ptr<FakeDNSSDQuerier> querier;
+		std::vector<DNSSDServiceID> addedServices;
+		std::vector<DNSSDServiceID> changedServices;
+		std::vector<DNSSDServiceID> removedServices;
+		DNSSDServiceID* testServiceID;
+		DNSSDResolveServiceQuery::Result* testServiceInfo;
+		DNSSDResolveServiceQuery::Result* testServiceInfo2;
 };
 
 CPPUNIT_TEST_SUITE_REGISTRATION(LinkLocalServiceBrowserTest);
diff --git a/Swiften/LinkLocal/UnitTest/Makefile.inc b/Swiften/LinkLocal/UnitTest/Makefile.inc
index abc1180..9640fa7 100644
--- a/Swiften/LinkLocal/UnitTest/Makefile.inc
+++ b/Swiften/LinkLocal/UnitTest/Makefile.inc
@@ -1,2 +1,3 @@
 UNITTEST_SOURCES += \
+	Swiften/LinkLocal/UnitTest/LinkLocalServiceBrowserTest.cpp \
 	Swiften/LinkLocal/UnitTest/LinkLocalServiceInfoTest.cpp
diff --git a/Swiften/LinkLocal/UnitTest/MockDNSSDService.h b/Swiften/LinkLocal/UnitTest/MockDNSSDService.h
deleted file mode 100644
index 69ac06d..0000000
--- a/Swiften/LinkLocal/UnitTest/MockDNSSDService.h
+++ /dev/null
@@ -1,97 +0,0 @@
-#pragma once
-
-#include <vector>
-#include <map>
-#include <boost/bind.hpp>
-
-#include "Swiften/EventLoop/MainEventLoop.h"
-#include "Swiften/LinkLocal/DNSSDService.h"
-#include "Swiften/LinkLocal/DNSSDServiceFactory.h"
-
-namespace Swift {
-	class MockDNSSDService : public DNSSDService {
-		public:
-			MockDNSSDService() {
-			}
-			
-			void start() {
-			}
-
-			void stop() {
-			}
-
-			virtual void registerService(const String&, int, const LinkLocalServiceInfo&) {
-				assert(false);
-			}
-
-			virtual void updateService(const LinkLocalServiceInfo&) {
-				assert(false);
-			}
-
-			virtual void unregisterService() {
-				assert(false);
-			}
-
-			virtual void startResolvingService(const LinkLocalServiceID& id) {
-				resolvingServices.push_back(id);
-				broadcastServiceInfo(id);
-			}
-
-			virtual void stopResolvingService(const LinkLocalServiceID& id) {
-				resolvingServices.erase(std::remove(resolvingServices.begin(), resolvingServices.end(), id), resolvingServices.end());
-			}
-			
-			virtual void resolveHostname(const String&, int) {
-				assert(false);
-			}
-
-			void addService(const LinkLocalServiceID& id) {
-				MainEventLoop::postEvent(boost::bind(boost::ref(onServiceAdded), id));
-			}
-
-			void removeService(const LinkLocalServiceID& id) {
-				serviceInfo.erase(id);
-				MainEventLoop::postEvent(boost::bind(boost::ref(onServiceRemoved), id));
-			}
-
-			void setServiceInfo(const LinkLocalServiceID& id, const DNSSDService::ResolveResult& info) {
-				std::pair<ServiceInfoMap::iterator, bool> r = serviceInfo.insert(std::make_pair(id, info));
-				if (!r.second) {
-					r.first->second = info;
-				}
-				broadcastServiceInfo(id);
-			}
-
-		private:
-			void broadcastServiceInfo(const LinkLocalServiceID& id) {
-				if (std::find(resolvingServices.begin(), resolvingServices.end(), id) != resolvingServices.end()) {
-					ServiceInfoMap::const_iterator i = serviceInfo.find(id);
-					if (i != serviceInfo.end()) {
-						MainEventLoop::postEvent(
-								boost::bind(boost::ref(onServiceResolved), id, i->second));
-					}
-				}
-			}
-
-		private:
-			typedef std::map<LinkLocalServiceID,DNSSDService::ResolveResult> ServiceInfoMap;
-			ServiceInfoMap serviceInfo;
-			std::vector<LinkLocalServiceID> resolvingServices;
-	};
-
-  class MockDNSSDServiceFactory : public DNSSDServiceFactory {
-    public:
-      boost::shared_ptr<DNSSDService> createDNSSDService() {
-        boost::shared_ptr<MockDNSSDService> result(new MockDNSSDService());   
-        services.push_back(result);
-        return result;
-      }
-
-      bool canCreate() const {
-        return true;
-      }
-
-      std::vector<boost::shared_ptr<MockDNSSDService> > services;
-  };
-
-}
-- 
cgit v0.10.2-6-g49f6