summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRemko Tronçon <git@el-tramo.be>2011-04-08 21:58:03 (GMT)
committerRemko Tronçon <git@el-tramo.be>2011-04-18 19:11:40 (GMT)
commit01385d84f07466550fff702079555c65963463a8 (patch)
treed995334b205737b2402a8f5778642342b9b3ab02 /Swiften
parent1a92834490cda1352a46eaef02a4e7ff76ab94e3 (diff)
downloadswift-contrib-01385d84f07466550fff702079555c65963463a8.zip
swift-contrib-01385d84f07466550fff702079555c65963463a8.tar.bz2
Added XEP-0237 Roster Versioning support.
Resolves: #803 Release-Notes: Added support for XEP-0237 Roster Versioning
Diffstat (limited to 'Swiften')
-rw-r--r--Swiften/Client/Client.cpp8
-rw-r--r--Swiften/Client/ClientSession.cpp2
-rw-r--r--Swiften/Client/ClientSession.h5
-rw-r--r--Swiften/Client/CoreClient.h5
-rw-r--r--Swiften/Client/FileStorages.cpp7
-rw-r--r--Swiften/Client/FileStorages.h3
-rw-r--r--Swiften/Client/MemoryStorages.cpp8
-rw-r--r--Swiften/Client/MemoryStorages.h2
-rw-r--r--Swiften/Client/Storages.cpp12
-rw-r--r--Swiften/Client/Storages.h4
-rw-r--r--Swiften/Elements/RosterItemPayload.h14
-rw-r--r--Swiften/Elements/RosterPayload.h9
-rw-r--r--Swiften/Elements/StreamFeatures.h11
-rw-r--r--Swiften/Parser/PayloadParsers/RosterParser.cpp8
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/RosterParserTest.cpp22
-rw-r--r--Swiften/Parser/StreamFeaturesParser.cpp3
-rw-r--r--Swiften/Parser/UnitTest/StreamFeaturesParserTest.cpp2
-rw-r--r--Swiften/Roster/GetRosterRequest.h6
-rw-r--r--Swiften/Roster/RosterFileStorage.cpp24
-rw-r--r--Swiften/Roster/RosterFileStorage.h29
-rw-r--r--Swiften/Roster/RosterMemoryStorage.cpp23
-rw-r--r--Swiften/Roster/RosterMemoryStorage.h25
-rw-r--r--Swiften/Roster/RosterStorage.cpp14
-rw-r--r--Swiften/Roster/RosterStorage.h21
-rw-r--r--Swiften/Roster/UnitTest/XMPPRosterControllerTest.cpp173
-rw-r--r--Swiften/Roster/UnitTest/XMPPRosterSignalHandler.cpp3
-rw-r--r--Swiften/Roster/UnitTest/XMPPRosterSignalHandler.h8
-rw-r--r--Swiften/Roster/XMPPRosterController.cpp47
-rw-r--r--Swiften/Roster/XMPPRosterController.h12
-rw-r--r--Swiften/SConscript4
-rw-r--r--Swiften/Serializer/PayloadSerializers/RosterSerializer.cpp3
-rw-r--r--Swiften/Serializer/PayloadSerializers/UnitTest/RosterSerializerTest.cpp27
-rw-r--r--Swiften/Serializer/StreamFeaturesSerializer.cpp5
-rw-r--r--Swiften/Serializer/UnitTest/StreamFeaturesSerializerTest.cpp2
34 files changed, 519 insertions, 32 deletions
diff --git a/Swiften/Client/Client.cpp b/Swiften/Client/Client.cpp
index 7918c46..c6ada7c 100644
--- a/Swiften/Client/Client.cpp
+++ b/Swiften/Client/Client.cpp
@@ -25,6 +25,7 @@
#include "Swiften/Presence/SubscriptionManager.h"
#include "Swiften/TLS/BlindCertificateTrustChecker.h"
#include <Swiften/Client/NickManagerImpl.h>
+#include <Swiften/Client/ClientSession.h>
namespace Swift {
@@ -35,7 +36,7 @@ Client::Client(const JID& jid, const std::string& password, NetworkFactories* ne
softwareVersionResponder->start();
roster = new XMPPRosterImpl();
- rosterController = new XMPPRosterController(getIQRouter(), roster);
+ rosterController = new XMPPRosterController(getIQRouter(), roster, storages->getRosterStorage());
subscriptionManager = new SubscriptionManager(getStanzaChannel());
@@ -98,6 +99,11 @@ void Client::setSoftwareVersion(const std::string& name, const std::string& vers
}
void Client::requestRoster() {
+ // FIXME: We should set this once when the session is finished, but there
+ // is currently no callback for this
+ if (getSession()) {
+ rosterController->setUseVersioning(getSession()->getRosterVersioningSupported());
+ }
rosterController->requestRoster();
}
diff --git a/Swiften/Client/ClientSession.cpp b/Swiften/Client/ClientSession.cpp
index e1c1d8e..cadc9a4 100644
--- a/Swiften/Client/ClientSession.cpp
+++ b/Swiften/Client/ClientSession.cpp
@@ -54,6 +54,7 @@ ClientSession::ClientSession(
needSessionStart(false),
needResourceBind(false),
needAcking(false),
+ rosterVersioningSupported(false),
authenticator(NULL),
certificateTrustChecker(NULL) {
}
@@ -223,6 +224,7 @@ void ClientSession::handleElement(boost::shared_ptr<Element> element) {
}
else {
// Start the session
+ rosterVersioningSupported = streamFeatures->hasRosterVersioning();
stream->setWhitespacePingEnabled(true);
needSessionStart = streamFeatures->hasSession();
needResourceBind = streamFeatures->hasResourceBind();
diff --git a/Swiften/Client/ClientSession.h b/Swiften/Client/ClientSession.h
index 25ee694..4447f57 100644
--- a/Swiften/Client/ClientSession.h
+++ b/Swiften/Client/ClientSession.h
@@ -89,6 +89,10 @@ namespace Swift {
return stanzaAckRequester_;
}
+ bool getRosterVersioningSupported() const {
+ return rosterVersioningSupported;
+ }
+
const JID& getLocalJID() const {
return localJID;
}
@@ -153,6 +157,7 @@ namespace Swift {
bool needSessionStart;
bool needResourceBind;
bool needAcking;
+ bool rosterVersioningSupported;
ClientAuthenticator* authenticator;
boost::shared_ptr<StanzaAckRequester> stanzaAckRequester_;
boost::shared_ptr<StanzaAckResponder> stanzaAckResponder_;
diff --git a/Swiften/Client/CoreClient.h b/Swiften/Client/CoreClient.h
index 060c699..ceb0b0a 100644
--- a/Swiften/Client/CoreClient.h
+++ b/Swiften/Client/CoreClient.h
@@ -196,6 +196,11 @@ namespace Swift {
*/
boost::signal<void (boost::shared_ptr<Stanza>)> onStanzaAcked;
+ protected:
+ boost::shared_ptr<ClientSession> getSession() const {
+ return session_;
+ }
+
private:
void handleConnectorFinished(boost::shared_ptr<Connection>);
void handleStanzaChannelAvailableChanged(bool available);
diff --git a/Swiften/Client/FileStorages.cpp b/Swiften/Client/FileStorages.cpp
index 3c76c46..e6a93fd 100644
--- a/Swiften/Client/FileStorages.cpp
+++ b/Swiften/Client/FileStorages.cpp
@@ -8,6 +8,7 @@
#include "Swiften/VCards/VCardFileStorage.h"
#include "Swiften/Avatars/AvatarFileStorage.h"
#include "Swiften/Disco/CapsFileStorage.h"
+#include "Swiften/Roster/RosterFileStorage.h"
namespace Swift {
@@ -16,9 +17,11 @@ FileStorages::FileStorages(const boost::filesystem::path& baseDir, const JID& ji
vcardStorage = new VCardFileStorage(baseDir / profile / "vcards");
capsStorage = new CapsFileStorage(baseDir / "caps");
avatarStorage = new AvatarFileStorage(baseDir / "avatars", baseDir / profile / "avatars");
+ rosterStorage = new RosterFileStorage(baseDir / profile / "roster.xml");
}
FileStorages::~FileStorages() {
+ delete rosterStorage;
delete avatarStorage;
delete capsStorage;
delete vcardStorage;
@@ -36,4 +39,8 @@ AvatarStorage* FileStorages::getAvatarStorage() const {
return avatarStorage;
}
+RosterStorage* FileStorages::getRosterStorage() const {
+ return rosterStorage;
+}
+
}
diff --git a/Swiften/Client/FileStorages.h b/Swiften/Client/FileStorages.h
index fc05c86..28df314 100644
--- a/Swiften/Client/FileStorages.h
+++ b/Swiften/Client/FileStorages.h
@@ -14,6 +14,7 @@ namespace Swift {
class VCardFileStorage;
class AvatarFileStorage;
class CapsFileStorage;
+ class RosterFileStorage;
class JID;
/**
@@ -41,10 +42,12 @@ namespace Swift {
virtual VCardStorage* getVCardStorage() const;
virtual AvatarStorage* getAvatarStorage() const;
virtual CapsStorage* getCapsStorage() const;
+ virtual RosterStorage* getRosterStorage() const;
private:
VCardFileStorage* vcardStorage;
AvatarFileStorage* avatarStorage;
CapsFileStorage* capsStorage;
+ RosterFileStorage* rosterStorage;
};
}
diff --git a/Swiften/Client/MemoryStorages.cpp b/Swiften/Client/MemoryStorages.cpp
index 5f6371b..6941add 100644
--- a/Swiften/Client/MemoryStorages.cpp
+++ b/Swiften/Client/MemoryStorages.cpp
@@ -8,6 +8,7 @@
#include "Swiften/VCards/VCardMemoryStorage.h"
#include "Swiften/Avatars/AvatarMemoryStorage.h"
#include "Swiften/Disco/CapsMemoryStorage.h"
+#include "Swiften/Roster/RosterMemoryStorage.h"
namespace Swift {
@@ -15,9 +16,11 @@ MemoryStorages::MemoryStorages() {
vcardStorage = new VCardMemoryStorage();
capsStorage = new CapsMemoryStorage();
avatarStorage = new AvatarMemoryStorage();
+ rosterStorage = new RosterMemoryStorage();
}
MemoryStorages::~MemoryStorages() {
+ delete rosterStorage;
delete avatarStorage;
delete capsStorage;
delete vcardStorage;
@@ -35,4 +38,9 @@ AvatarStorage* MemoryStorages::getAvatarStorage() const {
return avatarStorage;
}
+RosterStorage* MemoryStorages::getRosterStorage() const {
+ return rosterStorage;
+}
+
+
}
diff --git a/Swiften/Client/MemoryStorages.h b/Swiften/Client/MemoryStorages.h
index 67025cd..1e1a596 100644
--- a/Swiften/Client/MemoryStorages.h
+++ b/Swiften/Client/MemoryStorages.h
@@ -23,10 +23,12 @@ namespace Swift {
virtual VCardStorage* getVCardStorage() const;
virtual AvatarStorage* getAvatarStorage() const;
virtual CapsStorage* getCapsStorage() const;
+ virtual RosterStorage* getRosterStorage() const;
private:
VCardMemoryStorage* vcardStorage;
AvatarStorage* avatarStorage;
CapsStorage* capsStorage;
+ RosterStorage* rosterStorage;
};
}
diff --git a/Swiften/Client/Storages.cpp b/Swiften/Client/Storages.cpp
new file mode 100644
index 0000000..3c2dbc5
--- /dev/null
+++ b/Swiften/Client/Storages.cpp
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <Swiften/Client/Storages.h>
+
+using namespace Swift;
+
+Storages::~Storages() {
+}
diff --git a/Swiften/Client/Storages.h b/Swiften/Client/Storages.h
index e62f0a9..1c5bbe9 100644
--- a/Swiften/Client/Storages.h
+++ b/Swiften/Client/Storages.h
@@ -10,6 +10,7 @@ namespace Swift {
class VCardStorage;
class AvatarStorage;
class CapsStorage;
+ class RosterStorage;
/**
* An interface to hold storage classes for different
@@ -17,10 +18,11 @@ namespace Swift {
*/
class Storages {
public:
- virtual ~Storages() {}
+ virtual ~Storages();
virtual VCardStorage* getVCardStorage() const = 0;
virtual AvatarStorage* getAvatarStorage() const = 0;
virtual CapsStorage* getCapsStorage() const = 0;
+ virtual RosterStorage* getRosterStorage() const = 0;
};
}
diff --git a/Swiften/Elements/RosterItemPayload.h b/Swiften/Elements/RosterItemPayload.h
index b8a1b10..915ae31 100644
--- a/Swiften/Elements/RosterItemPayload.h
+++ b/Swiften/Elements/RosterItemPayload.h
@@ -4,22 +4,20 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#ifndef SWIFTEN_RosterItemPayloadPayload_H
-#define SWIFTEN_RosterItemPayloadPayload_H
+#pragma once
#include <vector>
-
-#include "Swiften/JID/JID.h"
#include <string>
+#include <Swiften/JID/JID.h>
+
namespace Swift {
- class RosterItemPayload
- {
+ class RosterItemPayload {
public:
enum Subscription { None, To, From, Both, Remove };
RosterItemPayload() : subscription_(None), ask_(false) {}
- RosterItemPayload(const JID& jid, const std::string& name, Subscription subscription) : jid_(jid), name_(name), subscription_(subscription), ask_(false) { }
+ RosterItemPayload(const JID& jid, const std::string& name, Subscription subscription, const std::vector<std::string>& groups = std::vector<std::string>()) : jid_(jid), name_(name), subscription_(subscription), groups_(groups), ask_(false) { }
void setJID(const JID& jid) { jid_ = jid; }
const JID& getJID() const { return jid_; }
@@ -51,5 +49,3 @@ namespace Swift {
std::string unknownContent_;
};
}
-
-#endif
diff --git a/Swiften/Elements/RosterPayload.h b/Swiften/Elements/RosterPayload.h
index b46b384..3102f0e 100644
--- a/Swiften/Elements/RosterPayload.h
+++ b/Swiften/Elements/RosterPayload.h
@@ -33,7 +33,16 @@ namespace Swift {
return items_;
}
+ const boost::optional<std::string>& getVersion() const {
+ return version_;
+ }
+
+ void setVersion(const std::string& version) {
+ version_ = version;
+ }
+
private:
RosterItemPayloads items_;
+ boost::optional<std::string> version_;
};
}
diff --git a/Swiften/Elements/StreamFeatures.h b/Swiften/Elements/StreamFeatures.h
index 4bb21f0..5dc5a26 100644
--- a/Swiften/Elements/StreamFeatures.h
+++ b/Swiften/Elements/StreamFeatures.h
@@ -17,7 +17,7 @@ namespace Swift {
public:
typedef boost::shared_ptr<StreamFeatures> ref;
- StreamFeatures() : hasStartTLS_(false), hasResourceBind_(false), hasSession_(false), hasStreamManagement_(false) {}
+ StreamFeatures() : hasStartTLS_(false), hasResourceBind_(false), hasSession_(false), hasStreamManagement_(false), hasRosterVersioning_(false) {}
void setHasStartTLS() {
hasStartTLS_ = true;
@@ -75,6 +75,14 @@ namespace Swift {
hasStreamManagement_ = true;
}
+ bool hasRosterVersioning() const {
+ return hasRosterVersioning_;
+ }
+
+ void setHasRosterVersioning() {
+ hasRosterVersioning_ = true;
+ }
+
private:
bool hasStartTLS_;
std::vector<std::string> compressionMethods_;
@@ -82,5 +90,6 @@ namespace Swift {
bool hasResourceBind_;
bool hasSession_;
bool hasStreamManagement_;
+ bool hasRosterVersioning_;
};
}
diff --git a/Swiften/Parser/PayloadParsers/RosterParser.cpp b/Swiften/Parser/PayloadParsers/RosterParser.cpp
index ba19fbf..0da9f48 100644
--- a/Swiften/Parser/PayloadParsers/RosterParser.cpp
+++ b/Swiften/Parser/PayloadParsers/RosterParser.cpp
@@ -13,7 +13,13 @@ RosterParser::RosterParser() : level_(TopLevel), inItem_(false), unknownContentP
}
void RosterParser::handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) {
- if (level_ == PayloadLevel) {
+ if (level_ == TopLevel) {
+ AttributeMap::const_iterator i = attributes.find("ver");
+ if (i != attributes.end()) {
+ getPayloadInternal()->setVersion(i->second);
+ }
+ }
+ else if (level_ == PayloadLevel) {
if (element == "item") {
inItem_ = true;
currentItem_ = RosterItemPayload();
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/RosterParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/RosterParserTest.cpp
index 1bcea0e..3102b74 100644
--- a/Swiften/Parser/PayloadParsers/UnitTest/RosterParserTest.cpp
+++ b/Swiften/Parser/PayloadParsers/UnitTest/RosterParserTest.cpp
@@ -17,6 +17,8 @@ class RosterParserTest : public CppUnit::TestFixture
CPPUNIT_TEST_SUITE(RosterParserTest);
CPPUNIT_TEST(testParse);
CPPUNIT_TEST(testParse_ItemWithUnknownContent);
+ CPPUNIT_TEST(testParse_WithVersion);
+ CPPUNIT_TEST(testParse_WithEmptyVersion);
CPPUNIT_TEST_SUITE_END();
public:
@@ -32,6 +34,8 @@ class RosterParserTest : public CppUnit::TestFixture
"</query>"));
RosterPayload* payload = dynamic_cast<RosterPayload*>(parser.getPayload().get());
+
+ CPPUNIT_ASSERT(!payload->getVersion());
const RosterPayload::RosterItemPayloads& items = payload->getItems();
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), items.size());
@@ -74,6 +78,24 @@ class RosterParserTest : public CppUnit::TestFixture
"<baz xmlns=\"jabber:iq:roster\"><fum xmlns=\"jabber:iq:roster\">foo</fum></baz>"
), items[0].getUnknownContent());
}
+
+ void testParse_WithVersion() {
+ PayloadsParserTester parser;
+ CPPUNIT_ASSERT(parser.parse("<query xmlns='jabber:iq:roster' ver='ver10'/>"));
+
+ RosterPayload* payload = dynamic_cast<RosterPayload*>(parser.getPayload().get());
+ CPPUNIT_ASSERT(payload->getVersion());
+ CPPUNIT_ASSERT_EQUAL(std::string("ver10"), *payload->getVersion());
+ }
+
+ void testParse_WithEmptyVersion() {
+ PayloadsParserTester parser;
+ CPPUNIT_ASSERT(parser.parse("<query xmlns='jabber:iq:roster' ver=''/>"));
+
+ RosterPayload* payload = dynamic_cast<RosterPayload*>(parser.getPayload().get());
+ CPPUNIT_ASSERT(payload->getVersion());
+ CPPUNIT_ASSERT_EQUAL(std::string(""), *payload->getVersion());
+ }
};
CPPUNIT_TEST_SUITE_REGISTRATION(RosterParserTest);
diff --git a/Swiften/Parser/StreamFeaturesParser.cpp b/Swiften/Parser/StreamFeaturesParser.cpp
index 377f215..1b3ad54 100644
--- a/Swiften/Parser/StreamFeaturesParser.cpp
+++ b/Swiften/Parser/StreamFeaturesParser.cpp
@@ -31,6 +31,9 @@ void StreamFeaturesParser::handleStartElement(const std::string& element, const
else if (element == "compression" && ns == "http://jabber.org/features/compress") {
inCompression_ = true;
}
+ else if (element == "ver" && ns == "urn:xmpp:features:rosterver") {
+ getElementGeneric()->setHasRosterVersioning();
+ }
}
else if (currentDepth_ == 2) {
if (inCompression_ && element == "method") {
diff --git a/Swiften/Parser/UnitTest/StreamFeaturesParserTest.cpp b/Swiften/Parser/UnitTest/StreamFeaturesParserTest.cpp
index 1cdaf54..9fdea88 100644
--- a/Swiften/Parser/UnitTest/StreamFeaturesParserTest.cpp
+++ b/Swiften/Parser/UnitTest/StreamFeaturesParserTest.cpp
@@ -37,6 +37,7 @@ class StreamFeaturesParserTest : public CppUnit::TestFixture {
"<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"/>"
"<sm xmlns='urn:xmpp:sm:2'/>"
"<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/>"
+ "<ver xmlns=\"urn:xmpp:features:rosterver\"/>"
"</stream:features>"));
StreamFeatures::ref element = boost::dynamic_pointer_cast<StreamFeatures>(testling.getElement());
@@ -49,6 +50,7 @@ class StreamFeaturesParserTest : public CppUnit::TestFixture {
CPPUNIT_ASSERT(element->hasAuthenticationMechanism("DIGEST-MD5"));
CPPUNIT_ASSERT(element->hasAuthenticationMechanism("PLAIN"));
CPPUNIT_ASSERT(element->hasStreamManagement());
+ CPPUNIT_ASSERT(element->hasRosterVersioning());
}
void testParse_Empty() {
diff --git a/Swiften/Roster/GetRosterRequest.h b/Swiften/Roster/GetRosterRequest.h
index 00cf77f..a3486f0 100644
--- a/Swiften/Roster/GetRosterRequest.h
+++ b/Swiften/Roster/GetRosterRequest.h
@@ -19,6 +19,12 @@ namespace Swift {
return ref(new GetRosterRequest(router));
}
+ static ref create(IQRouter* router, const std::string& version) {
+ ref result(new GetRosterRequest(router));
+ result->getPayloadGeneric()->setVersion(version);
+ return result;
+ }
+
private:
GetRosterRequest(IQRouter* router) :
GenericRequest<RosterPayload>(IQ::Get, JID(), boost::shared_ptr<Payload>(new RosterPayload()), router) {
diff --git a/Swiften/Roster/RosterFileStorage.cpp b/Swiften/Roster/RosterFileStorage.cpp
new file mode 100644
index 0000000..de7b442
--- /dev/null
+++ b/Swiften/Roster/RosterFileStorage.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <Swiften/Roster/RosterFileStorage.h>
+
+#include <boost/smart_ptr/make_shared.hpp>
+
+namespace Swift {
+
+RosterFileStorage::RosterFileStorage(const boost::filesystem::path& path) : path(path) {
+}
+
+// FIXME
+void RosterFileStorage::setRoster(boost::shared_ptr<RosterPayload> r) {
+ roster.reset();
+ if (r) {
+ roster = boost::make_shared<RosterPayload>(*r);
+ }
+}
+
+}
diff --git a/Swiften/Roster/RosterFileStorage.h b/Swiften/Roster/RosterFileStorage.h
new file mode 100644
index 0000000..dc91c27
--- /dev/null
+++ b/Swiften/Roster/RosterFileStorage.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/filesystem/path.hpp>
+
+#include <Swiften/Roster/RosterStorage.h>
+
+namespace Swift {
+ class RosterFileStorage : public RosterStorage {
+ public:
+ RosterFileStorage(const boost::filesystem::path& path);
+
+ // FIXME
+ virtual boost::shared_ptr<RosterPayload> getRoster() const {
+ return roster;
+ }
+
+ virtual void setRoster(boost::shared_ptr<RosterPayload>);
+
+ private:
+ boost::filesystem::path path;
+ boost::shared_ptr<RosterPayload> roster;
+ };
+}
diff --git a/Swiften/Roster/RosterMemoryStorage.cpp b/Swiften/Roster/RosterMemoryStorage.cpp
new file mode 100644
index 0000000..cbf4563
--- /dev/null
+++ b/Swiften/Roster/RosterMemoryStorage.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <Swiften/Roster/RosterMemoryStorage.h>
+
+#include <boost/smart_ptr/make_shared.hpp>
+
+namespace Swift {
+
+RosterMemoryStorage::RosterMemoryStorage() {
+}
+
+void RosterMemoryStorage::setRoster(boost::shared_ptr<RosterPayload> r) {
+ roster.reset();
+ if (r) {
+ roster = boost::make_shared<RosterPayload>(*r);
+ }
+}
+
+}
diff --git a/Swiften/Roster/RosterMemoryStorage.h b/Swiften/Roster/RosterMemoryStorage.h
new file mode 100644
index 0000000..b659d77
--- /dev/null
+++ b/Swiften/Roster/RosterMemoryStorage.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Roster/RosterStorage.h>
+
+namespace Swift {
+ class RosterMemoryStorage : public RosterStorage {
+ public:
+ RosterMemoryStorage();
+
+ virtual boost::shared_ptr<RosterPayload> getRoster() const {
+ return roster;
+ }
+
+ virtual void setRoster(boost::shared_ptr<RosterPayload>);
+
+ private:
+ boost::shared_ptr<RosterPayload> roster;
+ };
+}
diff --git a/Swiften/Roster/RosterStorage.cpp b/Swiften/Roster/RosterStorage.cpp
new file mode 100644
index 0000000..6bf58de
--- /dev/null
+++ b/Swiften/Roster/RosterStorage.cpp
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <Swiften/Roster/RosterStorage.h>
+
+namespace Swift {
+
+RosterStorage::~RosterStorage() {
+}
+
+}
diff --git a/Swiften/Roster/RosterStorage.h b/Swiften/Roster/RosterStorage.h
new file mode 100644
index 0000000..ba24cb3
--- /dev/null
+++ b/Swiften/Roster/RosterStorage.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/Elements/RosterPayload.h>
+
+namespace Swift {
+ class RosterStorage {
+ public:
+ virtual ~RosterStorage();
+
+ virtual boost::shared_ptr<RosterPayload> getRoster() const = 0;
+ virtual void setRoster(boost::shared_ptr<RosterPayload>) = 0;
+ };
+}
diff --git a/Swiften/Roster/UnitTest/XMPPRosterControllerTest.cpp b/Swiften/Roster/UnitTest/XMPPRosterControllerTest.cpp
index 4ef1cc1..4c98673 100644
--- a/Swiften/Roster/UnitTest/XMPPRosterControllerTest.cpp
+++ b/Swiften/Roster/UnitTest/XMPPRosterControllerTest.cpp
@@ -16,15 +16,24 @@
#include "Swiften/Client/DummyStanzaChannel.h"
#include "Swiften/Queries/IQRouter.h"
#include "Swiften/Roster/XMPPRosterImpl.h"
+#include <Swiften/Roster/RosterMemoryStorage.h>
using namespace Swift;
class XMPPRosterControllerTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(XMPPRosterControllerTest);
+ CPPUNIT_TEST(testGet_Response);
CPPUNIT_TEST(testGet_EmptyResponse);
+ CPPUNIT_TEST(testGet_NoRosterInStorage);
+ CPPUNIT_TEST(testGet_NoVersionInStorage);
+ CPPUNIT_TEST(testGet_VersionInStorage);
+ CPPUNIT_TEST(testGet_ServerDoesNotSupportVersion);
+ CPPUNIT_TEST(testGet_ResponseWithoutNewVersion);
+ CPPUNIT_TEST(testGet_ResponseWithNewVersion);
CPPUNIT_TEST(testAdd);
CPPUNIT_TEST(testModify);
CPPUNIT_TEST(testRemove);
+ CPPUNIT_TEST(testRemove_RosterStorageUpdated);
CPPUNIT_TEST(testMany);
CPPUNIT_TEST_SUITE_END();
@@ -34,20 +43,36 @@ class XMPPRosterControllerTest : public CppUnit::TestFixture {
router_ = new IQRouter(channel_);
xmppRoster_ = new XMPPRosterImpl();
handler_ = new XMPPRosterSignalHandler(xmppRoster_);
+ rosterStorage_ = new RosterMemoryStorage();
jid1_ = JID("foo@bar.com");
jid2_ = JID("alice@wonderland.lit");
jid3_ = JID("jane@austen.lit");
}
void tearDown() {
+ delete rosterStorage_;
delete handler_;
delete xmppRoster_;
delete router_;
delete channel_;
}
+ void testGet_Response() {
+ std::auto_ptr<XMPPRosterController> testling(createController());
+
+ testling->requestRoster();
+ boost::shared_ptr<RosterPayload> payload = boost::make_shared<RosterPayload>();
+ payload->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Both));
+ payload->addItem(RosterItemPayload(jid2_, "Alice", RosterItemPayload::Both));
+ channel_->onIQReceived(IQ::createResult("foo@bar.com", channel_->sentStanzas[0]->getID(), payload));
+
+ CPPUNIT_ASSERT_EQUAL(2, handler_->getEventCount());
+ CPPUNIT_ASSERT(xmppRoster_->getItem(jid1_));
+ CPPUNIT_ASSERT(xmppRoster_->getItem(jid2_));
+ }
+
void testGet_EmptyResponse() {
- XMPPRosterController controller(router_, xmppRoster_);
+ XMPPRosterController controller(router_, xmppRoster_, rosterStorage_);
controller.requestRoster();
@@ -55,7 +80,7 @@ class XMPPRosterControllerTest : public CppUnit::TestFixture {
}
void testAdd() {
- XMPPRosterController controller(router_, xmppRoster_);
+ XMPPRosterController controller(router_, xmppRoster_, rosterStorage_);
boost::shared_ptr<RosterPayload> payload(new RosterPayload());
payload->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Both));
@@ -68,8 +93,115 @@ class XMPPRosterControllerTest : public CppUnit::TestFixture {
CPPUNIT_ASSERT_EQUAL(std::string("Bob"), xmppRoster_->getNameForJID(jid1_));
}
+ void testGet_NoRosterInStorage() {
+ std::auto_ptr<XMPPRosterController> testling(createController());
+ testling->setUseVersioning(true);
+
+ testling->requestRoster();
+
+ boost::shared_ptr<RosterPayload> roster = channel_->sentStanzas[0]->getPayload<RosterPayload>();
+ CPPUNIT_ASSERT(roster->getVersion());
+ CPPUNIT_ASSERT_EQUAL(std::string(""), *roster->getVersion());
+ }
+
+ void testGet_NoVersionInStorage() {
+ std::auto_ptr<XMPPRosterController> testling(createController());
+ testling->setUseVersioning(true);
+ rosterStorage_->setRoster(boost::make_shared<RosterPayload>());
+
+ testling->requestRoster();
+
+ boost::shared_ptr<RosterPayload> roster = channel_->sentStanzas[0]->getPayload<RosterPayload>();
+ CPPUNIT_ASSERT(roster->getVersion());
+ CPPUNIT_ASSERT_EQUAL(std::string(""), *roster->getVersion());
+ }
+
+ void testGet_VersionInStorage() {
+ std::auto_ptr<XMPPRosterController> testling(createController());
+ testling->setUseVersioning(true);
+ boost::shared_ptr<RosterPayload> payload(new RosterPayload());
+ payload->setVersion("foover");
+ rosterStorage_->setRoster(payload);
+
+ testling->requestRoster();
+
+ boost::shared_ptr<RosterPayload> roster = channel_->sentStanzas[0]->getPayload<RosterPayload>();
+ CPPUNIT_ASSERT(roster->getVersion());
+ CPPUNIT_ASSERT_EQUAL(std::string("foover"), *roster->getVersion());
+ }
+
+ void testGet_ServerDoesNotSupportVersion() {
+ std::auto_ptr<XMPPRosterController> testling(createController());
+ boost::shared_ptr<RosterPayload> payload(new RosterPayload());
+ payload->setVersion("foover");
+ rosterStorage_->setRoster(payload);
+
+ testling->requestRoster();
+
+ boost::shared_ptr<RosterPayload> roster = channel_->sentStanzas[0]->getPayload<RosterPayload>();
+ CPPUNIT_ASSERT(!roster->getVersion());
+ }
+
+ void testGet_ResponseWithoutNewVersion() {
+ std::auto_ptr<XMPPRosterController> testling(createController());
+ testling->setUseVersioning(true);
+ boost::shared_ptr<RosterPayload> storedRoster(new RosterPayload());
+ storedRoster->setVersion("version10");
+ storedRoster->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Both));
+ storedRoster->addItem(RosterItemPayload(jid2_, "Alice", RosterItemPayload::Both));
+ rosterStorage_->setRoster(storedRoster);
+ testling->requestRoster();
+
+ channel_->onIQReceived(IQ::createResult("foo@bar.com", channel_->sentStanzas[0]->getID(), boost::shared_ptr<RosterPayload>()));
+
+ CPPUNIT_ASSERT_EQUAL(2, handler_->getEventCount());
+ CPPUNIT_ASSERT(xmppRoster_->getItem(jid1_));
+ CPPUNIT_ASSERT(xmppRoster_->getItem(jid2_));
+ CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent());
+ CPPUNIT_ASSERT_EQUAL(jid2_, handler_->getLastJID());
+ CPPUNIT_ASSERT(rosterStorage_->getRoster());
+ CPPUNIT_ASSERT(rosterStorage_->getRoster()->getVersion());
+ CPPUNIT_ASSERT_EQUAL(std::string("version10"), *rosterStorage_->getRoster()->getVersion());
+ CPPUNIT_ASSERT(rosterStorage_->getRoster()->getItem(jid1_));
+ CPPUNIT_ASSERT(rosterStorage_->getRoster()->getItem(jid2_));
+ }
+
+ void testGet_ResponseWithNewVersion() {
+ std::auto_ptr<XMPPRosterController> testling(createController());
+ testling->setUseVersioning(true);
+ boost::shared_ptr<RosterPayload> storedRoster(new RosterPayload());
+ storedRoster->setVersion("version10");
+ storedRoster->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Both));
+ rosterStorage_->setRoster(storedRoster);
+ testling->requestRoster();
+
+ boost::shared_ptr<RosterPayload> serverRoster(new RosterPayload());
+ serverRoster->setVersion("version12");
+ serverRoster->addItem(RosterItemPayload(jid2_, "Alice", RosterItemPayload::Both));
+ std::vector<std::string> groups;
+ groups.push_back("foo");
+ groups.push_back("bar");
+ serverRoster->addItem(RosterItemPayload(jid3_, "Rabbit", RosterItemPayload::Both, groups));
+ channel_->onIQReceived(IQ::createResult("foo@bar.com", channel_->sentStanzas[0]->getID(), serverRoster));
+
+
+ CPPUNIT_ASSERT_EQUAL(2, handler_->getEventCount());
+ CPPUNIT_ASSERT(!xmppRoster_->getItem(jid1_));
+ CPPUNIT_ASSERT(xmppRoster_->getItem(jid2_));
+ CPPUNIT_ASSERT(xmppRoster_->getItem(jid3_));
+ CPPUNIT_ASSERT_EQUAL(jid3_, handler_->getLastJID());
+ CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent());
+ CPPUNIT_ASSERT(rosterStorage_->getRoster());
+ CPPUNIT_ASSERT(rosterStorage_->getRoster()->getVersion());
+ CPPUNIT_ASSERT_EQUAL(std::string("version12"), *rosterStorage_->getRoster()->getVersion());
+ CPPUNIT_ASSERT(!rosterStorage_->getRoster()->getItem(jid1_));
+ CPPUNIT_ASSERT(rosterStorage_->getRoster()->getItem(jid2_));
+ CPPUNIT_ASSERT(rosterStorage_->getRoster()->getItem(jid3_));
+ CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(rosterStorage_->getRoster()->getItem(jid3_)->getGroups().size()));
+ }
+
void testModify() {
- XMPPRosterController controller(router_, xmppRoster_);
+ XMPPRosterController controller(router_, xmppRoster_, rosterStorage_);
boost::shared_ptr<RosterPayload> payload1(new RosterPayload());
payload1->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Both));
channel_->onIQReceived(IQ::createRequest(IQ::Set, JID(), "id1", payload1));
@@ -87,9 +219,9 @@ class XMPPRosterControllerTest : public CppUnit::TestFixture {
CPPUNIT_ASSERT_EQUAL(std::string("Bob2"), xmppRoster_->getNameForJID(jid1_));
}
-
+
void testRemove() {
- XMPPRosterController controller(router_, xmppRoster_);
+ XMPPRosterController controller(router_, xmppRoster_, rosterStorage_);
boost::shared_ptr<RosterPayload> payload1(new RosterPayload());
payload1->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Both));
channel_->onIQReceived(IQ::createRequest(IQ::Set, JID(), "id1", payload1));
@@ -107,8 +239,31 @@ class XMPPRosterControllerTest : public CppUnit::TestFixture {
}
+ void testRemove_RosterStorageUpdated() {
+ std::auto_ptr<XMPPRosterController> testling(createController());
+ testling->setUseVersioning(true);
+ boost::shared_ptr<RosterPayload> storedRoster(new RosterPayload());
+ storedRoster->setVersion("version10");
+ storedRoster->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Both));
+ storedRoster->addItem(RosterItemPayload(jid2_, "Alice", RosterItemPayload::Both));
+ rosterStorage_->setRoster(storedRoster);
+ testling->requestRoster();
+ channel_->onIQReceived(IQ::createResult("foo@bar.com", channel_->sentStanzas[0]->getID(), boost::shared_ptr<RosterPayload>()));
+
+ boost::shared_ptr<RosterPayload> payload2(new RosterPayload());
+ payload2->setVersion("version15");
+ payload2->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Remove));
+ channel_->onIQReceived(IQ::createRequest(IQ::Set, JID(), "id2", payload2));
+
+ CPPUNIT_ASSERT(rosterStorage_->getRoster());
+ CPPUNIT_ASSERT(rosterStorage_->getRoster()->getVersion());
+ CPPUNIT_ASSERT_EQUAL(std::string("version15"), *rosterStorage_->getRoster()->getVersion());
+ CPPUNIT_ASSERT(!rosterStorage_->getRoster()->getItem(jid1_));
+ CPPUNIT_ASSERT(rosterStorage_->getRoster()->getItem(jid2_));
+ }
+
void testMany() {
- XMPPRosterController controller(router_, xmppRoster_);
+ XMPPRosterController controller(router_, xmppRoster_, rosterStorage_);
boost::shared_ptr<RosterPayload> payload1(new RosterPayload());
payload1->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Both));
channel_->onIQReceived(IQ::createRequest(IQ::Set, JID(), "id1", payload1));
@@ -171,12 +326,18 @@ class XMPPRosterControllerTest : public CppUnit::TestFixture {
handler_->reset();
}
+
+ private:
+ XMPPRosterController* createController() {
+ return new XMPPRosterController(router_, xmppRoster_, rosterStorage_);
+ }
private:
DummyStanzaChannel* channel_;
IQRouter* router_;
XMPPRosterImpl* xmppRoster_;
XMPPRosterSignalHandler* handler_;
+ RosterMemoryStorage* rosterStorage_;
JID jid1_;
JID jid2_;
JID jid3_;
diff --git a/Swiften/Roster/UnitTest/XMPPRosterSignalHandler.cpp b/Swiften/Roster/UnitTest/XMPPRosterSignalHandler.cpp
index 983bc22..d89644a 100644
--- a/Swiften/Roster/UnitTest/XMPPRosterSignalHandler.cpp
+++ b/Swiften/Roster/UnitTest/XMPPRosterSignalHandler.cpp
@@ -11,7 +11,7 @@
using namespace Swift;
-XMPPRosterSignalHandler::XMPPRosterSignalHandler(Swift::XMPPRoster* roster) {
+XMPPRosterSignalHandler::XMPPRosterSignalHandler(Swift::XMPPRoster* roster) : eventCount(0) {
lastEvent_ = None;
roster->onJIDAdded.connect(boost::bind(&XMPPRosterSignalHandler::handleJIDAdded, this, _1));
roster->onJIDRemoved.connect(boost::bind(&XMPPRosterSignalHandler::handleJIDRemoved, this, _1));
@@ -24,4 +24,5 @@ void XMPPRosterSignalHandler::handleJIDUpdated(const Swift::JID& jid, const std:
lastOldName_ = oldName;
lastOldGroups_ = oldGroups;
lastEvent_ = Update;
+ eventCount++;
}
diff --git a/Swiften/Roster/UnitTest/XMPPRosterSignalHandler.h b/Swiften/Roster/UnitTest/XMPPRosterSignalHandler.h
index c59b4c6..2cf1159 100644
--- a/Swiften/Roster/UnitTest/XMPPRosterSignalHandler.h
+++ b/Swiften/Roster/UnitTest/XMPPRosterSignalHandler.h
@@ -37,15 +37,21 @@ public:
lastEvent_ = None;
}
+ int getEventCount() const {
+ return eventCount;
+ }
+
private:
void handleJIDAdded(const Swift::JID& jid) {
lastJID_ = jid;
lastEvent_ = Add;
+ eventCount++;
}
void handleJIDRemoved(const Swift::JID& jid) {
lastJID_ = jid;
lastEvent_ = Remove;
+ eventCount++;
}
void handleJIDUpdated(const Swift::JID& jid, const std::string& oldName, const std::vector<std::string>& oldGroups);
@@ -54,5 +60,5 @@ private:
Swift::JID lastJID_;
std::string lastOldName_;
std::vector<std::string> lastOldGroups_;
-
+ int eventCount;
};
diff --git a/Swiften/Roster/XMPPRosterController.cpp b/Swiften/Roster/XMPPRosterController.cpp
index a294d35..baa74ff 100644
--- a/Swiften/Roster/XMPPRosterController.cpp
+++ b/Swiften/Roster/XMPPRosterController.cpp
@@ -13,14 +13,15 @@
#include "Swiften/Queries/IQRouter.h"
#include "Swiften/Roster/GetRosterRequest.h"
#include "Swiften/Roster/XMPPRosterImpl.h"
+#include <Swiften/Roster/RosterStorage.h>
namespace Swift {
/**
* The controller does not gain ownership of these parameters.
*/
-XMPPRosterController::XMPPRosterController(IQRouter* iqRouter, XMPPRosterImpl* xmppRoster) : iqRouter_(iqRouter), rosterPushResponder_(iqRouter), xmppRoster_(xmppRoster) {
- rosterPushResponder_.onRosterReceived.connect(boost::bind(&XMPPRosterController::handleRosterReceived, this, _1, false));
+XMPPRosterController::XMPPRosterController(IQRouter* iqRouter, XMPPRosterImpl* xmppRoster, RosterStorage* rosterStorage) : iqRouter_(iqRouter), rosterPushResponder_(iqRouter), xmppRoster_(xmppRoster), rosterStorage_(rosterStorage), useVersioning(false) {
+ rosterPushResponder_.onRosterReceived.connect(boost::bind(&XMPPRosterController::handleRosterReceived, this, _1, false, boost::shared_ptr<RosterPayload>()));
rosterPushResponder_.start();
}
@@ -30,12 +31,24 @@ XMPPRosterController::~XMPPRosterController() {
void XMPPRosterController::requestRoster() {
xmppRoster_->clear();
- GetRosterRequest::ref rosterRequest = GetRosterRequest::create(iqRouter_);
- rosterRequest->onResponse.connect(boost::bind(&XMPPRosterController::handleRosterReceived, this, _1, true));
+
+ boost::shared_ptr<RosterPayload> storedRoster = rosterStorage_->getRoster();
+ GetRosterRequest::ref rosterRequest;
+ if (useVersioning) {
+ std::string version = "";
+ if (storedRoster && storedRoster->getVersion()) {
+ version = *storedRoster->getVersion();
+ }
+ rosterRequest = GetRosterRequest::create(iqRouter_, version);
+ }
+ else {
+ rosterRequest = GetRosterRequest::create(iqRouter_);
+ }
+ rosterRequest->onResponse.connect(boost::bind(&XMPPRosterController::handleRosterReceived, this, _1, true, storedRoster));
rosterRequest->send();
}
-void XMPPRosterController::handleRosterReceived(boost::shared_ptr<RosterPayload> rosterPayload, bool initial) {
+void XMPPRosterController::handleRosterReceived(boost::shared_ptr<RosterPayload> rosterPayload, bool initial, boost::shared_ptr<RosterPayload> previousRoster) {
if (rosterPayload) {
foreach(const RosterItemPayload& item, rosterPayload->getItems()) {
//Don't worry about the updated case, the XMPPRoster sorts that out.
@@ -46,9 +59,33 @@ void XMPPRosterController::handleRosterReceived(boost::shared_ptr<RosterPayload>
}
}
}
+ else if (previousRoster) {
+ // The cached version hasn't changed; emit all items
+ foreach(const RosterItemPayload& item, previousRoster->getItems()) {
+ if (item.getSubscription() != RosterItemPayload::Remove) {
+ xmppRoster_->addContact(item.getJID(), item.getName(), item.getGroups(), item.getSubscription());
+ }
+ else {
+ std::cerr << "ERROR: Stored invalid roster item" << std::endl;
+ }
+ }
+ }
if (initial) {
xmppRoster_->onInitialRosterPopulated();
}
+ if (rosterPayload && rosterPayload->getVersion() && useVersioning) {
+ saveRoster(*rosterPayload->getVersion());
+ }
+}
+
+void XMPPRosterController::saveRoster(const std::string& version) {
+ std::vector<XMPPRosterItem> items = xmppRoster_->getItems();
+ boost::shared_ptr<RosterPayload> roster(new RosterPayload());
+ roster->setVersion(version);
+ foreach(const XMPPRosterItem& item, items) {
+ roster->addItem(RosterItemPayload(item.getJID(), item.getName(), item.getSubscription(), item.getGroups()));
+ }
+ rosterStorage_->setRoster(roster);
}
}
diff --git a/Swiften/Roster/XMPPRosterController.h b/Swiften/Roster/XMPPRosterController.h
index eeb84f6..9313bb6 100644
--- a/Swiften/Roster/XMPPRosterController.h
+++ b/Swiften/Roster/XMPPRosterController.h
@@ -18,21 +18,29 @@
namespace Swift {
class IQRouter;
class XMPPRosterImpl;
+ class RosterStorage;
class XMPPRosterController {
public:
- XMPPRosterController(IQRouter *iqRouter, XMPPRosterImpl* xmppRoster);
+ XMPPRosterController(IQRouter *iqRouter, XMPPRosterImpl* xmppRoster, RosterStorage* storage);
~XMPPRosterController();
void requestRoster();
+ void setUseVersioning(bool b) {
+ useVersioning = b;
+ }
+
private:
- void handleRosterReceived(boost::shared_ptr<RosterPayload> rosterPayload, bool initial);
+ void handleRosterReceived(boost::shared_ptr<RosterPayload> rosterPayload, bool initial, boost::shared_ptr<RosterPayload> previousRoster);
+ void saveRoster(const std::string& version);
private:
IQRouter* iqRouter_;
RosterPushResponder rosterPushResponder_;
XMPPRosterImpl* xmppRoster_;
+ RosterStorage* rosterStorage_;
+ bool useVersioning;
};
}
diff --git a/Swiften/SConscript b/Swiften/SConscript
index 166e2ef..7f27ea1 100644
--- a/Swiften/SConscript
+++ b/Swiften/SConscript
@@ -62,6 +62,7 @@ if env["SCONS_STAGE"] == "build" :
"Client/NickResolver.cpp",
"Client/NickManager.cpp",
"Client/NickManagerImpl.cpp",
+ "Client/Storages.cpp",
"Compress/ZLibCodecompressor.cpp",
"Compress/ZLibDecompressor.cpp",
"Compress/ZLibCompressor.cpp",
@@ -88,6 +89,9 @@ if env["SCONS_STAGE"] == "build" :
"Queries/Requests/GetInBandRegistrationFormRequest.cpp",
"Queries/Requests/SubmitInBandRegistrationFormRequest.cpp",
"Queries/Responders/SoftwareVersionResponder.cpp",
+ "Roster/RosterStorage.cpp",
+ "Roster/RosterMemoryStorage.cpp",
+ "Roster/RosterFileStorage.cpp",
"Roster/XMPPRoster.cpp",
"Roster/XMPPRosterImpl.cpp",
"Roster/XMPPRosterController.cpp",
diff --git a/Swiften/Serializer/PayloadSerializers/RosterSerializer.cpp b/Swiften/Serializer/PayloadSerializers/RosterSerializer.cpp
index 40faf73..886676a 100644
--- a/Swiften/Serializer/PayloadSerializers/RosterSerializer.cpp
+++ b/Swiften/Serializer/PayloadSerializers/RosterSerializer.cpp
@@ -20,6 +20,9 @@ RosterSerializer::RosterSerializer() : GenericPayloadSerializer<RosterPayload>()
std::string RosterSerializer::serializePayload(boost::shared_ptr<RosterPayload> roster) const {
XMLElement queryElement("query", "jabber:iq:roster");
+ if (roster->getVersion()) {
+ queryElement.setAttribute("ver", *roster->getVersion());
+ }
foreach(const RosterItemPayload& item, roster->getItems()) {
boost::shared_ptr<XMLElement> itemElement(new XMLElement("item"));
itemElement->setAttribute("jid", item.getJID());
diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/RosterSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/RosterSerializerTest.cpp
index b8ceac3..61316df 100644
--- a/Swiften/Serializer/PayloadSerializers/UnitTest/RosterSerializerTest.cpp
+++ b/Swiften/Serializer/PayloadSerializers/UnitTest/RosterSerializerTest.cpp
@@ -11,16 +11,15 @@
using namespace Swift;
-class RosterSerializerTest : public CppUnit::TestFixture
-{
+class RosterSerializerTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(RosterSerializerTest);
CPPUNIT_TEST(testSerialize);
CPPUNIT_TEST(testSerialize_ItemWithUnknownContent);
+ CPPUNIT_TEST(testSerialize_WithVersion);
+ CPPUNIT_TEST(testSerialize_WithEmptyVersion);
CPPUNIT_TEST_SUITE_END();
public:
- RosterSerializerTest() {}
-
void testSerialize() {
RosterSerializer testling;
boost::shared_ptr<RosterPayload> roster(new RosterPayload());
@@ -77,6 +76,26 @@ class RosterSerializerTest : public CppUnit::TestFixture
CPPUNIT_ASSERT_EQUAL(expectedResult, testling.serialize(roster));
}
+
+ void testSerialize_WithVersion() {
+ RosterSerializer testling;
+ boost::shared_ptr<RosterPayload> roster(new RosterPayload());
+ roster->setVersion("ver20");
+
+ std::string expectedResult = "<query ver=\"ver20\" xmlns=\"jabber:iq:roster\"/>";
+
+ CPPUNIT_ASSERT_EQUAL(expectedResult, testling.serialize(roster));
+ }
+
+ void testSerialize_WithEmptyVersion() {
+ RosterSerializer testling;
+ boost::shared_ptr<RosterPayload> roster(new RosterPayload());
+ roster->setVersion("");
+
+ std::string expectedResult = "<query ver=\"\" xmlns=\"jabber:iq:roster\"/>";
+
+ CPPUNIT_ASSERT_EQUAL(expectedResult, testling.serialize(roster));
+ }
};
CPPUNIT_TEST_SUITE_REGISTRATION(RosterSerializerTest);
diff --git a/Swiften/Serializer/StreamFeaturesSerializer.cpp b/Swiften/Serializer/StreamFeaturesSerializer.cpp
index 915433c..11744b4 100644
--- a/Swiften/Serializer/StreamFeaturesSerializer.cpp
+++ b/Swiften/Serializer/StreamFeaturesSerializer.cpp
@@ -6,6 +6,8 @@
#include "Swiften/Serializer/StreamFeaturesSerializer.h"
+#include <boost/smart_ptr/make_shared.hpp>
+
#include "Swiften/Serializer/XML/XMLElement.h"
#include "Swiften/Serializer/XML/XMLTextNode.h"
#include "Swiften/Base/foreach.h"
@@ -49,6 +51,9 @@ std::string StreamFeaturesSerializer::serialize(boost::shared_ptr<Element> eleme
if (streamFeatures->hasStreamManagement()) {
streamFeaturesElement.addNode(boost::shared_ptr<XMLElement>(new XMLElement("sm", "urn:xmpp:sm:2")));
}
+ if (streamFeatures->hasRosterVersioning()) {
+ streamFeaturesElement.addNode(boost::make_shared<XMLElement>("ver", "urn:xmpp:features:rosterver"));
+ }
return streamFeaturesElement.serialize();
}
diff --git a/Swiften/Serializer/UnitTest/StreamFeaturesSerializerTest.cpp b/Swiften/Serializer/UnitTest/StreamFeaturesSerializerTest.cpp
index 65caa81..aa896c2 100644
--- a/Swiften/Serializer/UnitTest/StreamFeaturesSerializerTest.cpp
+++ b/Swiften/Serializer/UnitTest/StreamFeaturesSerializerTest.cpp
@@ -32,6 +32,7 @@ class StreamFeaturesSerializerTest : public CppUnit::TestFixture
streamFeatures->setHasResourceBind();
streamFeatures->setHasSession();
streamFeatures->setHasStreamManagement();
+ streamFeatures->setHasRosterVersioning();
CPPUNIT_ASSERT_EQUAL(std::string(
"<stream:features>"
@@ -47,6 +48,7 @@ class StreamFeaturesSerializerTest : public CppUnit::TestFixture
"<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"/>"
"<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/>"
"<sm xmlns=\"urn:xmpp:sm:2\"/>"
+ "<ver xmlns=\"urn:xmpp:features:rosterver\"/>"
"</stream:features>"), testling.serialize(streamFeatures));
}
};