From b145bde2b103b0e688eef6300d34668431c5ad04 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Wed, 29 Jul 2009 22:18:02 +0200
Subject: Created LinkLocalPresenceManager.


diff --git a/Slimber/CLI/main.cpp b/Slimber/CLI/main.cpp
index 8b70625..7ebdb0f 100644
--- a/Slimber/CLI/main.cpp
+++ b/Slimber/CLI/main.cpp
@@ -10,6 +10,7 @@
 #include "Slimber/Server.h"
 #include "Slimber/FileVCardCollection.h"
 #include "Swiften/LinkLocal/LinkLocalRoster.h"
+#include "Swiften/LinkLocal/LinkLocalServiceBrowser.h"
 #include "Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h"
 #include "Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h"
 #include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.h"
@@ -22,10 +23,8 @@ int main() {
 	SimpleEventLoop eventLoop;
 	boost::shared_ptr<BonjourQuerier> querier(new BonjourQuerier());
 	querier->start();
-	boost::shared_ptr<DNSSDBrowseQuery> query = querier->createBrowseQuery();
-	query->startBrowsing();
-	boost::shared_ptr<DNSSDRegisterQuery> query2 = querier->createRegisterQuery("remko", 1234, LinkLocalServiceInfo());
-	query2->registerService();
+	LinkLocalServiceBrowser browser(querier);
+	browser.start();
 
 /*
 	boost::shared_ptr<DNSSDService> dnsSDService;
diff --git a/Slimber/LinkLocalPresenceManager.cpp b/Slimber/LinkLocalPresenceManager.cpp
new file mode 100644
index 0000000..b964786
--- /dev/null
+++ b/Slimber/LinkLocalPresenceManager.cpp
@@ -0,0 +1,98 @@
+#include "Slimber/LinkLocalPresenceManager.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Base/foreach.h"
+#include "Swiften/LinkLocal/LinkLocalServiceBrowser.h"
+#include "Swiften/Elements/RosterPayload.h"
+#include "Swiften/Elements/Presence.h"
+
+namespace Swift {
+
+LinkLocalPresenceManager::LinkLocalPresenceManager(LinkLocalServiceBrowser* browser) : browser(browser) {
+	browser->onServiceAdded.connect(
+			boost::bind(&LinkLocalPresenceManager::handleServiceAdded, this, _1));
+	browser->onServiceChanged.connect(
+			boost::bind(&LinkLocalPresenceManager::handleServiceChanged, this, _1));
+	browser->onServiceRemoved.connect(
+			boost::bind(&LinkLocalPresenceManager::handleServiceRemoved, this, _1));
+}
+
+void LinkLocalPresenceManager::handleServiceAdded(const LinkLocalService& service) {
+	boost::shared_ptr<RosterPayload> roster(new RosterPayload());
+	roster->addItem(getRosterItem(service));
+	onRosterChanged(roster);
+	onPresenceChanged(getPresence(service));
+}
+
+void LinkLocalPresenceManager::handleServiceChanged(const LinkLocalService& service) {
+	onPresenceChanged(getPresence(service));
+}
+
+void LinkLocalPresenceManager::handleServiceRemoved(const LinkLocalService& service) {
+	boost::shared_ptr<RosterPayload> roster(new RosterPayload());
+	roster->addItem(RosterItemPayload(getJIDForService(service), "", RosterItemPayload::Remove));
+	onRosterChanged(roster);
+}
+
+boost::shared_ptr<RosterPayload> LinkLocalPresenceManager::getRoster() const {
+	boost::shared_ptr<RosterPayload> roster(new RosterPayload());
+	foreach(const LinkLocalService& service, browser->getServices()) {
+		roster->addItem(getRosterItem(service));
+	}
+	return roster;
+}
+
+std::vector<boost::shared_ptr<Presence> > LinkLocalPresenceManager::getAllPresence() const {
+	std::vector<boost::shared_ptr<Presence> > result;
+	foreach(const LinkLocalService& service, browser->getServices()) {
+		result.push_back(getPresence(service));
+	}
+	return result;
+}
+
+RosterItemPayload LinkLocalPresenceManager::getRosterItem(const LinkLocalService& service) const {
+ return RosterItemPayload(getJIDForService(service), getRosterName(service), RosterItemPayload::Both);
+}
+
+String LinkLocalPresenceManager::getRosterName(const LinkLocalService& service) const {
+	LinkLocalServiceInfo info = service.getInfo();
+	if (!info.getNick().isEmpty()) {
+		return info.getNick();
+	}
+	else if (!info.getFirstName().isEmpty()) {
+		String result = info.getFirstName();
+		if (!info.getLastName().isEmpty()) {
+			result += " " + info.getLastName();
+		}
+		return result;
+	}
+	else if (!info.getLastName().isEmpty()) {
+		return info.getLastName();
+	}
+	return "";
+}
+
+JID LinkLocalPresenceManager::getJIDForService(const LinkLocalService& service) const {
+	return JID(service.getName());
+}
+
+boost::shared_ptr<Presence> LinkLocalPresenceManager::getPresence(const LinkLocalService& service) const {
+	boost::shared_ptr<Presence> presence(new Presence());
+	presence->setFrom(getJIDForService(service));
+	switch (service.getInfo().getStatus()) {
+		case LinkLocalServiceInfo::Available:
+			presence->setShow(StatusShow::Online);
+			break;
+		case LinkLocalServiceInfo::Away:
+			presence->setShow(StatusShow::Away);
+			break;
+		case LinkLocalServiceInfo::DND:
+			presence->setShow(StatusShow::DND);
+			break;
+	}
+	presence->setStatus(service.getInfo().getMessage());
+	return presence;
+}
+
+}
diff --git a/Slimber/LinkLocalPresenceManager.h b/Slimber/LinkLocalPresenceManager.h
new file mode 100644
index 0000000..2af2313
--- /dev/null
+++ b/Slimber/LinkLocalPresenceManager.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/signal.hpp>
+
+#include "Swiften/Elements/RosterItemPayload.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/JID/JID.h"
+
+namespace Swift {
+	class LinkLocalService;
+	class LinkLocalServiceBrowser;
+	class RosterPayload;
+	class Presence;
+
+	class LinkLocalPresenceManager {
+		public:
+			LinkLocalPresenceManager(LinkLocalServiceBrowser*);
+
+			boost::shared_ptr<RosterPayload> getRoster() const;
+			std::vector<boost::shared_ptr<Presence> > getAllPresence() const;
+
+			boost::signal<void (boost::shared_ptr<RosterPayload>)> onRosterChanged;
+			boost::signal<void (boost::shared_ptr<Presence>)> onPresenceChanged;
+
+		private:
+			void handleServiceAdded(const LinkLocalService&);
+			void handleServiceChanged(const LinkLocalService&);
+			void handleServiceRemoved(const LinkLocalService&);
+
+			RosterItemPayload getRosterItem(const LinkLocalService& service) const;
+			String getRosterName(const LinkLocalService& service) const;
+			JID getJIDForService(const LinkLocalService& service) const;
+			boost::shared_ptr<Presence> getPresence(const LinkLocalService& service) const;
+
+		private:
+			LinkLocalServiceBrowser* browser;
+	};
+}
diff --git a/Slimber/Makefile.inc b/Slimber/Makefile.inc
index fea164f..16fcb8f 100644
--- a/Slimber/Makefile.inc
+++ b/Slimber/Makefile.inc
@@ -1,14 +1,16 @@
 SLIMBER_TARGET = Slimber/Slimber.a
 SLIMBER_SOURCES = \
+	Slimber/LinkLocalPresenceManager.cpp \
 	Slimber/FileVCardCollection.cpp \
 	Slimber/VCardCollection.cpp \
 	Slimber/Server.cpp
 SLIMBER_OBJECTS = \
 	$(SLIMBER_SOURCES:.cpp=.o)
 
-DEPS += \
-	$(SLIMBER_SOURCES:.cpp=.dep) \
+DEPS += $(SLIMBER_SOURCES:.cpp=.dep)
+UNITTEST_LIBS += $(SLIMBER_TARGET)
 
+include Slimber/UnitTest/Makefile.inc
 include Slimber/CLI/Makefile.inc
 ifeq ($(MACOSX),1)
 include Slimber/Cocoa/Makefile.inc
diff --git a/Slimber/UnitTest/LinkLocalPresenceManagerTest.cpp b/Slimber/UnitTest/LinkLocalPresenceManagerTest.cpp
new file mode 100644
index 0000000..a1ecdf8
--- /dev/null
+++ b/Slimber/UnitTest/LinkLocalPresenceManagerTest.cpp
@@ -0,0 +1,224 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/bind.hpp>
+#include <map>
+
+#include "Swiften/Elements/Presence.h"
+#include "Swiften/Elements/RosterPayload.h"
+#include "Swiften/Elements/RosterItemPayload.h"
+#include "Slimber/LinkLocalPresenceManager.h"
+#include "Swiften/LinkLocal/LinkLocalServiceInfo.h"
+#include "Swiften/LinkLocal/LinkLocalServiceBrowser.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"
+
+using namespace Swift;
+
+class LinkLocalPresenceManagerTest : public CppUnit::TestFixture {
+		CPPUNIT_TEST_SUITE(LinkLocalPresenceManagerTest);
+		CPPUNIT_TEST(testServiceAdded);
+		CPPUNIT_TEST(testServiceRemoved);
+		CPPUNIT_TEST(testServiceChanged);
+		CPPUNIT_TEST(testGetRoster);
+		CPPUNIT_TEST(testGetAllPresence);
+		CPPUNIT_TEST(testGetRoster_InfoWithNick);
+		CPPUNIT_TEST(testGetRoster_InfoWithFirstName);
+		CPPUNIT_TEST(testGetRoster_InfoWithLastName);
+		CPPUNIT_TEST(testGetRoster_InfoWithFirstAndLastName);
+		CPPUNIT_TEST(testGetRoster_NoInfo);
+		CPPUNIT_TEST_SUITE_END();
+
+	public:
+		LinkLocalPresenceManagerTest() {}
+
+		void setUp() {
+			eventLoop = new DummyEventLoop();
+			querier = boost::shared_ptr<FakeDNSSDQuerier>(new FakeDNSSDQuerier("wonderland.lit"));
+			browser = new LinkLocalServiceBrowser(querier);
+			browser->start();
+		}
+
+		void tearDown() {
+			browser->stop();
+			delete browser;
+			delete eventLoop;
+		}
+
+		void testServiceAdded() {
+			std::auto_ptr<LinkLocalPresenceManager> testling(createTestling());
+
+			addService("alice@wonderland", "Alice");
+
+			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(rosterChanges.size()));
+			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(rosterChanges[0]->getItems().size()));
+			boost::optional<RosterItemPayload> item = rosterChanges[0]->getItem(JID("alice@wonderland"));
+			CPPUNIT_ASSERT(item);
+			CPPUNIT_ASSERT_EQUAL(String("Alice"), item->getName());
+			CPPUNIT_ASSERT_EQUAL(RosterItemPayload::Both, item->getSubscription());
+			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(presenceChanges.size()));
+			CPPUNIT_ASSERT(StatusShow::Online == presenceChanges[0]->getShow());
+			CPPUNIT_ASSERT(JID("alice@wonderland") == presenceChanges[0]->getFrom());
+		}
+
+		void testServiceRemoved() {
+			std::auto_ptr<LinkLocalPresenceManager> testling(createTestling());
+
+			addService("alice@wonderland");
+			removeService("alice@wonderland");
+
+			CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(rosterChanges.size()));
+			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(rosterChanges[1]->getItems().size()));
+			boost::optional<RosterItemPayload> item = rosterChanges[1]->getItem(JID("alice@wonderland"));
+			CPPUNIT_ASSERT(item);
+			CPPUNIT_ASSERT_EQUAL(RosterItemPayload::Remove, item->getSubscription());
+		}
+
+		void testServiceChanged() {
+			std::auto_ptr<LinkLocalPresenceManager> testling(createTestling());
+
+			addService("alice@wonderland");
+			updateServicePresence("alice@wonderland", LinkLocalServiceInfo::Away, "I'm Away");
+
+			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(rosterChanges.size()));
+			CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(presenceChanges.size()));
+			CPPUNIT_ASSERT(StatusShow::Away == presenceChanges[1]->getShow());
+			CPPUNIT_ASSERT(JID("alice@wonderland") == presenceChanges[1]->getFrom());
+			CPPUNIT_ASSERT_EQUAL(String("I'm Away"), presenceChanges[1]->getStatus());
+		}
+
+		void testGetAllPresence() {
+			std::auto_ptr<LinkLocalPresenceManager> testling(createTestling());
+
+			addService("alice@wonderland");
+			addService("rabbit@teaparty");
+			updateServicePresence("rabbit@teaparty", LinkLocalServiceInfo::Away, "Partying");
+			
+			std::vector<boost::shared_ptr<Presence> > presences = testling->getAllPresence();
+			CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(presences.size()));
+			// The order doesn't matter
+			CPPUNIT_ASSERT(JID("rabbit@teaparty") == presences[0]->getFrom());
+			CPPUNIT_ASSERT(StatusShow::Away == presences[0]->getShow());
+			CPPUNIT_ASSERT(JID("alice@wonderland") == presences[1]->getFrom());
+			CPPUNIT_ASSERT(StatusShow::Online == presences[1]->getShow());
+		}
+
+		void testGetRoster() {
+			std::auto_ptr<LinkLocalPresenceManager> testling(createTestling());
+
+			addService("alice@wonderland", "Alice");
+			addService("rabbit@teaparty", "Rabbit");
+
+			boost::shared_ptr<RosterPayload> roster = testling->getRoster();
+			CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(roster->getItems().size()));
+			boost::optional<RosterItemPayload> item;
+			item = roster->getItem(JID("alice@wonderland"));
+			CPPUNIT_ASSERT(item);
+			CPPUNIT_ASSERT_EQUAL(String("Alice"), item->getName());
+			CPPUNIT_ASSERT_EQUAL(RosterItemPayload::Both, item->getSubscription());
+			item = roster->getItem(JID("rabbit@teaparty"));
+			CPPUNIT_ASSERT(item);
+			CPPUNIT_ASSERT_EQUAL(String("Rabbit"), item->getName());
+			CPPUNIT_ASSERT_EQUAL(RosterItemPayload::Both, item->getSubscription());
+		}
+
+		void testGetRoster_InfoWithNick() {
+			std::auto_ptr<LinkLocalPresenceManager> testling(createTestling());
+
+			addService("alice@wonderland", "Alice", "Alice In", "Wonderland");
+
+			boost::optional<RosterItemPayload> item = testling->getRoster()->getItem(JID("alice@wonderland"));
+			CPPUNIT_ASSERT_EQUAL(String("Alice"), item->getName());
+		}
+
+		void testGetRoster_InfoWithFirstName() {
+			std::auto_ptr<LinkLocalPresenceManager> testling(createTestling());
+
+			addService("alice@wonderland", "", "Alice In", "");
+
+			boost::optional<RosterItemPayload> item = testling->getRoster()->getItem(JID("alice@wonderland"));
+			CPPUNIT_ASSERT_EQUAL(String("Alice In"), item->getName());
+		}
+
+		void testGetRoster_InfoWithLastName() {
+			std::auto_ptr<LinkLocalPresenceManager> testling(createTestling());
+
+			addService("alice@wonderland", "", "", "Wonderland");
+
+			boost::optional<RosterItemPayload> item = testling->getRoster()->getItem(JID("alice@wonderland"));
+			CPPUNIT_ASSERT_EQUAL(String("Wonderland"), item->getName());
+		}
+
+		void testGetRoster_InfoWithFirstAndLastName() {
+			std::auto_ptr<LinkLocalPresenceManager> testling(createTestling());
+
+			addService("alice@wonderland", "", "Alice In", "Wonderland");
+
+			boost::optional<RosterItemPayload> item = testling->getRoster()->getItem(JID("alice@wonderland"));
+			CPPUNIT_ASSERT_EQUAL(String("Alice In Wonderland"), item->getName());
+		}
+
+		void testGetRoster_NoInfo() {
+			std::auto_ptr<LinkLocalPresenceManager> testling(createTestling());
+
+			addService("alice@wonderland");
+
+			boost::optional<RosterItemPayload> item = testling->getRoster()->getItem(JID("alice@wonderland"));
+			CPPUNIT_ASSERT_EQUAL(String(""), item->getName());
+		}
+
+	private:
+		std::auto_ptr<LinkLocalPresenceManager> createTestling() {
+			std::auto_ptr<LinkLocalPresenceManager> testling(
+					new LinkLocalPresenceManager(browser));
+			testling->onRosterChanged.connect(boost::bind(
+					&LinkLocalPresenceManagerTest::handleRosterChanged, this, _1));
+			testling->onPresenceChanged.connect(boost::bind(
+					&LinkLocalPresenceManagerTest::handlePresenceChanged, this, _1));
+			return testling;
+		}
+
+		void addService(const String& name, const String& nickName = String(), const String& firstName = String(), const String& lastName = String()) {
+			DNSSDServiceID service(name, "local.");
+			LinkLocalServiceInfo info;
+			info.setFirstName(firstName);
+			info.setLastName(lastName);
+			info.setNick(nickName);
+			querier->setServiceInfo(service, DNSSDResolveServiceQuery::Result(name + "._presence._tcp.local", "rabbithole.local", 1234, info.toTXTRecord()));
+			querier->addService(service);
+			eventLoop->processEvents();
+		}
+
+		void removeService(const String& name) {
+			DNSSDServiceID service(name, "local.");
+			querier->removeService(DNSSDServiceID(name, "local."));
+			eventLoop->processEvents();
+		}
+
+		void updateServicePresence(const String& name, LinkLocalServiceInfo::Status status, const String& message) {
+			DNSSDServiceID service(name, "local.");
+			LinkLocalServiceInfo info;
+			info.setStatus(status);
+			info.setMessage(message);
+			querier->setServiceInfo(service, DNSSDResolveServiceQuery::Result(name + "._presence._tcp.local", "rabbithole.local", 1234, info.toTXTRecord()));
+			eventLoop->processEvents();
+		}
+
+		void handleRosterChanged(boost::shared_ptr<RosterPayload> payload) {
+			rosterChanges.push_back(payload);
+		}
+
+		void handlePresenceChanged(boost::shared_ptr<Presence> presence) {
+			presenceChanges.push_back(presence);
+		}
+
+	private:
+		DummyEventLoop* eventLoop;
+		boost::shared_ptr<FakeDNSSDQuerier> querier;
+		LinkLocalServiceBrowser* browser;
+		std::vector< boost::shared_ptr<RosterPayload> > rosterChanges;
+		std::vector< boost::shared_ptr<Presence> > presenceChanges;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(LinkLocalPresenceManagerTest);
diff --git a/Slimber/UnitTest/Makefile.inc b/Slimber/UnitTest/Makefile.inc
new file mode 100644
index 0000000..f2c4db8
--- /dev/null
+++ b/Slimber/UnitTest/Makefile.inc
@@ -0,0 +1,2 @@
+UNITTEST_SOURCES += \
+	Slimber/UnitTest/LinkLocalPresenceManagerTest.cpp
diff --git a/Swiften/Elements/RosterPayload.cpp b/Swiften/Elements/RosterPayload.cpp
index 6d39264..8e2fd87 100644
--- a/Swiften/Elements/RosterPayload.cpp
+++ b/Swiften/Elements/RosterPayload.cpp
@@ -7,7 +7,7 @@ boost::optional<RosterItemPayload> RosterPayload::getItem(const JID& jid) const
 	foreach(const RosterItemPayload& item, items_) {
     // FIXME: MSVC rejects this. Find out why.
 		//if (item.getJID() == jid) {
-    if (item.getJID().compare(jid, JID::WithResource)) {
+    if (item.getJID().equals(jid, JID::WithResource)) {
 			return boost::optional<RosterItemPayload>(item);
 		}
 	}
diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourBrowseQuery.h b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourBrowseQuery.h
index be0c921..c605175 100644
--- a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourBrowseQuery.h
+++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourBrowseQuery.h
@@ -41,6 +41,7 @@ namespace Swift {
 					MainEventLoop::postEvent(boost::bind(boost::ref(onError)), shared_from_this());
 				}
 				else {
+					std::cout << "Discovered service: name:" << name << " domain:" << domain << " type: " << type << std::endl;
 					DNSSDServiceID service(name, domain, type, interfaceIndex);
 					if (flags & kDNSServiceFlagsAdd) {
 						MainEventLoop::postEvent(boost::bind(boost::ref(onServiceAdded), service), shared_from_this());
diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveServiceQuery.h b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveServiceQuery.h
index d16ca5b..886b87b 100644
--- a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveServiceQuery.h
+++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveServiceQuery.h
@@ -45,6 +45,7 @@ namespace Swift {
 					MainEventLoop::postEvent(boost::bind(boost::ref(onServiceResolved), boost::optional<Result>()), shared_from_this());
 				}
 				else {
+					std::cout << "Service resolved: name:" << fullName << " host:" << host << " port:" << port << std::endl;
 					MainEventLoop::postEvent(
 							boost::bind(
 								boost::ref(onServiceResolved), 
diff --git a/Swiften/LinkLocal/LinkLocalServiceBrowser.cpp b/Swiften/LinkLocal/LinkLocalServiceBrowser.cpp
index f7c5478..f65cd7a 100644
--- a/Swiften/LinkLocal/LinkLocalServiceBrowser.cpp
+++ b/Swiften/LinkLocal/LinkLocalServiceBrowser.cpp
@@ -102,19 +102,22 @@ void LinkLocalServiceBrowser::handleServiceRemoved(const DNSSDServiceID& service
 	assert(i != resolveQueries.end());
 	i->second->stop();
 	resolveQueries.erase(i);
-	services.erase(service);
-	onServiceRemoved(service);
+	ServiceMap::iterator j = services.find(service);
+	assert(j != services.end());
+	LinkLocalService linkLocalService(j->first, j->second);
+	services.erase(j);
+	onServiceRemoved(linkLocalService);
 }
 
 void LinkLocalServiceBrowser::handleServiceResolved(const DNSSDServiceID& service, const boost::optional<DNSSDResolveServiceQuery::Result>& result) {
 	if (result) {
 		std::pair<ServiceMap::iterator, bool> r = services.insert(std::make_pair(service, *result));
 		if (r.second) {
-			onServiceAdded(service);
+			onServiceAdded(LinkLocalService(r.first->first, r.first->second));
 		}
 		else {
 			r.first->second = *result;
-			onServiceChanged(service);
+			onServiceChanged(LinkLocalService(r.first->first, r.first->second));
 		}
 	}
 }
diff --git a/Swiften/LinkLocal/LinkLocalServiceBrowser.h b/Swiften/LinkLocal/LinkLocalServiceBrowser.h
index dcdd576..a6623b1 100644
--- a/Swiften/LinkLocal/LinkLocalServiceBrowser.h
+++ b/Swiften/LinkLocal/LinkLocalServiceBrowser.h
@@ -31,9 +31,9 @@ namespace Swift {
 
 			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 (const LinkLocalService&)> onServiceAdded;
+			boost::signal<void (const LinkLocalService&)> onServiceChanged;
+			boost::signal<void (const LinkLocalService&)> onServiceRemoved;
 			boost::signal<void (bool)> onStopped;
 
 		private:
diff --git a/Swiften/LinkLocal/UnitTest/LinkLocalServiceBrowserTest.cpp b/Swiften/LinkLocal/UnitTest/LinkLocalServiceBrowserTest.cpp
index e9501cb..6e4d3b4 100644
--- a/Swiften/LinkLocal/UnitTest/LinkLocalServiceBrowserTest.cpp
+++ b/Swiften/LinkLocal/UnitTest/LinkLocalServiceBrowserTest.cpp
@@ -84,7 +84,7 @@ class LinkLocalServiceBrowserTest : public CppUnit::TestFixture {
 			eventLoop->processEvents();
 
 			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(addedServices.size()));
-			CPPUNIT_ASSERT(addedServices[0] == *testServiceID);
+			CPPUNIT_ASSERT(addedServices[0].getID() == *testServiceID);
 			CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(changedServices.size()));
 			CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(removedServices.size()));
 			std::vector<LinkLocalService> services = testling->getServices();
@@ -141,7 +141,7 @@ class LinkLocalServiceBrowserTest : public CppUnit::TestFixture {
 			eventLoop->processEvents();
 
 			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(addedServices.size()));
-			CPPUNIT_ASSERT(addedServices[0] == *aliceServiceID);
+			CPPUNIT_ASSERT(addedServices[0].getID() == *aliceServiceID);
 			CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(changedServices.size()));
 			CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(removedServices.size()));
 			std::vector<LinkLocalService> services = testling->getServices();
@@ -166,7 +166,7 @@ class LinkLocalServiceBrowserTest : public CppUnit::TestFixture {
 			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(addedServices.size()));
 			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(changedServices.size()));
 			CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(removedServices.size()));
-			CPPUNIT_ASSERT(changedServices[0] == *testServiceID);
+			CPPUNIT_ASSERT(changedServices[0].getID() == *testServiceID);
 			std::vector<LinkLocalService> services = testling->getServices();
 			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(services.size()));
 			CPPUNIT_ASSERT(*testServiceID == services[0].getID());
@@ -191,7 +191,7 @@ class LinkLocalServiceBrowserTest : public CppUnit::TestFixture {
 			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);
+			CPPUNIT_ASSERT(removedServices[0].getID() == *testServiceID);
 			CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(testling->getServices().size()));
 
 			testling->stop();
@@ -294,15 +294,15 @@ class LinkLocalServiceBrowserTest : public CppUnit::TestFixture {
 			return testling;
 		}
 
-		void handleServiceAdded(const DNSSDServiceID& service) {
+		void handleServiceAdded(const LinkLocalService& service) {
 			addedServices.push_back(service);
 		}
 
-		void handleServiceRemoved(const DNSSDServiceID& service) {
+		void handleServiceRemoved(const LinkLocalService& service) {
 			removedServices.push_back(service);
 		}
 
-		void handleServiceChanged(const DNSSDServiceID& service) {
+		void handleServiceChanged(const LinkLocalService& service) {
 			changedServices.push_back(service);
 		}
 
@@ -320,9 +320,9 @@ class LinkLocalServiceBrowserTest : public CppUnit::TestFixture {
 	private:
 		DummyEventLoop* eventLoop;
 		boost::shared_ptr<FakeDNSSDQuerier> querier;
-		std::vector<DNSSDServiceID> addedServices;
-		std::vector<DNSSDServiceID> changedServices;
-		std::vector<DNSSDServiceID> removedServices;
+		std::vector<LinkLocalService> addedServices;
+		std::vector<LinkLocalService> changedServices;
+		std::vector<LinkLocalService> removedServices;
 		DNSSDServiceID* aliceServiceID;
 		DNSSDResolveServiceQuery::Result* aliceServiceInfo;
 		DNSSDServiceID* testServiceID;
-- 
cgit v0.10.2-6-g49f6