diff options
| author | Remko Tronçon <git@el-tramo.be> | 2010-08-22 08:55:08 (GMT) | 
|---|---|---|
| committer | Remko Tronçon <git@el-tramo.be> | 2010-08-24 17:10:23 (GMT) | 
| commit | b6a34463dfcedd454ab42230a47cdb7294a76f21 (patch) | |
| tree | 832b0243325451932c0d15e9e33d53948fce0110 | |
| parent | 6e4b357141a6d09632f1e96d0eaf54f79daf52c9 (diff) | |
| download | swift-b6a34463dfcedd454ab42230a47cdb7294a76f21.zip swift-b6a34463dfcedd454ab42230a47cdb7294a76f21.tar.bz2  | |
Implemented VCardManager.
31 files changed, 525 insertions, 214 deletions
@@ -307,22 +307,23 @@  					<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>  					<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>  					<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/> +					<extension id="be.el_tramo.ecppunit.CPPUnitErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>  				</extensions>  			</storageModule>  			<storageModule moduleId="cdtBuildSystem" version="4.0.0"> -				<configuration artifactName="${ProjName}" buildProperties="" description="" id="0.980756260.1834106966" name="Unit tests" parent="org.eclipse.cdt.build.core.prefbase.cfg"> +				<configuration artifactName="${ProjName}" buildProperties="" description="" errorParsers="org.eclipse.cdt.core.VCErrorParser;org.eclipse.cdt.core.GmakeErrorParser;org.eclipse.cdt.core.CWDLocator;org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GASErrorParser;org.eclipse.cdt.core.GLDErrorParser;be.el_tramo.ecppunit.CPPUnitErrorParser" id="0.980756260.1834106966" name="Unit tests" parent="org.eclipse.cdt.build.core.prefbase.cfg">  					<folderInfo id="0.980756260.1834106966." name="/" resourcePath=""> -						<toolChain id="org.eclipse.cdt.build.core.prefbase.toolchain.1171413969" name="No ToolChain" resourceTypeBasedDiscovery="false" superClass="org.eclipse.cdt.build.core.prefbase.toolchain"> +						<toolChain errorParsers="" id="org.eclipse.cdt.build.core.prefbase.toolchain.1171413969" name="No ToolChain" resourceTypeBasedDiscovery="false" superClass="org.eclipse.cdt.build.core.prefbase.toolchain">  							<targetPlatform binaryParser="org.eclipse.cdt.core.MachO64;org.eclipse.cdt.core.ELF;org.eclipse.cdt.core.PE;org.eclipse.cdt.core.GNU_ELF" id="org.eclipse.cdt.build.core.prefbase.toolchain.1171413969.1006500186" name=""/> -							<builder arguments="${ProjDirPath}/3rdParty/SCons/scons.py" autoBuildTarget="check=1" buildPath="" cleanBuildTarget="-c" command="python" enableAutoBuild="true" id="org.eclipse.cdt.build.core.settings.default.builder.1417638948" incrementalBuildTarget="check=1" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" superClass="org.eclipse.cdt.build.core.settings.default.builder"/> -							<tool id="org.eclipse.cdt.build.core.settings.holder.libs.358970395" name="holder for library settings" superClass="org.eclipse.cdt.build.core.settings.holder.libs"/> -							<tool id="org.eclipse.cdt.build.core.settings.holder.655623884" name="Assembly" superClass="org.eclipse.cdt.build.core.settings.holder"> +							<builder arguments="${ProjDirPath}/3rdParty/SCons/scons.py" autoBuildTarget="check=1 QA" buildPath="" cleanBuildTarget="-c" command="python" enableAutoBuild="true" errorParsers="org.eclipse.cdt.core.GmakeErrorParser;org.eclipse.cdt.core.CWDLocator" id="org.eclipse.cdt.build.core.settings.default.builder.1417638948" incrementalBuildTarget="check=1 QA" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" superClass="org.eclipse.cdt.build.core.settings.default.builder"/> +							<tool errorParsers="org.eclipse.cdt.core.VCErrorParser;org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GASErrorParser;org.eclipse.cdt.core.GLDErrorParser" id="org.eclipse.cdt.build.core.settings.holder.libs.358970395" name="holder for library settings" superClass="org.eclipse.cdt.build.core.settings.holder.libs"/> +							<tool errorParsers="org.eclipse.cdt.core.VCErrorParser;org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GASErrorParser;org.eclipse.cdt.core.GLDErrorParser" id="org.eclipse.cdt.build.core.settings.holder.655623884" name="Assembly" superClass="org.eclipse.cdt.build.core.settings.holder">  								<inputType id="org.eclipse.cdt.build.core.settings.holder.inType.1994670288" languageId="org.eclipse.cdt.core.assembly" languageName="Assembly" sourceContentType="org.eclipse.cdt.core.asmSource" superClass="org.eclipse.cdt.build.core.settings.holder.inType"/>  							</tool> -							<tool id="org.eclipse.cdt.build.core.settings.holder.1108727159" name="GNU C++" superClass="org.eclipse.cdt.build.core.settings.holder"> +							<tool errorParsers="org.eclipse.cdt.core.VCErrorParser;org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GASErrorParser;org.eclipse.cdt.core.GLDErrorParser" id="org.eclipse.cdt.build.core.settings.holder.1108727159" name="GNU C++" superClass="org.eclipse.cdt.build.core.settings.holder">  								<inputType id="org.eclipse.cdt.build.core.settings.holder.inType.463954066" languageId="org.eclipse.cdt.core.g++" languageName="GNU C++" sourceContentType="org.eclipse.cdt.core.cxxSource,org.eclipse.cdt.core.cxxHeader" superClass="org.eclipse.cdt.build.core.settings.holder.inType"/>  							</tool> -							<tool id="org.eclipse.cdt.build.core.settings.holder.569111652" name="GNU C" superClass="org.eclipse.cdt.build.core.settings.holder"> +							<tool errorParsers="org.eclipse.cdt.core.VCErrorParser;org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GASErrorParser;org.eclipse.cdt.core.GLDErrorParser" id="org.eclipse.cdt.build.core.settings.holder.569111652" name="GNU C" superClass="org.eclipse.cdt.build.core.settings.holder">  								<inputType id="org.eclipse.cdt.build.core.settings.holder.inType.966696268" languageId="org.eclipse.cdt.core.gcc" languageName="GNU C" sourceContentType="org.eclipse.cdt.core.cSource,org.eclipse.cdt.core.cHeader" superClass="org.eclipse.cdt.build.core.settings.holder.inType"/>  							</tool>  						</toolChain> @@ -18,7 +18,7 @@  				</dictionary>  				<dictionary>  					<key>org.eclipse.cdt.make.core.autoBuildTarget</key> -					<value>dist=1</value> +					<value>check=1 QA</value>  				</dictionary>  				<dictionary>  					<key>org.eclipse.cdt.make.core.buildArguments</key> @@ -54,7 +54,7 @@  				</dictionary>  				<dictionary>  					<key>org.eclipse.cdt.make.core.fullBuildTarget</key> -					<value>dist=1</value> +					<value>check=1 QA</value>  				</dictionary>  				<dictionary>  					<key>org.eclipse.cdt.make.core.stopOnError</key> diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp index 2204366..d5686bd 100644 --- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp +++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp @@ -18,7 +18,10 @@  #include "Swift/Controllers/EventController.h"  #include "Swift/Controllers/Chat/MUCController.h"  #include "Swiften/Presence/PresenceSender.h" -#include "Swiften/Avatars/UnitTest/MockAvatarManager.h" +#include "Swiften/Avatars/AvatarManager.h" +#include "Swiften/Avatars/AvatarMemoryStorage.h" +#include "Swiften/VCards/VCardManager.h" +#include "Swiften/VCards/VCardMemoryStorage.h"  #include "Swift/Controllers/NickResolver.h"  #include "Swiften/Roster/XMPPRoster.h"  #include "Swift/Controllers/UnitTest/MockChatWindow.h" @@ -65,12 +68,19 @@ public:  		chatListWindowFactory_ = mocks_->InterfaceMock<ChatListWindowFactory>();  		mocks_->ExpectCall(chatListWindowFactory_, ChatListWindowFactory::createWindow).With(uiEventStream_).Return(NULL);  		manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, nickResolver_, presenceOracle_, serverDiscoInfo_, presenceSender_, uiEventStream_, chatListWindowFactory_, true, NULL); -		avatarManager_ = new MockAvatarManager(); + +		vcardStorage_ = new VCardMemoryStorage(); +		vcardManager_ = new VCardManager(jid_, iqRouter_, vcardStorage_); +		avatarStorage_ = new AvatarMemoryStorage(); +		avatarManager_ = new AvatarManager(vcardManager_, stanzaChannel_, avatarStorage_, NULL);  		manager_->setAvatarManager(avatarManager_);  	};  	void tearDown() {  		delete avatarManager_; +		delete avatarStorage_; +		delete vcardManager_; +		delete vcardStorage_;  		delete manager_;  		delete presenceSender_;  		delete presenceOracle_; @@ -303,6 +313,9 @@ private:  	ChatWindowFactory* chatWindowFactory_;  	NickResolver* nickResolver_;  	PresenceOracle* presenceOracle_; +	VCardStorage* vcardStorage_; +	VCardManager* vcardManager_; +	AvatarStorage* avatarStorage_;  	AvatarManager* avatarManager_;  	boost::shared_ptr<DiscoInfo> serverDiscoInfo_;  	boost::shared_ptr<XMPPRoster> xmppRoster_; diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp index 6eb3a2c..1032de1 100644 --- a/Swift/Controllers/MainController.cpp +++ b/Swift/Controllers/MainController.cpp @@ -20,6 +20,8 @@  #include "Swift/Controllers/BuildVersion.h"  #include "Swift/Controllers/Chat/ChatController.h"  #include "Swiften/VCards/VCardStorageFactory.h" +#include "Swiften/VCards/VCardManager.h" +#include "Swiften/VCards/VCardStorage.h"  #include "Swift/Controllers/Chat/MUCSearchController.h"  #include "Swift/Controllers/Chat/ChatsManager.h"  #include "Swift/Controllers/EventController.h" @@ -85,16 +87,16 @@ MainController::MainController(  			mainWindowFactory_(mainWindowFactory),  			loginWindowFactory_(loginWindowFactory),  			settings_(settings), -			loginWindow_(NULL),  			vcardStorageFactory_(vcardStorageFactory), -			useDelayForLatency_(useDelayForLatency)  { +			loginWindow_(NULL) , +			useDelayForLatency_(useDelayForLatency) {  	presenceOracle_ = NULL; -	avatarManager_ = NULL;  	chatsManager_ = NULL;  	eventController_ = NULL;  	eventWindowController_ = NULL;  	nickResolver_ = NULL;  	avatarManager_ = NULL; +	vcardManager_ = NULL;  	rosterController_ = NULL;  	xmppRosterController_ = NULL;  	clientVersionResponder_ = NULL; @@ -153,11 +155,13 @@ MainController::MainController(  MainController::~MainController() {  	delete systemTrayController_;  	delete soundEventController_; -	delete avatarStorage_;  	delete xmlConsoleController_;  	delete uiEventStream_;  	delete eventController_;  	resetClient(); +	for(VCardStorageMap::iterator i = vcardStorages_.begin(); i != vcardStorages_.end(); ++i) { +		delete i->second; +	}  }  void MainController::resetClient() { @@ -173,6 +177,8 @@ void MainController::resetClient() {  	nickResolver_ = NULL;  	delete avatarManager_;  	avatarManager_ = NULL; +	delete vcardManager_; +	vcardManager_ = NULL;  	delete eventWindowController_;  	eventWindowController_ = NULL;  	delete rosterController_; @@ -223,7 +229,9 @@ void MainController::handleConnected() {  		presenceOracle_ = new PresenceOracle(client_);  		nickResolver_ = new NickResolver(xmppRoster_);		 -		avatarManager_ = new AvatarManager(client_, client_, avatarStorage_); +		vcardManager_ = new VCardManager(jid_, client_, getVCardStorageForProfile(jid_)); +		vcardManager_->onOwnVCardChanged.connect(boost::bind(&MainController::handleOwnVCardReceived, this, _1)); +		avatarManager_ = new AvatarManager(vcardManager_, client_, avatarStorage_);  		rosterController_ = new RosterController(jid_, xmppRoster_, avatarManager_, mainWindowFactory_, nickResolver_, presenceOracle_, eventController_, uiEventStream_, client_);  		rosterController_->onChangeStatusRequest.connect(boost::bind(&MainController::handleChangeStatusRequest, this, _1, _2)); @@ -261,10 +269,7 @@ void MainController::handleConnected() {  	discoInfoRequest->onResponse.connect(boost::bind(&MainController::handleServerDiscoInfoResponse, this, _1, _2));  	discoInfoRequest->send(); -	boost::shared_ptr<GetVCardRequest> vCardRequest(new GetVCardRequest(JID(), client_)); -	vCardRequest->onResponse.connect(boost::bind(&MainController::handleOwnVCardReceived, this, _1, _2)); -	vCardRequest->send(); - +	vcardManager_->requestOwnVCard();  	setManagersEnabled(true);  	//Send presence last to catch all the incoming presences. @@ -480,17 +485,24 @@ void MainController::handleServerDiscoInfoResponse(boost::shared_ptr<DiscoInfo>  	}  } -void MainController::handleOwnVCardReceived(boost::shared_ptr<VCard> vCard, const boost::optional<ErrorPayload>& error) { -	if (!vCard) { +void MainController::handleOwnVCardReceived(VCard::ref vCard) { +	if (!vCard || vCard->getPhoto().isEmpty()) {  		return;  	} -	if (!error && !vCard->getPhoto().isEmpty()) { -		vCardPhotoHash_ = Hexify::hexify(SHA1::getHash(vCard->getPhoto())); -		if (client_ && client_->isAvailable()) { -			sendPresence(statusTracker_->getNextPresence()); -		} -		avatarManager_->setAvatar(jid_, vCard->getPhoto()); +	vCardPhotoHash_ = Hexify::hexify(SHA1::getHash(vCard->getPhoto())); +	if (client_ && client_->isAvailable()) { +		sendPresence(statusTracker_->getNextPresence());  	}  } +VCardStorage* MainController::getVCardStorageForProfile(const JID& jid) { +	String profile = jid.toBare().toString(); +	std::pair<VCardStorageMap::iterator, bool> r = vcardStorages_.insert(std::make_pair<String, VCardStorage*>(profile, NULL)); +	if (r.second) { +		r.first->second = vcardStorageFactory_->createVCardStorage(profile); +	} +	return r.first->second; +} + +  } diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h index a612175..cf04e59 100644 --- a/Swift/Controllers/MainController.h +++ b/Swift/Controllers/MainController.h @@ -29,6 +29,8 @@  namespace Swift {  	class AvatarStorage; +	class VCardStorage; +	class VCardManager;  	class Application;  	class Client;  	class ChatWindowFactory; @@ -94,7 +96,7 @@ namespace Swift {  			void handleError(const ClientError& error);  			void handleServerDiscoInfoResponse(boost::shared_ptr<DiscoInfo>, const boost::optional<ErrorPayload>&);  			void handleEventQueueLengthChange(int count); -			void handleOwnVCardReceived(boost::shared_ptr<VCard> vCard, const boost::optional<ErrorPayload>& error); +			void handleOwnVCardReceived(VCard::ref vCard);  			void sendPresence(boost::shared_ptr<Presence> presence);  			void handleInputIdleChanged(bool);  			void logout(); @@ -107,6 +109,9 @@ namespace Swift {  			void reconnectAfterError();  			void setManagersEnabled(bool enabled); +			VCardStorage* getVCardStorageForProfile(const JID& jid); + +		private:  			BoostIOServiceThread boostIOServiceThread_;  			BoostTimerFactory timerFactory_;  			PlatformIdleQuerier idleQuerier_; @@ -121,6 +126,7 @@ namespace Swift {  			ProfileSettingsProvider* profileSettings_;  			AvatarStorage* avatarStorage_;  			VCardStorageFactory* vcardStorageFactory_; +			VCardManager* vcardManager_;  			ApplicationMessageDisplay* applicationMessageDisplay_;  			ChatController* chatController_;  			XMPPRosterController* xmppRosterController_; @@ -153,5 +159,8 @@ namespace Swift {  			int timeBeforeNextReconnect_;  			Timer::ref reconnectTimer_;  			StatusTracker* statusTracker_; + +			typedef std::map<String, VCardStorage*> VCardStorageMap; +			VCardStorageMap vcardStorages_;  	};  } diff --git a/Swiften/Avatars/AvatarFileStorage.cpp b/Swiften/Avatars/AvatarFileStorage.cpp index a0ebd21..57bb1be 100644 --- a/Swiften/Avatars/AvatarFileStorage.cpp +++ b/Swiften/Avatars/AvatarFileStorage.cpp @@ -34,4 +34,11 @@ boost::filesystem::path AvatarFileStorage::getAvatarPath(const String& hash) con  	return path_ / hash.getUTF8String();  } +ByteArray AvatarFileStorage::getAvatar(const String& hash) const { +	ByteArray data; +	data.readFromFile(getAvatarPath(hash).string()); +	return data; +} + +  } diff --git a/Swiften/Avatars/AvatarFileStorage.h b/Swiften/Avatars/AvatarFileStorage.h index 6318c60..5ade779 100644 --- a/Swiften/Avatars/AvatarFileStorage.h +++ b/Swiften/Avatars/AvatarFileStorage.h @@ -20,6 +20,7 @@ namespace Swift {  			virtual bool hasAvatar(const String& hash) const;  			virtual void addAvatar(const String& hash, const ByteArray& avatar); +			virtual ByteArray getAvatar(const String& hash) const;  			virtual boost::filesystem::path getAvatarPath(const String& hash) const; diff --git a/Swiften/Avatars/AvatarManager.cpp b/Swiften/Avatars/AvatarManager.cpp index 3861520..33b1bee 100644 --- a/Swiften/Avatars/AvatarManager.cpp +++ b/Swiften/Avatars/AvatarManager.cpp @@ -15,18 +15,13 @@  #include "Swiften/StringCodecs/Hexify.h"  #include "Swiften/Avatars/AvatarStorage.h"  #include "Swiften/MUC/MUCRegistry.h" +#include "Swiften/VCards/VCardManager.h"  namespace Swift { -AvatarManager::AvatarManager(StanzaChannel* stanzaChannel, IQRouter* iqRouter, AvatarStorage* avatarStorage, MUCRegistry* mucRegistry) : stanzaChannel_(stanzaChannel), iqRouter_(iqRouter), avatarStorage_(avatarStorage), mucRegistry_(mucRegistry) { +AvatarManager::AvatarManager(VCardManager* vcardManager, StanzaChannel* stanzaChannel, AvatarStorage* avatarStorage, MUCRegistry* mucRegistry) : vcardManager_(vcardManager), stanzaChannel_(stanzaChannel), avatarStorage_(avatarStorage), mucRegistry_(mucRegistry) {  	stanzaChannel->onPresenceReceived.connect(boost::bind(&AvatarManager::handlePresenceReceived, this, _1)); -} - -AvatarManager::AvatarManager() { -	stanzaChannel_ = NULL; -	iqRouter_ = NULL; -	avatarStorage_ = NULL; -	mucRegistry_ = NULL; +	vcardManager_->onVCardChanged.connect(boost::bind(&AvatarManager::handleVCardChanged, this, _1, _2));  }  AvatarManager::~AvatarManager() { @@ -39,50 +34,30 @@ void AvatarManager::setMUCRegistry(MUCRegistry* mucRegistry) {  void AvatarManager::handlePresenceReceived(boost::shared_ptr<Presence> presence) {  	boost::shared_ptr<VCardUpdate> update = presence->getPayload<VCardUpdate>(); -	if (!update) { +	if (!update || presence->getPayload<ErrorPayload>()) {  		return;  	} -	if (presence->getPayload<ErrorPayload>()) { +	JID from = getAvatarJID(presence->getFrom()); +	if (getAvatarHash(from) == update->getPhotoHash()) {  		return;  	} -	JID from = getAvatarJID(presence->getFrom()); -	String& hash = avatarHashes_[from]; -	if (hash != update->getPhotoHash()) { -		String newHash = update->getPhotoHash(); -		if (avatarStorage_->hasAvatar(newHash)) { -			setAvatarHash(from, newHash); -		} -		else { -			boost::shared_ptr<GetVCardRequest> request(new GetVCardRequest(from, iqRouter_)); -			request->onResponse.connect(boost::bind(&AvatarManager::handleVCardReceived, this, from, newHash, _1, _2)); -			request->send(); -		} +	if (avatarStorage_->hasAvatar(update->getPhotoHash())) { +		setAvatarHash(from, update->getPhotoHash()); +	} +	else { +		vcardManager_->requestVCard(from);  	}  } -void AvatarManager::handleVCardReceived(const JID& from, const String& promisedHash, boost::shared_ptr<VCard> vCard, const boost::optional<ErrorPayload>& error) { -	if (error) { -		// FIXME: What to do here? -		std::cerr << "Warning: " << from << ": Could not get vCard" << std::endl; -		return; -	} +void AvatarManager::handleVCardChanged(const JID& from, VCard::ref vCard) {  	if (!vCard) {  		std::cerr << "Warning: " << from << ": null vcard payload" << std::endl; -		//FIXME: Why could this happen?  		return;  	} -	String realHash = Hexify::hexify(SHA1::getHash(vCard->getPhoto())); -	if (promisedHash != realHash) { -		std::cerr << "Warning: " << from << ": Got different vCard photo hash (" << promisedHash << " != " << realHash << ")" << std::endl; -	} -	avatarStorage_->addAvatar(realHash, vCard->getPhoto()); -	setAvatarHash(from, realHash); -} -void AvatarManager::setAvatar(const JID& jid, const ByteArray& avatar) { -	String hash = Hexify::hexify(SHA1::getHash(avatar)); -	avatarStorage_->addAvatar(hash, avatar); -	setAvatarHash(getAvatarJID(jid), hash); +	String hash = Hexify::hexify(SHA1::getHash(vCard->getPhoto())); +	avatarStorage_->addAvatar(hash, vCard->getPhoto()); +	setAvatarHash(from, hash);  }  void AvatarManager::setAvatarHash(const JID& from, const String& hash) { @@ -90,6 +65,14 @@ void AvatarManager::setAvatarHash(const JID& from, const String& hash) {  	onAvatarChanged(from, hash);  } +/* +void AvatarManager::setAvatar(const JID& jid, const ByteArray& avatar) { +	String hash = Hexify::hexify(SHA1::getHash(avatar)); +	avatarStorage_->addAvatar(hash, avatar); +	setAvatarHash(getAvatarJID(jid), hash); +} +*/ +  String AvatarManager::getAvatarHash(const JID& jid) const {  	std::map<JID, String>::const_iterator i = avatarHashes_.find(getAvatarJID(jid));  	if (i != avatarHashes_.end()) { diff --git a/Swiften/Avatars/AvatarManager.h b/Swiften/Avatars/AvatarManager.h index ad4b30f..543d167 100644 --- a/Swiften/Avatars/AvatarManager.h +++ b/Swiften/Avatars/AvatarManager.h @@ -9,9 +9,9 @@  #include <boost/filesystem.hpp>  #include <boost/shared_ptr.hpp>  #include <boost/optional.hpp> -#include "Swiften/Base/boost_bsignals.h"  #include <map> +#include "Swiften/Base/boost_bsignals.h"  #include "Swiften/JID/JID.h"  #include "Swiften/Elements/Presence.h"  #include "Swiften/Elements/VCard.h" @@ -21,35 +21,32 @@ namespace Swift {  	class MUCRegistry;  	class AvatarStorage;  	class StanzaChannel; -	class IQRouter; +	class VCardManager;  	class AvatarManager {  		public: -			AvatarManager(StanzaChannel*, IQRouter*, AvatarStorage*, MUCRegistry* = NULL); +			AvatarManager(VCardManager*, StanzaChannel*, AvatarStorage*, MUCRegistry* = NULL);  			virtual ~AvatarManager();  			virtual void setMUCRegistry(MUCRegistry*); -			virtual String getAvatarHash(const JID&) const;  			virtual boost::filesystem::path getAvatarPath(const JID&) const; -			virtual void setAvatar(const JID&, const ByteArray& avatar); -		public: -			boost::signal<void (const JID&, const String&)> onAvatarChanged; +//			virtual void setAvatar(const JID&, const ByteArray& avatar);*/ -		protected: -			/** Used only for testing. Leads to a non-functional object. */ -			AvatarManager(); +		public: +			boost::signal<void (const JID&, const String& /*hash*/)> onAvatarChanged;  		private:  			void handlePresenceReceived(boost::shared_ptr<Presence>); -			void handleVCardReceived(const JID& from, const String& hash, boost::shared_ptr<VCard>, const boost::optional<ErrorPayload>&); +			void handleVCardChanged(const JID& from, VCard::ref);  			void setAvatarHash(const JID& from, const String& hash);  			JID getAvatarJID(const JID& o) const; +			String getAvatarHash(const JID&) const;  		private: +			VCardManager* vcardManager_;  			StanzaChannel* stanzaChannel_; -			IQRouter* iqRouter_;  			AvatarStorage* avatarStorage_;  			MUCRegistry* mucRegistry_;  			std::map<JID, String> avatarHashes_; diff --git a/Swiften/Avatars/AvatarMemoryStorage.h b/Swiften/Avatars/AvatarMemoryStorage.h new file mode 100644 index 0000000..f60f603 --- /dev/null +++ b/Swiften/Avatars/AvatarMemoryStorage.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <map> + +#include "Swiften/Base/String.h" +#include "Swiften/Base/ByteArray.h" +#include "Swiften/Avatars/AvatarStorage.h" + +namespace Swift { +	class AvatarMemoryStorage : public AvatarStorage { +		public: +			virtual bool hasAvatar(const String& hash) const { return avatars.find(hash) != avatars.end(); } +			virtual void addAvatar(const String& hash, const ByteArray& avatar) { avatars[hash] = avatar; } +			virtual ByteArray getAvatar(const String& hash) const { +				std::map<String, ByteArray>::const_iterator i = avatars.find(hash); +				return i == avatars.end() ? ByteArray() : i->second; +			} + +			virtual boost::filesystem::path getAvatarPath(const String& hash) const { +				return boost::filesystem::path(); +			} + +		private: +			std::map<String, ByteArray> avatars; +	}; +} diff --git a/Swiften/Avatars/AvatarStorage.h b/Swiften/Avatars/AvatarStorage.h index bd8aa49..d699f40 100644 --- a/Swiften/Avatars/AvatarStorage.h +++ b/Swiften/Avatars/AvatarStorage.h @@ -18,6 +18,7 @@ namespace Swift {  			virtual bool hasAvatar(const String& hash) const = 0;  			virtual void addAvatar(const String& hash, const ByteArray& avatar) = 0; +			virtual ByteArray getAvatar(const String& hash) const = 0;  			virtual boost::filesystem::path getAvatarPath(const String& hash) const = 0;  	}; diff --git a/Swiften/Avatars/UnitTest/AvatarManagerTest.cpp b/Swiften/Avatars/UnitTest/AvatarManagerTest.cpp index f954aa1..858d257 100644 --- a/Swiften/Avatars/UnitTest/AvatarManagerTest.cpp +++ b/Swiften/Avatars/UnitTest/AvatarManagerTest.cpp @@ -6,24 +6,32 @@  #include <cppunit/extensions/HelperMacros.h>  #include <cppunit/extensions/TestFactoryRegistry.h> +#include <boost/bind.hpp>  #include "Swiften/Elements/VCardUpdate.h"  #include "Swiften/Avatars/AvatarManager.h" -#include "Swiften/Avatars/AvatarStorage.h" +#include "Swiften/Avatars/AvatarMemoryStorage.h" +#include "Swiften/VCards/VCardMemoryStorage.h" +#include "Swiften/VCards/VCardManager.h"  #include "Swiften/MUC/MUCRegistry.h"  #include "Swiften/Queries/IQRouter.h"  #include "Swiften/Client/DummyStanzaChannel.h" +#include "Swiften/StringCodecs/SHA1.h" +#include "Swiften/StringCodecs/Hexify.h"  using namespace Swift;  class AvatarManagerTest : public CppUnit::TestFixture {  		CPPUNIT_TEST_SUITE(AvatarManagerTest); -		CPPUNIT_TEST(testUpdate_UpdateNewHash); +		CPPUNIT_TEST(testUpdate_NewHashNewVCardRequestsVCard); +		CPPUNIT_TEST(testUpdate_NewHashStoresAvatarAndEmitsNotificationOnVCardReceive); +		CPPUNIT_TEST(testUpdate_KnownHash); +		CPPUNIT_TEST(testUpdate_KnownHashFromDifferentUserDoesNotRequestVCardButTriggersNotification);  		/*&  		CPPUNIT_TEST(testUpdate_UpdateNewHashAlreadyHaveAvatar);  		CPPUNIT_TEST(testUpdate_UpdateNewHashFromMUC);  		CPPUNIT_TEST(testUpdate_UpdateSameHash);*/ -		CPPUNIT_TEST(testUpdate_UpdateWithError); +		//CPPUNIT_TEST(testUpdate_UpdateWithError);  		/*  		CPPUNIT_TEST(testUpdate_UpdateNewHashSameThanOtherUser);  		CPPUNIT_TEST(testReceiveVCard); @@ -33,99 +41,135 @@ class AvatarManagerTest : public CppUnit::TestFixture {  	public:  		void setUp() { -			stanzaChannel_ = new DummyStanzaChannel(); -			iqRouter_ = new IQRouter(stanzaChannel_); -			mucRegistry_ = new DummyMUCRegistry(); -			avatarStorage_ = new DummyAvatarStorage(); +			ownJID = JID("foo@fum.com/bum"); +			stanzaChannel = new DummyStanzaChannel(); +			iqRouter = new IQRouter(stanzaChannel); +			mucRegistry = new DummyMUCRegistry(); +			avatarStorage = new AvatarMemoryStorage(); +			vcardStorage = new VCardMemoryStorage(); +			vcardManager = new VCardManager(ownJID, iqRouter, vcardStorage); +			avatar1 = ByteArray("abcdefg"); +			avatar1Hash = Hexify::hexify(SHA1::getHash(avatar1)); +			user1 = JID("user1@bar.com/bla"); +			user2 = JID("user2@foo.com/baz");  		}  		void tearDown() { -			delete avatarStorage_; -			delete mucRegistry_; -			delete iqRouter_; -			delete stanzaChannel_; +			delete vcardManager; +			delete vcardStorage; +			delete avatarStorage; +			delete mucRegistry; +			delete iqRouter; +			delete stanzaChannel;  		} -		void testUpdate_UpdateNewHash() { +		void testUpdate_NewHashNewVCardRequestsVCard() {  			std::auto_ptr<AvatarManager> testling = createManager(); -			stanzaChannel_->onPresenceReceived(createPresenceWithPhotoHash()); +			stanzaChannel->onPresenceReceived(createPresenceWithPhotoHash(user1, avatar1Hash)); -			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(stanzaChannel_->sentStanzas.size())); -			CPPUNIT_ASSERT(stanzaChannel_->isRequestAtIndex<VCard>(0, JID("foo@bar.com"), IQ::Get)); +			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(stanzaChannel->sentStanzas.size())); +			CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<VCard>(0, user1.toBare(), IQ::Get));  		} -		void testUpdate_UpdateNewHashAlreadyHaveAvatar() { -			avatarStorage_->addAvatar("aef56135bcce35eb24a43fcd684005b4ca286497", ByteArray("ghij")); -			std::auto_ptr<AvatarManager> testling = createManager(); -			stanzaChannel_->onPresenceReceived(createPresenceWithPhotoHash()); - -			CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(stanzaChannel_->sentStanzas.size())); -		} -/* -		void testUpdate_UpdateNewHashFromMUC() { +		void testUpdate_NewHashStoresAvatarAndEmitsNotificationOnVCardReceive() {  			std::auto_ptr<AvatarManager> testling = createManager(); +			stanzaChannel->onPresenceReceived(createPresenceWithPhotoHash(user1, avatar1Hash)); +			stanzaChannel->onIQReceived(createVCardResult(avatar1)); + +			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(changes.size())); +			CPPUNIT_ASSERT_EQUAL(user1.toBare(), changes[0].first); +			CPPUNIT_ASSERT_EQUAL(avatar1Hash, changes[0].second); +			CPPUNIT_ASSERT(avatarStorage->hasAvatar(avatar1Hash)); +			CPPUNIT_ASSERT_EQUAL(avatar1, avatarStorage->getAvatar(avatar1Hash));  		} -		void testUpdate_UpdateSameHash() { +		void testUpdate_KnownHash() {  			std::auto_ptr<AvatarManager> testling = createManager(); -		} +			stanzaChannel->onPresenceReceived(createPresenceWithPhotoHash(user1, avatar1Hash)); +			stanzaChannel->onIQReceived(createVCardResult(avatar1)); +			changes.clear(); +			stanzaChannel->sentStanzas.clear(); -		void testUpdate_UpdateNewHashSameThanOtherUser() { -			std::auto_ptr<AvatarManager> testling = createManager(); -		} +			stanzaChannel->onPresenceReceived(createPresenceWithPhotoHash(user1, avatar1Hash)); -		void testReceiveVCard() { -			std::auto_ptr<AvatarManager> testling = createManager(); +			CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(stanzaChannel->sentStanzas.size())); +			CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(changes.size()));  		} -		void testGetAvatarPath() { +		void testUpdate_KnownHashFromDifferentUserDoesNotRequestVCardButTriggersNotification() {  			std::auto_ptr<AvatarManager> testling = createManager(); +			stanzaChannel->onPresenceReceived(createPresenceWithPhotoHash(user1, avatar1Hash)); +			stanzaChannel->onIQReceived(createVCardResult(avatar1)); +			changes.clear(); +			stanzaChannel->sentStanzas.clear(); + +			stanzaChannel->onPresenceReceived(createPresenceWithPhotoHash(user2, avatar1Hash)); + +			CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(stanzaChannel->sentStanzas.size())); +			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(changes.size())); +			CPPUNIT_ASSERT_EQUAL(user2.toBare(), changes[0].first); +			CPPUNIT_ASSERT_EQUAL(avatar1Hash, changes[0].second);  		} -		void testGetAvatarPathFromMUC() { +/* +		void testUpdate_UpdateNewHashFromMUC() {  			std::auto_ptr<AvatarManager> testling = createManager();  		} +  		*/ -		void testUpdate_UpdateWithError() { +		/*void testUpdate_UpdateWithError() {  			std::auto_ptr<AvatarManager> testling = createManager();  			boost::shared_ptr<Presence> update = createPresenceWithPhotoHash();  			update->addPayload(boost::shared_ptr<ErrorPayload>(new ErrorPayload()));  			stanzaChannel_->onPresenceReceived(update);  			CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(stanzaChannel_->sentStanzas.size())); -		} +		}*/  	private:  		std::auto_ptr<AvatarManager> createManager() { -			return std::auto_ptr<AvatarManager>(new AvatarManager(stanzaChannel_, iqRouter_, avatarStorage_, mucRegistry_)); +			std::auto_ptr<AvatarManager> result(new AvatarManager(vcardManager, stanzaChannel, avatarStorage, mucRegistry)); +			result->onAvatarChanged.connect(boost::bind(&AvatarManagerTest::handleAvatarChanged, this, _1, _2)); +			return result;  		} -		boost::shared_ptr<Presence> createPresenceWithPhotoHash() { +		boost::shared_ptr<Presence> createPresenceWithPhotoHash(const JID& jid, const String& hash) {  			boost::shared_ptr<Presence> presence(new Presence()); -			presence->setFrom(JID("foo@bar.com/baz")); -			presence->addPayload(boost::shared_ptr<VCardUpdate>(new VCardUpdate("aef56135bcce35eb24a43fcd684005b4ca286497"))); +			presence->setFrom(jid); +			presence->addPayload(boost::shared_ptr<VCardUpdate>(new VCardUpdate(hash)));  			return presence;  		} +		IQ::ref createVCardResult(const ByteArray& avatar) { +			VCard::ref vcard(new VCard()); +			vcard->setPhoto(avatar); +			return IQ::createResult(JID("baz@fum.com"), stanzaChannel->sentStanzas[0]->getID(), vcard); +		} + +		void handleAvatarChanged(const JID& jid, const String& hash) { +			changes.push_back(std::pair<JID,String>(jid, hash)); +		} +  	private:  		struct DummyMUCRegistry : public MUCRegistry {  			bool isMUC(const JID& jid) const { return std::find(mucs_.begin(), mucs_.end(), jid) != mucs_.end(); }  			std::vector<JID> mucs_;  		}; -		struct DummyAvatarStorage : public AvatarStorage { -			virtual bool hasAvatar(const String& hash) const { return avatars.find(hash) != avatars.end(); } -			virtual void addAvatar(const String& hash, const ByteArray& avatar) { avatars[hash] = avatar; } -			virtual boost::filesystem::path getAvatarPath(const String& hash) const { -				return boost::filesystem::path("/avatars") / hash.getUTF8String(); -			} -			std::map<String, ByteArray> avatars; -		}; -		DummyStanzaChannel* stanzaChannel_; -		IQRouter* iqRouter_; -		DummyMUCRegistry* mucRegistry_; -		DummyAvatarStorage* avatarStorage_; + +		JID ownJID; +		DummyStanzaChannel* stanzaChannel; +		IQRouter* iqRouter; +		DummyMUCRegistry* mucRegistry; +		AvatarMemoryStorage* avatarStorage; +		VCardManager* vcardManager; +		VCardMemoryStorage* vcardStorage; +		ByteArray avatar1; +		String avatar1Hash; +		std::vector<std::pair<JID,String> > changes; +		JID user1; +		JID user2;  };  CPPUNIT_TEST_SUITE_REGISTRATION(AvatarManagerTest); diff --git a/Swiften/Avatars/UnitTest/MockAvatarManager.cpp b/Swiften/Avatars/UnitTest/MockAvatarManager.cpp deleted file mode 100644 index 4a96e01..0000000 --- a/Swiften/Avatars/UnitTest/MockAvatarManager.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. - */ - -#include "Swiften/Avatars/UnitTest/MockAvatarManager.h" - -namespace Swift { - -MockAvatarManager::MockAvatarManager() { - -} - -MockAvatarManager::~MockAvatarManager() { - -} - -String MockAvatarManager::getAvatarHash(const JID& jid) const { -	return jid.toBare(); -} - -boost::filesystem::path MockAvatarManager::getAvatarPath(const JID& jid) const { -	return jid.getResource().getUTF8String(); -} - -void MockAvatarManager::setAvatar(const JID&, const ByteArray&) { - -} - -} - diff --git a/Swiften/Avatars/UnitTest/MockAvatarManager.h b/Swiften/Avatars/UnitTest/MockAvatarManager.h deleted file mode 100644 index 9f31f12..0000000 --- a/Swiften/Avatars/UnitTest/MockAvatarManager.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. - */ - -#pragma once - -#include "Swiften/Avatars/AvatarManager.h" -#include "Swiften/Client/DummyStanzaChannel.h" - -namespace Swift { -	class MockAvatarManager : public AvatarManager { -		public: -			MockAvatarManager(); -			virtual ~MockAvatarManager(); -			virtual String getAvatarHash(const JID&) const; -			virtual boost::filesystem::path getAvatarPath(const JID&) const; -			virtual void setAvatar(const JID&, const ByteArray& avatar); -		private: -			DummyStanzaChannel channel_; -	}; -} diff --git a/Swiften/Elements/IQ.h b/Swiften/Elements/IQ.h index 790d032..2bb55e1 100644 --- a/Swiften/Elements/IQ.h +++ b/Swiften/Elements/IQ.h @@ -8,10 +8,11 @@  #include "Swiften/Elements/Stanza.h"  #include "Swiften/Elements/ErrorPayload.h" +#include "Swiften/Base/Shared.h"  namespace Swift   { -	class IQ : public Stanza +	class IQ : public Stanza, public Shared<IQ>  	{  		public:   			enum Type { Get, Set, Result, Error }; @@ -33,8 +34,8 @@ namespace Swift  			static boost::shared_ptr<IQ> createError(  					const JID& to,  					const String& id, -					ErrorPayload::Condition condition, -					ErrorPayload::Type type); +					ErrorPayload::Condition condition = ErrorPayload::BadRequest, +					ErrorPayload::Type type = ErrorPayload::Cancel);  		private:  			Type type_; diff --git a/Swiften/QA/StorageTest/SConscript b/Swiften/QA/StorageTest/SConscript index c7401e0..c35d5b3 100644 --- a/Swiften/QA/StorageTest/SConscript +++ b/Swiften/QA/StorageTest/SConscript @@ -10,6 +10,7 @@ if env["TEST"] :  	myenv.MergeFlags(myenv["BOOST_FLAGS"])  	myenv.MergeFlags(myenv["LIBIDN_FLAGS"])  	myenv.MergeFlags(myenv.get("EXPAT_FLAGS", {})) +	myenv.MergeFlags(myenv.get("LIBXML_FLAGS", {}))  	tester = myenv.Program("StorageTest", [  			"VCardFileStorageTest.cpp", diff --git a/Swiften/QA/StorageTest/VCardFileStorageTest.cpp b/Swiften/QA/StorageTest/VCardFileStorageTest.cpp index 9704409..694e13b 100644 --- a/Swiften/QA/StorageTest/VCardFileStorageTest.cpp +++ b/Swiften/QA/StorageTest/VCardFileStorageTest.cpp @@ -39,9 +39,9 @@ class VCardFileStorageTest : public CppUnit::TestFixture {  			vcard->setFullName("Alice In Wonderland");  			vcard->setEMail("alice@wonderland.lit"); -			testling->setVCard(JID("alice@wonderland.lit"), vcard); +			testling->setVCard(JID("alice@wonderland.lit/TeaRoom"), vcard); -			boost::filesystem::path vcardFile(vcardsPath / "alice@wonderland.lit.xml"); +			boost::filesystem::path vcardFile(vcardsPath / "alice@wonderland.lit%2fTeaRoom.xml");  			CPPUNIT_ASSERT(boost::filesystem::exists(vcardFile));  			ByteArray data;  			data.readFromFile(vcardFile.string()); diff --git a/Swiften/Queries/Requests/GetVCardRequest.h b/Swiften/Queries/Requests/GetVCardRequest.h index 7ebacdf..f369cdc 100644 --- a/Swiften/Queries/Requests/GetVCardRequest.h +++ b/Swiften/Queries/Requests/GetVCardRequest.h @@ -8,9 +8,10 @@  #include "Swiften/Queries/GenericRequest.h"  #include "Swiften/Elements/VCard.h" +#include "Swiften/Base/Shared.h"  namespace Swift { -	class GetVCardRequest : public GenericRequest<VCard> { +	class GetVCardRequest : public GenericRequest<VCard>, public Shared<GetVCardRequest> {  		public:  			GetVCardRequest(const JID& jid, IQRouter* router) : GenericRequest<VCard>(IQ::Get, jid, boost::shared_ptr<Payload>(new VCard()), router) {  			} diff --git a/Swiften/SConscript b/Swiften/SConscript index f206dd0..6e2628a 100644 --- a/Swiften/SConscript +++ b/Swiften/SConscript @@ -140,7 +140,6 @@ if env["SCONS_STAGE"] == "build" :  	env.Append(UNITTEST_SOURCES = [  			File("Application/UnitTest/ApplicationPathProviderTest.cpp"), -			File("Avatars/UnitTest/MockAvatarManager.cpp"),  			File("Avatars/UnitTest/AvatarManagerTest.cpp"),  			File("Base/UnitTest/IDGeneratorTest.cpp"),  			File("Base/UnitTest/StringTest.cpp"), @@ -233,4 +232,5 @@ if env["SCONS_STAGE"] == "build" :  			File("StringCodecs/UnitTest/HexifyTest.cpp"),  			File("StringCodecs/UnitTest/HMACSHA1Test.cpp"),  			File("StringCodecs/UnitTest/PBKDF2Test.cpp"), +			File("VCards/UnitTest/VCardManagerTest.cpp"),  		]) diff --git a/Swiften/Serializer/PayloadSerializers/VCardSerializer.cpp b/Swiften/Serializer/PayloadSerializers/VCardSerializer.cpp index 19a6b6e..8975818 100644 --- a/Swiften/Serializer/PayloadSerializers/VCardSerializer.cpp +++ b/Swiften/Serializer/PayloadSerializers/VCardSerializer.cpp @@ -10,6 +10,7 @@  #include "Swiften/Serializer/XML/XMLElement.h"  #include "Swiften/Serializer/XML/XMLTextNode.h" +#include "Swiften/StringCodecs/Hexify.h"  namespace Swift { @@ -49,7 +50,20 @@ String VCardSerializer::serializePayload(boost::shared_ptr<VCard> vcard)  const  		nickElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(vcard->getNickname())));  		queryElement.addNode(nickElement);  	} -	// TODO +	if (!vcard->getPhoto().isEmpty() || !vcard->getPhotoType().isEmpty()) { +		XMLElement::ref photoElement(new XMLElement("PHOTO")); +		if (!vcard->getPhotoType().isEmpty()) { +			XMLElement::ref typeElement(new XMLElement("TYPE")); +			typeElement->addNode(XMLTextNode::ref(new XMLTextNode(vcard->getPhotoType()))); +			photoElement->addNode(typeElement); +		} +		if (!vcard->getPhoto().isEmpty()) { +			XMLElement::ref binvalElement(new XMLElement("BINVAL")); +			binvalElement->addNode(XMLTextNode::ref(new XMLTextNode(Hexify::hexify(vcard->getPhoto())))); +			photoElement->addNode(binvalElement); +		} +		queryElement.addNode(photoElement); +	}  	return queryElement.serialize();  } diff --git a/Swiften/Serializer/XML/XMLElement.h b/Swiften/Serializer/XML/XMLElement.h index 373a939..8447ab1 100644 --- a/Swiften/Serializer/XML/XMLElement.h +++ b/Swiften/Serializer/XML/XMLElement.h @@ -4,18 +4,18 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#ifndef SWIFTEN_XMLElement_H -#define SWIFTEN_XMLElement_H +#pragma once  #include <boost/shared_ptr.hpp>  #include <vector>  #include <map>  #include "Swiften/Base/String.h" +#include "Swiften/Base/Shared.h"  #include "Swiften/Serializer/XML/XMLNode.h"  namespace Swift { -	class XMLElement : public XMLNode { +	class XMLElement : public XMLNode, public Shared<XMLElement> {  		public:  			XMLElement(const String& tag, const String& xmlns = ""); @@ -30,4 +30,3 @@ namespace Swift {  			std::vector< boost::shared_ptr<XMLNode> > childNodes_;  	};  } -#endif diff --git a/Swiften/Serializer/XML/XMLTextNode.h b/Swiften/Serializer/XML/XMLTextNode.h index c1d13ef..e158916 100644 --- a/Swiften/Serializer/XML/XMLTextNode.h +++ b/Swiften/Serializer/XML/XMLTextNode.h @@ -7,9 +7,10 @@  #pragma once  #include "Swiften/Serializer/XML/XMLNode.h" +#include "Swiften/Base/Shared.h"  namespace Swift { -	class XMLTextNode : public XMLNode { +	class XMLTextNode : public XMLNode, public Shared<XMLTextNode> {  		public:  			typedef boost::shared_ptr<XMLTextNode> ref; diff --git a/Swiften/VCards/SConscript b/Swiften/VCards/SConscript index 538eb4a..e83e633 100644 --- a/Swiften/VCards/SConscript +++ b/Swiften/VCards/SConscript @@ -1,6 +1,7 @@  Import("swiften_env")  objects = swiften_env.StaticObject([ +			"VCardManager.cpp",  			"VCardStorage.cpp",  			"VCardFileStorage.cpp",  			"VCardStorageFactory.cpp", diff --git a/Swiften/VCards/UnitTest/VCardManagerTest.cpp b/Swiften/VCards/UnitTest/VCardManagerTest.cpp new file mode 100644 index 0000000..927bcfe --- /dev/null +++ b/Swiften/VCards/UnitTest/VCardManagerTest.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> +#include <vector> +#include <boost/bind.hpp> + +#include "Swiften/VCards/VCardManager.h" +#include "Swiften/VCards/VCardMemoryStorage.h" +#include "Swiften/Queries/IQRouter.h" +#include "Swiften/Client/DummyStanzaChannel.h" + +using namespace Swift; + +class VCardManagerTest : public CppUnit::TestFixture { +		CPPUNIT_TEST_SUITE(VCardManagerTest); +		CPPUNIT_TEST(testGet_NewVCardRequestsVCard); +		CPPUNIT_TEST(testGet_ExistingVCard); +		CPPUNIT_TEST(testRequest_RequestsVCard); +		CPPUNIT_TEST(testRequest_ReceiveEmitsNotification); +		CPPUNIT_TEST(testRequest_Error); +		CPPUNIT_TEST(testRequest_VCardAlreadyRequested); +		CPPUNIT_TEST(testRequest_AfterPreviousRequest); +		CPPUNIT_TEST(testRequestOwnVCard); +		CPPUNIT_TEST_SUITE_END(); + +	public: +		void setUp() { +			ownJID = JID("baz@fum.com/dum"); +			stanzaChannel = new DummyStanzaChannel(); +			iqRouter = new IQRouter(stanzaChannel); +			vcardStorage = new VCardMemoryStorage(); +		} + +		void tearDown() { +			delete vcardStorage; +			delete iqRouter; +			delete stanzaChannel; +		} + +		void testGet_NewVCardRequestsVCard() { +			std::auto_ptr<VCardManager> testling = createManager(); +			VCard::ref result = testling->getVCardAndRequestWhenNeeded(JID("foo@bar.com/baz")); + +			CPPUNIT_ASSERT(!result); +			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(stanzaChannel->sentStanzas.size())); +			CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<VCard>(0, JID("foo@bar.com/baz"), IQ::Get)); +		} + +		void testGet_ExistingVCard() { +			std::auto_ptr<VCardManager> testling = createManager(); +			VCard::ref vcard(new VCard()); +			vcard->setFullName("Foo Bar"); +			vcardStorage->setVCard(JID("foo@bar.com/baz"), vcard); + +			VCard::ref result = testling->getVCardAndRequestWhenNeeded(JID("foo@bar.com/baz")); + +			CPPUNIT_ASSERT_EQUAL(String("Foo Bar"), result->getFullName()); +			CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(stanzaChannel->sentStanzas.size())); +		} + +		void testRequest_RequestsVCard() { +			std::auto_ptr<VCardManager> testling = createManager(); +			testling->requestVCard(JID("foo@bar.com/baz")); + +			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(stanzaChannel->sentStanzas.size())); +			CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<VCard>(0, JID("foo@bar.com/baz"), IQ::Get)); +		} + +		void testRequest_ReceiveEmitsNotification() { +			std::auto_ptr<VCardManager> testling = createManager(); +			testling->requestVCard(JID("foo@bar.com/baz")); +			stanzaChannel->onIQReceived(createVCardResult()); + +			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(changes.size())); +			CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com/baz"), changes[0].first); +			CPPUNIT_ASSERT_EQUAL(String("Foo Bar"), changes[0].second->getFullName()); +			CPPUNIT_ASSERT_EQUAL(String("Foo Bar"), vcardStorage->getVCard(JID("foo@bar.com/baz"))->getFullName()); +		} + +		void testRequest_Error() { +			std::auto_ptr<VCardManager> testling = createManager(); +			testling->requestVCard(JID("foo@bar.com/baz")); +			stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getID())); + +			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(changes.size())); +			CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com/baz"), changes[0].first); +			CPPUNIT_ASSERT_EQUAL(String(""), changes[0].second->getFullName()); +			CPPUNIT_ASSERT_EQUAL(String(""), vcardStorage->getVCard(JID("foo@bar.com/baz"))->getFullName()); +		} + +		void testRequest_VCardAlreadyRequested() { +			std::auto_ptr<VCardManager> testling = createManager(); +			testling->requestVCard(JID("foo@bar.com/baz")); +			VCard::ref result = testling->getVCardAndRequestWhenNeeded(JID("foo@bar.com/baz")); + +			CPPUNIT_ASSERT(!result); +			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(stanzaChannel->sentStanzas.size())); +		} + +		void testRequest_AfterPreviousRequest() { +			std::auto_ptr<VCardManager> testling = createManager(); +			testling->requestVCard(JID("foo@bar.com/baz")); +			stanzaChannel->onIQReceived(createVCardResult()); +			testling->requestVCard(JID("foo@bar.com/baz")); + +			CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(stanzaChannel->sentStanzas.size())); +			CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<VCard>(1, JID("foo@bar.com/baz"), IQ::Get)); +		} + +		void testRequestOwnVCard() { +			std::auto_ptr<VCardManager> testling = createManager(); +			testling->requestVCard(ownJID); +			stanzaChannel->onIQReceived(createOwnVCardResult()); + +			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(stanzaChannel->sentStanzas.size())); +			CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<VCard>(0, JID(), IQ::Get)); +			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(changes.size())); +			CPPUNIT_ASSERT_EQUAL(ownJID.toBare(), changes[0].first); +			CPPUNIT_ASSERT_EQUAL(String("Myself"), changes[0].second->getFullName()); +			CPPUNIT_ASSERT_EQUAL(String("Myself"), vcardStorage->getVCard(ownJID.toBare())->getFullName()); +		} + +	private: +		std::auto_ptr<VCardManager> createManager() { +			std::auto_ptr<VCardManager> manager(new VCardManager(ownJID, iqRouter, vcardStorage)); +			manager->onVCardChanged.connect(boost::bind(&VCardManagerTest::handleVCardChanged, this, _1, _2)); +			return manager; +		} + +		void handleVCardChanged(const JID& jid, VCard::ref vcard) { +			changes.push_back(std::pair<JID, VCard::ref>(jid, vcard)); +		} + +		IQ::ref createVCardResult() { +			VCard::ref vcard(new VCard()); +			vcard->setFullName("Foo Bar"); +			return IQ::createResult(JID("baz@fum.com/dum"), stanzaChannel->sentStanzas[0]->getID(), vcard); +		} + +		IQ::ref createOwnVCardResult() { +			VCard::ref vcard(new VCard()); +			vcard->setFullName("Myself"); +			return IQ::createResult(JID(), stanzaChannel->sentStanzas[0]->getID(), vcard); +		} + +	private: +		JID ownJID; +		DummyStanzaChannel* stanzaChannel; +		IQRouter* iqRouter; +		VCardMemoryStorage* vcardStorage; +		std::vector< std::pair<JID, VCard::ref> > changes; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(VCardManagerTest); diff --git a/Swiften/VCards/VCardFileStorage.cpp b/Swiften/VCards/VCardFileStorage.cpp index d3163fb..66bae04 100644 --- a/Swiften/VCards/VCardFileStorage.cpp +++ b/Swiften/VCards/VCardFileStorage.cpp @@ -47,7 +47,9 @@ void VCardFileStorage::setVCard(const JID& jid, boost::shared_ptr<VCard> v) {  }  boost::filesystem::path VCardFileStorage::getVCardPath(const JID& jid) const { -	return boost::filesystem::path(vcardsPath / (jid.toBare().toString().getUTF8String() + ".xml")); +	String file(jid.toString()); +	file.replaceAll('/', "%2f"); +	return boost::filesystem::path(vcardsPath / (file.getUTF8String() + ".xml"));  }  } diff --git a/Swiften/VCards/VCardFileStorage.h b/Swiften/VCards/VCardFileStorage.h index d75ac92..5f8cb1a 100644 --- a/Swiften/VCards/VCardFileStorage.h +++ b/Swiften/VCards/VCardFileStorage.h @@ -16,8 +16,8 @@ namespace Swift {  		public:  			VCardFileStorage(boost::filesystem::path dir); -			virtual boost::shared_ptr<VCard> getVCard(const JID& jid) const; -			virtual void setVCard(const JID& jid, boost::shared_ptr<VCard> v); +			virtual VCard::ref getVCard(const JID& jid) const; +			virtual void setVCard(const JID& jid, VCard::ref v);  		private:  			boost::filesystem::path getVCardPath(const JID&) const; diff --git a/Swiften/VCards/VCardFileStorageFactory.h b/Swiften/VCards/VCardFileStorageFactory.h index 136c6a7..27e50af 100644 --- a/Swiften/VCards/VCardFileStorageFactory.h +++ b/Swiften/VCards/VCardFileStorageFactory.h @@ -18,7 +18,7 @@ namespace Swift {  			}  			virtual VCardStorage* createVCardStorage(const String& profile) { -				return new VCardFileStorage(base / profile.getUTF8String()); +				return new VCardFileStorage(base / profile.getUTF8String() / "vcards");  			}  		private: diff --git a/Swiften/VCards/VCardManager.cpp b/Swiften/VCards/VCardManager.cpp index 0174dea..628f4c8 100644 --- a/Swiften/VCards/VCardManager.cpp +++ b/Swiften/VCards/VCardManager.cpp @@ -6,17 +6,49 @@  #include "Swiften/VCards/VCardManager.h" +#include <boost/bind.hpp> + +#include "Swiften/JID/JID.h" +#include "Swiften/VCards/VCardStorage.h" +#include "Swiften/Queries/Requests/GetVCardRequest.h" +  namespace Swift { -VCardManager::VCardManager(IQRouter* iqRouter, VCardStorage* vcardStorage) : iqRouter(iqRouter), storage(vcardStorage) { +VCardManager::VCardManager(const JID& ownJID, IQRouter* iqRouter, VCardStorage* vcardStorage) : ownJID(ownJID), iqRouter(iqRouter), storage(vcardStorage) {  } -boost::shared_ptr<VCard> VCardManager::getVCardAndRequestWhenNeeded(const JID& jid) const { -	boost::shared_ptr<VCard> vcard = storage->getVCard(jid); +VCard::ref VCardManager::getVCardAndRequestWhenNeeded(const JID& jid) { +	VCard::ref vcard = storage->getVCard(jid);  	if (!vcard) { -		// TODO: Request vcard if necessary +		requestVCard(jid);  	}  	return vcard;  } -	 + +void VCardManager::requestVCard(const JID& requestedJID) { +	JID jid = requestedJID.equals(ownJID, JID::WithoutResource) ? JID() : requestedJID; +	if (requestedVCards.find(jid) != requestedVCards.end()) { +		return; +	} +	GetVCardRequest::ref request(new GetVCardRequest(jid, iqRouter)); +	request->onResponse.connect(boost::bind(&VCardManager::handleVCardReceived, this, jid, _1, _2)); +	request->send(); +	requestedVCards.insert(jid); +} + +void VCardManager::requestOwnVCard() { +	requestVCard(JID()); +} + + +void VCardManager::handleVCardReceived(const JID& actualJID, VCard::ref vcard, const boost::optional<ErrorPayload>& error) { +	if (error) { +		vcard = VCard::ref(new VCard()); +	} +	requestedVCards.erase(actualJID); +	JID jid = actualJID.isValid() ? actualJID : ownJID.toBare(); +	storage->setVCard(jid, vcard); +	onVCardChanged(jid, vcard); +} +  } diff --git a/Swiften/VCards/VCardManager.h b/Swiften/VCards/VCardManager.h index bbf07c0..fc99128 100644 --- a/Swiften/VCards/VCardManager.h +++ b/Swiften/VCards/VCardManager.h @@ -6,23 +6,40 @@  #pragma once +#include <boost/signals.hpp> +#include <set> + +#include "Swiften/JID/JID.h"  #include "Swiften/Elements/VCard.h" +#include "Swiften/Elements/ErrorPayload.h"  namespace Swift {  	class JID;  	class VCardStorage; +	class IQRouter;  	class VCardManager {  		public: -			VCardManager(IQRouter* iqRouter, VCardStorage* vcardStorage); +			VCardManager(const JID& ownJID, IQRouter* iqRouter, VCardStorage* vcardStorage); -			virtual boost::shared_ptr<VCard> getVCardAndRequestWhenNeeded(const JID& jid) const ; +			VCard::ref getVCardAndRequestWhenNeeded(const JID& jid); +			void requestVCard(const JID& jid); +			void requestOwnVCard();  		public: -			boost::signal<void (const JID&)> onVCardChanged; +			/** +			 * The JID will always be bare. +			 */ +			boost::signal<void (const JID&, VCard::ref)> onVCardChanged; +			boost::signal<void (VCard::ref)> onOwnVCardChanged; + +		private: +			void handleVCardReceived(const JID& from, VCard::ref, const boost::optional<ErrorPayload>&);  		private: +			JID ownJID;  			IQRouter* iqRouter;  			VCardStorage* storage; +			std::set<JID> requestedVCards;  	};  } diff --git a/Swiften/VCards/VCardMemoryStorage.h b/Swiften/VCards/VCardMemoryStorage.h new file mode 100644 index 0000000..b2e99c6 --- /dev/null +++ b/Swiften/VCards/VCardMemoryStorage.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2010 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 <map> + +#include "Swiften/JID/JID.h" +#include "Swiften/VCards/VCardStorage.h" + +namespace Swift { +	class VCardMemoryStorage : public VCardStorage { +		public: +			VCardMemoryStorage() {} + +			virtual VCard::ref getVCard(const JID& jid) const { +				VCardMap::const_iterator i = vcards.find(jid); +				if (i != vcards.end()) { +					return i->second; +				} +				else { +					return VCard::ref(); +				} +			} + +			virtual void setVCard(const JID& jid, VCard::ref v) { +				vcards[jid] = v; +			} + +		private: +			typedef std::map<JID, VCard::ref> VCardMap; +			VCardMap vcards; +	}; +} diff --git a/Swiften/VCards/VCardStorage.h b/Swiften/VCards/VCardStorage.h index 2a9044c..854ccc6 100644 --- a/Swiften/VCards/VCardStorage.h +++ b/Swiften/VCards/VCardStorage.h @@ -17,7 +17,7 @@ namespace Swift {  		public:  			virtual ~VCardStorage(); -			virtual boost::shared_ptr<VCard> getVCard(const JID& jid) const = 0; -			virtual void setVCard(const JID&, boost::shared_ptr<VCard>) = 0; +			virtual VCard::ref getVCard(const JID& jid) const = 0; +			virtual void setVCard(const JID&, VCard::ref) = 0;  	};  }  | 
 Swift