summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swiften/Presence')
-rw-r--r--Swiften/Presence/PresenceOracle.cpp58
-rw-r--r--Swiften/Presence/PresenceOracle.h30
-rw-r--r--Swiften/Presence/PresenceSender.cpp49
-rw-r--r--Swiften/Presence/PresenceSender.h24
-rw-r--r--Swiften/Presence/UnitTest/PresenceOracleTest.cpp126
-rw-r--r--Swiften/Presence/UnitTest/PresenceSenderTest.cpp120
6 files changed, 407 insertions, 0 deletions
diff --git a/Swiften/Presence/PresenceOracle.cpp b/Swiften/Presence/PresenceOracle.cpp
new file mode 100644
index 0000000..988fc10
--- /dev/null
+++ b/Swiften/Presence/PresenceOracle.cpp
@@ -0,0 +1,58 @@
+#include "PresenceOracle.h"
+
+#include <boost/bind.hpp>
+#include "Swiften/Client/StanzaChannel.h"
+namespace Swift {
+
+typedef std::pair<JID, std::map<JID, boost::shared_ptr<Presence> > > JIDMapPair;
+typedef std::pair<JID, boost::shared_ptr<Presence> > JIDPresencePair;
+
+PresenceOracle::PresenceOracle(StanzaChannel* stanzaChannel) {
+ stanzaChannel_ = stanzaChannel;
+ stanzaChannel_->onPresenceReceived.connect(boost::bind(&PresenceOracle::handleIncomingPresence, this, _1));
+}
+
+void PresenceOracle::cancelSubscription(const JID& jid) {
+ boost::shared_ptr<Presence> stanza(new Presence());
+ stanza->setType(Presence::Unsubscribed);
+ stanza->setTo(jid);
+ stanzaChannel_->sendPresence(stanza);
+}
+
+void PresenceOracle::confirmSubscription(const JID& jid) {
+ boost::shared_ptr<Presence> stanza(new Presence());
+ stanza->setType(Presence::Subscribed);
+ stanza->setTo(jid);
+ stanzaChannel_->sendPresence(stanza);
+}
+
+
+void PresenceOracle::requestSubscription(const JID& jid) {
+ boost::shared_ptr<Presence> stanza(new Presence());
+ stanza->setType(Presence::Subscribe);
+ stanza->setTo(jid);
+ stanzaChannel_->sendPresence(stanza);
+}
+
+
+void PresenceOracle::handleIncomingPresence(boost::shared_ptr<Presence> presence) {
+ JID bareJID = JID(presence->getFrom().toBare());
+
+ if (presence->getType() == Presence::Subscribe) {
+ onPresenceSubscriptionRequest(bareJID, presence->getStatus());
+ } else {
+ std::map<JID, boost::shared_ptr<Presence> > jidMap = entries_[bareJID];
+ boost::shared_ptr<Presence> last;
+ foreach(JIDPresencePair pair, jidMap) {
+ if (pair.first == presence->getFrom()) {
+ last = pair.second;
+ break;
+ }
+ }
+ jidMap[presence->getFrom()] = presence;
+ entries_[bareJID] = jidMap;
+ onPresenceChange(presence, last);
+ }
+}
+
+}
diff --git a/Swiften/Presence/PresenceOracle.h b/Swiften/Presence/PresenceOracle.h
new file mode 100644
index 0000000..dc6fe5d
--- /dev/null
+++ b/Swiften/Presence/PresenceOracle.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/Presence.h"
+
+#include <map>
+#include <boost/signal.hpp>
+
+namespace Swift {
+class StanzaChannel;
+
+class PresenceOracle {
+ public:
+ PresenceOracle(StanzaChannel* stanzaChannel);
+ ~PresenceOracle() {};
+
+ void cancelSubscription(const JID& jid);
+ void confirmSubscription(const JID& jid);
+ void requestSubscription(const JID& jid);
+
+ boost::signal<void (boost::shared_ptr<Presence>, boost::shared_ptr<Presence>)> onPresenceChange;
+ boost::signal<void (const JID&, const String&)> onPresenceSubscriptionRequest;
+
+ private:
+ void handleIncomingPresence(boost::shared_ptr<Presence> presence);
+ std::map<JID, std::map<JID, boost::shared_ptr<Presence> > > entries_;
+ StanzaChannel* stanzaChannel_;
+};
+}
+
diff --git a/Swiften/Presence/PresenceSender.cpp b/Swiften/Presence/PresenceSender.cpp
new file mode 100644
index 0000000..8e7ef68
--- /dev/null
+++ b/Swiften/Presence/PresenceSender.cpp
@@ -0,0 +1,49 @@
+#include "Swiften/Presence/PresenceSender.h"
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Client/StanzaChannel.h"
+
+namespace Swift {
+
+PresenceSender::PresenceSender(StanzaChannel* channel) : channel(channel) {
+}
+
+void PresenceSender::sendPresence(boost::shared_ptr<Presence> presence) {
+ if (!channel->isAvailable()) {
+ return;
+ }
+
+ channel->sendPresence(presence);
+
+ if (!presence->getTo().isValid()) {
+ boost::shared_ptr<Presence> presenceCopy(new Presence(*presence));
+ foreach(const JID& jid, directedPresenceReceivers) {
+ presenceCopy->setTo(jid);
+ channel->sendPresence(presenceCopy);
+ }
+
+ lastSentUndirectedPresence = presence;
+ }
+}
+
+void PresenceSender::addDirectedPresenceReceiver(const JID& jid) {
+ directedPresenceReceivers.insert(jid);
+ if (channel->isAvailable()) {
+ if (lastSentUndirectedPresence && lastSentUndirectedPresence->getType() == Presence::Available) {
+ boost::shared_ptr<Presence> presenceCopy(new Presence(*lastSentUndirectedPresence));
+ presenceCopy->setTo(jid);
+ channel->sendPresence(presenceCopy);
+ }
+ }
+}
+
+void PresenceSender::removeDirectedPresenceReceiver(const JID& jid) {
+ directedPresenceReceivers.erase(jid);
+ if (channel->isAvailable()) {
+ boost::shared_ptr<Presence> presence(new Presence());
+ presence->setType(Presence::Unavailable);
+ presence->setTo(jid);
+ channel->sendPresence(presence);
+ }
+}
+
+}
diff --git a/Swiften/Presence/PresenceSender.h b/Swiften/Presence/PresenceSender.h
new file mode 100644
index 0000000..ef69447
--- /dev/null
+++ b/Swiften/Presence/PresenceSender.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <set>
+
+#include "Swiften/Elements/Presence.h"
+
+namespace Swift {
+ class StanzaChannel;
+
+ class PresenceSender {
+ public:
+ PresenceSender(StanzaChannel*);
+
+ void addDirectedPresenceReceiver(const JID&);
+ void removeDirectedPresenceReceiver(const JID&);
+
+ void sendPresence(boost::shared_ptr<Presence>);
+
+ private:
+ boost::shared_ptr<Presence> lastSentUndirectedPresence;
+ StanzaChannel* channel;
+ std::set<JID> directedPresenceReceivers;
+ };
+}
diff --git a/Swiften/Presence/UnitTest/PresenceOracleTest.cpp b/Swiften/Presence/UnitTest/PresenceOracleTest.cpp
new file mode 100644
index 0000000..2c9c526
--- /dev/null
+++ b/Swiften/Presence/UnitTest/PresenceOracleTest.cpp
@@ -0,0 +1,126 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
+
+#include "Swiften/Presence/PresenceOracle.h"
+#include "Swiften/Client/DummyStanzaChannel.h"
+
+using namespace Swift;
+
+class PresencePointerPair {
+ public:
+ boost::shared_ptr<Presence> one;
+ boost::shared_ptr<Presence> two;
+};
+
+class SubscriptionRequestInfo {
+ public:
+ boost::optional<JID> jid;
+ boost::optional<String> reason;
+};
+
+class PresenceOracleTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(PresenceOracleTest);
+ CPPUNIT_TEST(testFirstPresence);
+ CPPUNIT_TEST(testSecondPresence);
+ CPPUNIT_TEST(testSubscriptionRequest);
+ CPPUNIT_TEST_SUITE_END();
+
+ private:
+ PresenceOracle* oracle_;
+ DummyStanzaChannel* stanzaChannel_;
+
+ public:
+
+ void handlePresenceChange(boost::shared_ptr<Presence> newPresence, boost::shared_ptr<Presence> lastPresence, PresencePointerPair* out) {
+ CPPUNIT_ASSERT(out->one.get() == NULL);
+ CPPUNIT_ASSERT(out->two.get() == NULL);
+ out->one = newPresence;
+ out->two = lastPresence;
+ CPPUNIT_ASSERT(newPresence.get());
+ CPPUNIT_ASSERT_EQUAL(newPresence, out->one);
+ }
+
+ void handlePresenceSubscriptionRequest(const JID& jid, const String& reason, SubscriptionRequestInfo* info) {
+ CPPUNIT_ASSERT(!info->jid);
+ CPPUNIT_ASSERT(!info->reason);
+ info->jid = jid;
+ info->reason = reason;
+ }
+
+ void setUp() {
+ stanzaChannel_ = new DummyStanzaChannel();
+ oracle_ = new PresenceOracle(stanzaChannel_);
+ }
+
+ void tearDown() {
+ delete oracle_;
+ delete stanzaChannel_;
+ }
+
+ void testFirstPresence() {
+ PresencePointerPair out;
+ oracle_->onPresenceChange.connect(boost::bind(&PresenceOracleTest::handlePresenceChange, this, _1, _2, &out));
+
+ SubscriptionRequestInfo info;
+ oracle_->onPresenceSubscriptionRequest.connect(boost::bind(&PresenceOracleTest::handlePresenceSubscriptionRequest, this, _1, _2, &info));
+
+ boost::shared_ptr<Presence> sentPresence(new Presence("blarb"));
+ stanzaChannel_->onPresenceReceived(sentPresence);
+
+ CPPUNIT_ASSERT(!info.jid);
+ CPPUNIT_ASSERT(!info.reason);
+ CPPUNIT_ASSERT(out.two.get() == NULL);
+ CPPUNIT_ASSERT_EQUAL(sentPresence, out.one);
+ }
+
+ void testSecondPresence() {
+ PresencePointerPair out;
+ oracle_->onPresenceChange.connect(boost::bind(&PresenceOracleTest::handlePresenceChange, this, _1, _2, &out));
+
+ boost::shared_ptr<Presence> sentPresence1(new Presence("blarb"));
+ stanzaChannel_->onPresenceReceived(sentPresence1);
+ CPPUNIT_ASSERT_EQUAL(sentPresence1, out.one);
+ out.one = boost::shared_ptr<Presence>();
+
+ SubscriptionRequestInfo info;
+ oracle_->onPresenceSubscriptionRequest.connect(boost::bind(&PresenceOracleTest::handlePresenceSubscriptionRequest, this, _1, _2, &info));
+
+ boost::shared_ptr<Presence> sentPresence2(new Presence("test2"));
+ stanzaChannel_->onPresenceReceived(sentPresence2);
+
+ CPPUNIT_ASSERT(!info.jid);
+ CPPUNIT_ASSERT(!info.reason);
+ CPPUNIT_ASSERT_EQUAL(sentPresence1, out.two);
+ CPPUNIT_ASSERT_EQUAL(sentPresence2, out.one);
+ }
+
+ void testSubscriptionRequest() {
+ PresencePointerPair out;
+ oracle_->onPresenceChange.connect(boost::bind(&PresenceOracleTest::handlePresenceChange, this, _1, _2, &out));
+ SubscriptionRequestInfo info;
+ oracle_->onPresenceSubscriptionRequest.connect(boost::bind(&PresenceOracleTest::handlePresenceSubscriptionRequest, this, _1, _2, &info));
+
+ String reasonText = "Because I want to";
+ JID sentJID = JID("me@example.com");
+
+ boost::shared_ptr<Presence> sentPresence(new Presence());
+ sentPresence->setType(Presence::Subscribe);
+ sentPresence->setFrom(sentJID);
+ sentPresence->setStatus(reasonText);
+ stanzaChannel_->onPresenceReceived(sentPresence);
+
+ CPPUNIT_ASSERT(info.jid);
+ CPPUNIT_ASSERT(info.reason);
+ CPPUNIT_ASSERT_EQUAL(sentJID, info.jid.get());
+ CPPUNIT_ASSERT_EQUAL(reasonText, info.reason.get());
+
+ CPPUNIT_ASSERT(!out.two);
+ CPPUNIT_ASSERT(!out.one);
+ }
+};
+CPPUNIT_TEST_SUITE_REGISTRATION(PresenceOracleTest);
+
diff --git a/Swiften/Presence/UnitTest/PresenceSenderTest.cpp b/Swiften/Presence/UnitTest/PresenceSenderTest.cpp
new file mode 100644
index 0000000..1df0269
--- /dev/null
+++ b/Swiften/Presence/UnitTest/PresenceSenderTest.cpp
@@ -0,0 +1,120 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Client/DummyStanzaChannel.h"
+#include "Swiften/Presence/PresenceSender.h"
+
+using namespace Swift;
+
+class PresenceSenderTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(PresenceSenderTest);
+ CPPUNIT_TEST(testSendPresence);
+ CPPUNIT_TEST(testSendPresence_UndirectedPresenceWithDirectedPresenceReceivers);
+ CPPUNIT_TEST(testSendPresence_DirectedPresenceWithDirectedPresenceReceivers);
+ CPPUNIT_TEST(testAddDirectedPresenceReceiver);
+ CPPUNIT_TEST(testAddDirectedPresenceReceiver_AfterSendingDirectedPresence);
+ CPPUNIT_TEST(testRemoveDirectedPresenceReceiver);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void setUp() {
+ channel = new DummyStanzaChannel();
+ testPresence = boost::shared_ptr<Presence>(new Presence());
+ testPresence->setStatus("Foo");
+ secondTestPresence = boost::shared_ptr<Presence>(new Presence());
+ secondTestPresence->setStatus("Bar");
+ }
+
+ void tearDown() {
+ delete channel;
+ }
+
+ void testSendPresence() {
+ std::auto_ptr<PresenceSender> testling(createPresenceSender());
+ testling->sendPresence(testPresence);
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel->sentStanzas.size()));
+ boost::shared_ptr<Presence> presence = boost::dynamic_pointer_cast<Presence>(channel->sentStanzas[0]);
+ CPPUNIT_ASSERT(testPresence == presence);
+ }
+
+ void testSendPresence_UndirectedPresenceWithDirectedPresenceReceivers() {
+ std::auto_ptr<PresenceSender> testling(createPresenceSender());
+ testling->addDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"));
+
+ testling->sendPresence(testPresence);
+
+ CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(channel->sentStanzas.size()));
+ boost::shared_ptr<Presence> presence = boost::dynamic_pointer_cast<Presence>(channel->sentStanzas[0]);
+ CPPUNIT_ASSERT(testPresence == presence);
+ presence = boost::dynamic_pointer_cast<Presence>(channel->sentStanzas[1]);
+ CPPUNIT_ASSERT_EQUAL(testPresence->getStatus(), presence->getStatus());
+ CPPUNIT_ASSERT_EQUAL(JID("alice@wonderland.lit/teaparty"), presence->getTo());
+ }
+
+ void testSendPresence_DirectedPresenceWithDirectedPresenceReceivers() {
+ std::auto_ptr<PresenceSender> testling(createPresenceSender());
+ testling->addDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"));
+ channel->sentStanzas.clear();
+
+ testPresence->setTo(JID("foo@bar.com"));
+ testling->sendPresence(testPresence);
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel->sentStanzas.size()));
+ boost::shared_ptr<Presence> presence = boost::dynamic_pointer_cast<Presence>(channel->sentStanzas[0]);
+ CPPUNIT_ASSERT(testPresence == presence);
+ }
+
+ void testAddDirectedPresenceReceiver() {
+ std::auto_ptr<PresenceSender> testling(createPresenceSender());
+ testling->sendPresence(testPresence);
+ channel->sentStanzas.clear();
+
+ testling->addDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"));
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel->sentStanzas.size()));
+ boost::shared_ptr<Presence> presence = boost::dynamic_pointer_cast<Presence>(channel->sentStanzas[0]);
+ CPPUNIT_ASSERT_EQUAL(testPresence->getStatus(), presence->getStatus());
+ CPPUNIT_ASSERT_EQUAL(JID("alice@wonderland.lit/teaparty"), presence->getTo());
+ }
+
+ void testAddDirectedPresenceReceiver_AfterSendingDirectedPresence() {
+ std::auto_ptr<PresenceSender> testling(createPresenceSender());
+ testling->sendPresence(testPresence);
+ secondTestPresence->setTo(JID("foo@bar.com"));
+ testling->sendPresence(secondTestPresence);
+ channel->sentStanzas.clear();
+
+ testling->addDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"));
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel->sentStanzas.size()));
+ boost::shared_ptr<Presence> presence = boost::dynamic_pointer_cast<Presence>(channel->sentStanzas[0]);
+ CPPUNIT_ASSERT_EQUAL(testPresence->getStatus(), presence->getStatus());
+ CPPUNIT_ASSERT_EQUAL(JID("alice@wonderland.lit/teaparty"), presence->getTo());
+ }
+
+ void testRemoveDirectedPresenceReceiver() {
+ std::auto_ptr<PresenceSender> testling(createPresenceSender());
+ testling->addDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"));
+ channel->sentStanzas.clear();
+
+ testling->removeDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"));
+ testling->sendPresence(testPresence);
+
+ CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(channel->sentStanzas.size()));
+ CPPUNIT_ASSERT_EQUAL(boost::dynamic_pointer_cast<Presence>(channel->sentStanzas[0])->getType(), Presence::Unavailable);
+ CPPUNIT_ASSERT(channel->sentStanzas[1] == testPresence);
+ }
+
+ private:
+ PresenceSender* createPresenceSender() {
+ return new PresenceSender(channel);
+ }
+
+ private:
+ DummyStanzaChannel* channel;
+ boost::shared_ptr<Presence> testPresence;
+ boost::shared_ptr<Presence> secondTestPresence;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(PresenceSenderTest);