summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Slimber')
-rw-r--r--Slimber/CLI/main.cpp7
-rw-r--r--Slimber/LinkLocalPresenceManager.cpp98
-rw-r--r--Slimber/LinkLocalPresenceManager.h39
-rw-r--r--Slimber/Makefile.inc6
-rw-r--r--Slimber/UnitTest/LinkLocalPresenceManagerTest.cpp224
-rw-r--r--Slimber/UnitTest/Makefile.inc2
6 files changed, 370 insertions, 6 deletions
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