summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Swift/Controllers/RosterController.cpp13
-rw-r--r--Swift/Controllers/UnitTest/RosterControllerTest.cpp63
-rw-r--r--Swiften/Presence/PresenceOracle.cpp28
-rw-r--r--Swiften/Presence/PresenceOracle.h1
4 files changed, 93 insertions, 12 deletions
diff --git a/Swift/Controllers/RosterController.cpp b/Swift/Controllers/RosterController.cpp
index 9e50f0e..dfe34b0 100644
--- a/Swift/Controllers/RosterController.cpp
+++ b/Swift/Controllers/RosterController.cpp
@@ -236,12 +236,15 @@ void RosterController::handleIncomingPresence(Presence::ref newPresence) {
return;
}
Presence::ref appliedPresence(newPresence);
- if (newPresence->getType() == Presence::Unsubscribe) {
- /* In 3921bis, subscription removal isn't followed by a presence push of unavailable*/
- appliedPresence = boost::shared_ptr<Presence>(new Presence());
- appliedPresence->setFrom(newPresence->getFrom().toBare());
+ JID newJID(appliedPresence->getFrom());
+ Presence::ref highestPresence = presenceOracle_->getHighestPriorityPresence(appliedPresence->getFrom().toBare());
+ JID highestJID(highestPresence->getFrom());
+ bool highestPriority = (newJID == highestJID || highestPresence->getType() == Presence::Unavailable);
+ if (!highestPriority) {
+ /* Update in case there are full-JID roster entries.*/
+ roster_->applyOnItems(SetPresence(appliedPresence, JID::WithResource));
}
- roster_->applyOnItems(SetPresence(appliedPresence));
+ roster_->applyOnItems(SetPresence(highestPresence));
}
void RosterController::handleSubscriptionRequest(const JID& jid, const String& message) {
diff --git a/Swift/Controllers/UnitTest/RosterControllerTest.cpp b/Swift/Controllers/UnitTest/RosterControllerTest.cpp
index 92336f6..e4770d7 100644
--- a/Swift/Controllers/UnitTest/RosterControllerTest.cpp
+++ b/Swift/Controllers/UnitTest/RosterControllerTest.cpp
@@ -43,6 +43,9 @@ class RosterControllerTest : public CppUnit::TestFixture
CPPUNIT_TEST(testReceiveRename);
CPPUNIT_TEST(testSendRename);
CPPUNIT_TEST(testSendRegroup);
+ CPPUNIT_TEST(testPresence);
+ CPPUNIT_TEST(testHighestPresence);
+ CPPUNIT_TEST(testNotHighestPresence);
CPPUNIT_TEST_SUITE_END();
public:
@@ -88,6 +91,66 @@ class RosterControllerTest : public CppUnit::TestFixture
return dynamic_cast<GroupRosterItem*>(CHILDREN[i]);
}
+ JID withResource(const JID& jid, const String& resource) {
+ return JID(jid.toBare().toString() + "/" + resource);
+ }
+
+ void testPresence() {
+ std::vector<String> groups;
+ groups.push_back("testGroup1");
+ groups.push_back("testGroup2");
+ JID from("test@testdomain.com");
+ xmppRoster_->addContact(from, "name", groups, RosterItemPayload::Both);
+ Presence::ref presence(new Presence());
+ presence->setFrom(withResource(from, "bob"));
+ presence->setPriority(2);
+ presence->setStatus("So totally here");
+ stanzaChannel_->onPresenceReceived(presence);
+ ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(CHILDREN[0])->getChildren()[0]);
+ CPPUNIT_ASSERT_EQUAL(presence->getStatus(), item->getStatusText());
+ ContactRosterItem* item2 = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(CHILDREN[1])->getChildren()[0]);
+ CPPUNIT_ASSERT_EQUAL(presence->getStatus(), item2->getStatusText());
+
+ };
+
+ void testHighestPresence() {
+ std::vector<String> groups;
+ groups.push_back("testGroup1");
+ JID from("test@testdomain.com");
+ xmppRoster_->addContact(from, "name", groups, RosterItemPayload::Both);
+ Presence::ref lowPresence(new Presence());
+ lowPresence->setFrom(withResource(from, "bob"));
+ lowPresence->setPriority(2);
+ lowPresence->setStatus("Not here");
+ Presence::ref highPresence(new Presence());
+ highPresence->setFrom(withResource(from, "bert"));
+ highPresence->setPriority(10);
+ highPresence->setStatus("So totally here");
+ stanzaChannel_->onPresenceReceived(lowPresence);
+ stanzaChannel_->onPresenceReceived(highPresence);
+ ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(CHILDREN[0])->getChildren()[0]);
+ CPPUNIT_ASSERT_EQUAL(highPresence->getStatus(), item->getStatusText());
+ };
+
+ void testNotHighestPresence() {
+ std::vector<String> groups;
+ groups.push_back("testGroup1");
+ JID from("test@testdomain.com");
+ xmppRoster_->addContact(from, "name", groups, RosterItemPayload::Both);
+ Presence::ref lowPresence(new Presence());
+ lowPresence->setFrom(withResource(from, "bob"));
+ lowPresence->setPriority(2);
+ lowPresence->setStatus("Not here");
+ Presence::ref highPresence(new Presence());
+ highPresence->setFrom(withResource(from, "bert"));
+ highPresence->setPriority(10);
+ highPresence->setStatus("So totally here");
+ stanzaChannel_->onPresenceReceived(highPresence);
+ stanzaChannel_->onPresenceReceived(lowPresence);
+ ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(CHILDREN[0])->getChildren()[0]);
+ CPPUNIT_ASSERT_EQUAL(highPresence->getStatus(), item->getStatusText());
+ };
+
void testAdd() {
std::vector<String> groups;
groups.push_back("testGroup1");
diff --git a/Swiften/Presence/PresenceOracle.cpp b/Swiften/Presence/PresenceOracle.cpp
index 439a84d..83bbbf7 100644
--- a/Swiften/Presence/PresenceOracle.cpp
+++ b/Swiften/Presence/PresenceOracle.cpp
@@ -30,17 +30,31 @@ void PresenceOracle::handleStanzaChannelAvailableChanged(bool available) {
}
-void PresenceOracle::handleIncomingPresence(boost::shared_ptr<Presence> presence) {
- JID bareJID = JID(presence->getFrom().toBare());
-
+void PresenceOracle::handleIncomingPresence(Presence::ref presence) {
+ JID bareJID(presence->getFrom().toBare());
if (presence->getType() == Presence::Subscribe) {
onPresenceSubscriptionRequest(bareJID, presence->getStatus());
- }
- else {
+ } else {
+ Presence::ref passedPresence = presence;
+ if (presence->getType() == Presence::Unsubscribe) {
+ /* 3921bis says that we don't follow up with an unavailable, so simulate this ourselves */
+ onPresenceSubscriptionRevoked(bareJID, presence->getStatus());
+ passedPresence = Presence::ref(new Presence());
+ passedPresence->setType(Presence::Unavailable);
+ passedPresence->setFrom(bareJID);
+ passedPresence->setStatus(presence->getStatus());
+ }
std::map<JID, boost::shared_ptr<Presence> > jidMap = entries_[bareJID];
- jidMap[presence->getFrom()] = presence;
+ if (passedPresence->getFrom().isBare() && presence->getType() == Presence::Unavailable) {
+ /* Have a bare-JID only presence of offline */
+ jidMap.clear();
+ } else if (passedPresence->getType() == Presence::Available) {
+ /* Don't have a bare-JID only offline presence once there are available presences */
+ jidMap.erase(bareJID);
+ }
+ jidMap[passedPresence->getFrom()] = passedPresence;
entries_[bareJID] = jidMap;
- onPresenceChange(presence);
+ onPresenceChange(passedPresence);
}
}
diff --git a/Swiften/Presence/PresenceOracle.h b/Swiften/Presence/PresenceOracle.h
index 8c4fce4..b0d1e15 100644
--- a/Swiften/Presence/PresenceOracle.h
+++ b/Swiften/Presence/PresenceOracle.h
@@ -26,6 +26,7 @@ class StanzaChannel;
public:
boost::signal<void (boost::shared_ptr<Presence>)> onPresenceChange;
boost::signal<void (const JID&, const String&)> onPresenceSubscriptionRequest;
+ boost::signal<void (const JID&, const String&)> onPresenceSubscriptionRevoked;
private:
void handleIncomingPresence(boost::shared_ptr<Presence> presence);