diff options
Diffstat (limited to 'Swift/Controllers')
67 files changed, 1367 insertions, 118 deletions
| diff --git a/Swift/Controllers/AdHocManager.cpp b/Swift/Controllers/AdHocManager.cpp new file mode 100644 index 0000000..0fa63a1 --- /dev/null +++ b/Swift/Controllers/AdHocManager.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2010-2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swift/Controllers/AdHocManager.h> + +#include <boost/bind.hpp> +#include <boost/shared_ptr.hpp> + +#include <Swiften/Base/foreach.h> +#include <Swiften/Queries/IQRouter.h> +#include <Swiften/AdHoc/OutgoingAdHocCommandSession.h> +#include <Swift/Controllers/UIInterfaces/MainWindow.h> +#include <Swift/Controllers/UIInterfaces/AdHocCommandWindowFactory.h> +#include <Swift/Controllers/UIEvents/UIEventStream.h> +#include <Swift/Controllers/UIEvents/RequestAdHocUIEvent.h> + +namespace Swift { + +AdHocManager::AdHocManager(const JID& jid, AdHocCommandWindowFactory* factory, IQRouter* iqRouter, UIEventStream* uiEventStream, MainWindow* mainWindow) : jid_(jid) { +	iqRouter_ = iqRouter; +	uiEventStream_ = uiEventStream; +	mainWindow_ = mainWindow; +	factory_ = factory; + +	uiEventStream_->onUIEvent.connect(boost::bind(&AdHocManager::handleUIEvent, this, _1)); +} + +AdHocManager::~AdHocManager() { +	uiEventStream_->onUIEvent.disconnect(boost::bind(&AdHocManager::handleUIEvent, this, _1)); +} + +void AdHocManager::setServerDiscoInfo(boost::shared_ptr<DiscoInfo> info) { +	if (iqRouter_->isAvailable() && info->hasFeature(DiscoInfo::CommandsFeature)) { +		if (discoItemsRequest_) { +			discoItemsRequest_->onResponse.disconnect(boost::bind(&AdHocManager::handleServerDiscoItemsResponse, this, _1, _2)); +			discoItemsRequest_.reset(); +		} +		discoItemsRequest_ = GetDiscoItemsRequest::create(JID(jid_.getDomain()), DiscoInfo::CommandsFeature, iqRouter_); +		discoItemsRequest_->onResponse.connect(boost::bind(&AdHocManager::handleServerDiscoItemsResponse, this, _1, _2)); +		discoItemsRequest_->send(); +	} else { +		mainWindow_->setAvailableAdHocCommands(std::vector<DiscoItems::Item>()); +	} + +} + +void AdHocManager::handleServerDiscoItemsResponse(boost::shared_ptr<DiscoItems> items, ErrorPayload::ref error) { +	std::vector<DiscoItems::Item> commands; +	if (!error) { +		foreach (DiscoItems::Item item, items->getItems()) { +			if (item.getNode() != "http://isode.com/xmpp/commands#test") { +				commands.push_back(item); +			} +		} +	} +	mainWindow_->setAvailableAdHocCommands(commands); +} + +void AdHocManager::handleUIEvent(boost::shared_ptr<UIEvent> event) { +	boost::shared_ptr<RequestAdHocUIEvent> adHocEvent = boost::dynamic_pointer_cast<RequestAdHocUIEvent>(event); +	if (adHocEvent) { +		factory_->createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession>(new OutgoingAdHocCommandSession(adHocEvent->getCommand(), factory_, iqRouter_))); +	} +} + +} diff --git a/Swift/Controllers/AdHocManager.h b/Swift/Controllers/AdHocManager.h new file mode 100644 index 0000000..47b03cd --- /dev/null +++ b/Swift/Controllers/AdHocManager.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2010-2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <boost/shared_ptr.hpp> +#include <vector> + +#include <Swiften/Base/boost_bsignals.h> +#include <Swiften/JID/JID.h> +#include <Swiften/Elements/DiscoInfo.h> +#include <Swiften/Elements/DiscoItems.h> +#include <Swiften/Elements/ErrorPayload.h> +#include <Swiften/Disco/GetDiscoItemsRequest.h> +#include <Swift/Controllers/UIEvents/UIEvent.h> + +namespace Swift { +	class IQRouter; +	class MainWindow; +	class UIEventStream; +	class AdHocCommandWindowFactory; +	class AdHocManager { +		public: +			AdHocManager(const JID& jid, AdHocCommandWindowFactory* factory, IQRouter* iqRouter, UIEventStream* uiEventStream, MainWindow* mainWindow); +			~AdHocManager(); +			void setServerDiscoInfo(boost::shared_ptr<DiscoInfo> info); +		private: +			void handleUIEvent(boost::shared_ptr<UIEvent> event); +			void handleServerDiscoItemsResponse(boost::shared_ptr<DiscoItems>, ErrorPayload::ref error); +			JID jid_; +			IQRouter* iqRouter_; +			UIEventStream* uiEventStream_; +			MainWindow* mainWindow_; +			AdHocCommandWindowFactory* factory_; +			GetDiscoItemsRequest::ref discoItemsRequest_; +	}; +} diff --git a/Swift/Controllers/CertificateMemoryStorageFactory.h b/Swift/Controllers/CertificateMemoryStorageFactory.h new file mode 100644 index 0000000..adbce80 --- /dev/null +++ b/Swift/Controllers/CertificateMemoryStorageFactory.h @@ -0,0 +1,28 @@ +/* + * 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 <Swift/Controllers/Storages/CertificateStorageFactory.h> +#include <Swift/Controllers/Storages/CertificateMemoryStorage.h> + +namespace Swift { +	class CertificateFactory; + +	class CertificateMemoryStorageFactory : public CertificateStorageFactory { +		public: +			CertificateMemoryStorageFactory() { +			} + +			virtual CertificateStorage* createCertificateStorage(const JID&) const { +				return new CertificateMemoryStorage(); +			} + +		private: +			boost::filesystem::path basePath; +			CertificateFactory* certificateFactory; +	}; +} diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp index 22ef68d..9767f8d 100644 --- a/Swift/Controllers/Chat/ChatController.cpp +++ b/Swift/Controllers/Chat/ChatController.cpp @@ -102,8 +102,23 @@ void ChatController::preHandleIncomingMessage(boost::shared_ptr<MessageEvent> me  			setToJID(from);  		}  	} -	chatStateNotifier_->receivedMessageFromContact(message->getPayload<ChatState>()); +	boost::shared_ptr<Replace> replace = message->getPayload<Replace>(); +	if (replace) { +		// Determine the timestamp +		boost::posix_time::ptime timeStamp = boost::posix_time::microsec_clock::universal_time(); +		boost::optional<boost::posix_time::ptime> messageTimeStamp = getMessageTimestamp(message); +		if (messageTimeStamp) { +			timeStamp = *messageTimeStamp; +		} +		std::string body = message->getBody(); +		chatWindow_->replaceMessage(body, lastMessageUIID_, timeStamp); +		replacedMessage_ = true; +	} +	else { +		replacedMessage_ = false; +	}  	chatStateTracker_->handleMessageReceived(message); +	chatStateNotifier_->receivedMessageFromContact(message->getPayload<ChatState>());  }  void ChatController::postHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent) { @@ -116,21 +131,30 @@ void ChatController::preSendMessageRequest(boost::shared_ptr<Message> message) {  }  void ChatController::postSendMessage(const std::string& body, boost::shared_ptr<Stanza> sentStanza) { -	std::string id = addMessage(body, QT_TRANSLATE_NOOP("", "me"), true, labelsEnabled_ ? chatWindow_->getSelectedSecurityLabel().getLabel() : boost::shared_ptr<SecurityLabel>(), std::string(avatarManager_->getAvatarPath(selfJID_).string()), boost::posix_time::microsec_clock::universal_time()); -	if (stanzaChannel_->getStreamManagementEnabled()) { -		chatWindow_->setAckState(id, ChatWindow::Pending); -		unackedStanzas_[sentStanza] = id; +	if (stanzaChannel_->getStreamManagementEnabled() && !myLastMessageUIID_.empty()) { +		chatWindow_->setAckState(myLastMessageUIID_, ChatWindow::Pending); +		unackedStanzas_[sentStanza] = myLastMessageUIID_; +	} +	boost::shared_ptr<Replace> replace = sentStanza->getPayload<Replace>(); +	if (replace) { +		chatWindow_->replaceMessage(body, myLastMessageUIID_, boost::posix_time::microsec_clock::universal_time()); +	} else { +		myLastMessageUIID_ = addMessage(body, QT_TRANSLATE_NOOP("", "me"), true, labelsEnabled_ ? chatWindow_->getSelectedSecurityLabel().getLabel() : boost::shared_ptr<SecurityLabel>(), std::string(avatarManager_->getAvatarPath(selfJID_).string()), boost::posix_time::microsec_clock::universal_time()); +		if (stanzaChannel_->getStreamManagementEnabled()) { +			chatWindow_->setAckState(myLastMessageUIID_, ChatWindow::Pending); +			unackedStanzas_[sentStanza] = myLastMessageUIID_; +		}  	}  	lastWasPresence_ = false;  	chatStateNotifier_->userSentMessage();  }  void ChatController::handleStanzaAcked(boost::shared_ptr<Stanza> stanza) { -	std::string id = unackedStanzas_[stanza]; -	if (id != "") { -		chatWindow_->setAckState(id, ChatWindow::Received); +	std::map<boost::shared_ptr<Stanza>, std::string>::iterator unackedStanza = unackedStanzas_.find(stanza); +	if (unackedStanza != unackedStanzas_.end()) { +		chatWindow_->setAckState(unackedStanza->second, ChatWindow::Received); +		unackedStanzas_.erase(unackedStanza);  	} -	unackedStanzas_.erase(unackedStanzas_.find(stanza));  }  void ChatController::setOnline(bool online) { diff --git a/Swift/Controllers/Chat/ChatController.h b/Swift/Controllers/Chat/ChatController.h index dd4bf90..4fafb44 100644 --- a/Swift/Controllers/Chat/ChatController.h +++ b/Swift/Controllers/Chat/ChatController.h @@ -40,6 +40,7 @@ namespace Swift {  			NickResolver* nickResolver_;  			ChatStateNotifier* chatStateNotifier_;  			ChatStateTracker* chatStateTracker_; +			std::string myLastMessageUIID_;  			bool isInMUC_;  			bool lastWasPresence_;  			std::string lastStatusChangeString_; diff --git a/Swift/Controllers/Chat/ChatControllerBase.cpp b/Swift/Controllers/Chat/ChatControllerBase.cpp index 281d968..14e17cd 100644 --- a/Swift/Controllers/Chat/ChatControllerBase.cpp +++ b/Swift/Controllers/Chat/ChatControllerBase.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -30,9 +30,10 @@ namespace Swift {  ChatControllerBase::ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory) : selfJID_(self), stanzaChannel_(stanzaChannel), iqRouter_(iqRouter), chatWindowFactory_(chatWindowFactory), toJID_(toJID), labelsEnabled_(false), presenceOracle_(presenceOracle), avatarManager_(avatarManager), useDelayForLatency_(useDelayForLatency), eventController_(eventController), timerFactory_(timerFactory) {  	chatWindow_ = chatWindowFactory_->createChatWindow(toJID, eventStream);  	chatWindow_->onAllMessagesRead.connect(boost::bind(&ChatControllerBase::handleAllMessagesRead, this)); -	chatWindow_->onSendMessageRequest.connect(boost::bind(&ChatControllerBase::handleSendMessageRequest, this, _1)); +	chatWindow_->onSendMessageRequest.connect(boost::bind(&ChatControllerBase::handleSendMessageRequest, this, _1, _2));  	setOnline(stanzaChannel->isAvailable() && iqRouter->isAvailable());  	createDayChangeTimer(); +	replacedMessage_ = false;  }  ChatControllerBase::~ChatControllerBase() { @@ -88,7 +89,7 @@ void ChatControllerBase::handleAllMessagesRead() {  	chatWindow_->setUnreadMessageCount(0);  } -void ChatControllerBase::handleSendMessageRequest(const std::string &body) { +void ChatControllerBase::handleSendMessageRequest(const std::string &body, bool isCorrectionMessage) {  	if (!stanzaChannel_->isAvailable() || body.empty()) {  		return;  	} @@ -104,8 +105,13 @@ void ChatControllerBase::handleSendMessageRequest(const std::string &body) {  		boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time();  		message->addPayload(boost::shared_ptr<Delay>(new Delay(now, selfJID_)));  	} +	if (isCorrectionMessage) { +		message->addPayload(boost::shared_ptr<Replace> (new Replace(lastSentMessageStanzaID_))); +	} +	message->setID(lastSentMessageStanzaID_ = idGenerator_.generateID());  	stanzaChannel_->sendMessage(message);  	postSendMessage(message->getBody(), boost::dynamic_pointer_cast<Stanza>(message)); +	onActivity(message->getBody());  }  void ChatControllerBase::handleSecurityLabelsCatalogResponse(boost::shared_ptr<SecurityLabelsCatalog> catalog, ErrorPayload::ref error) { @@ -163,7 +169,7 @@ void ChatControllerBase::handleIncomingMessage(boost::shared_ptr<MessageEvent> m  		JID from = message->getFrom();  		std::vector<boost::shared_ptr<Delay> > delayPayloads = message->getPayloads<Delay>();  		for (size_t i = 0; useDelayForLatency_ && i < delayPayloads.size(); i++) { -			if (!delayPayloads[i]->getFrom()) {  +			if (!delayPayloads[i]->getFrom()) {  				continue;  			}  			boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time(); @@ -179,8 +185,10 @@ void ChatControllerBase::handleIncomingMessage(boost::shared_ptr<MessageEvent> m  		if (messageTimeStamp) {  			timeStamp = *messageTimeStamp;  		} - -		addMessage(body, senderDisplayNameFromMessage(from), isIncomingMessageFromMe(message), label, std::string(avatarManager_->getAvatarPath(from).string()), timeStamp); +		onActivity(body); +		if (!replacedMessage_) { +			lastMessageUIID_ = addMessage(body, senderDisplayNameFromMessage(from), isIncomingMessageFromMe(message), label, std::string(avatarManager_->getAvatarPath(from).string()), timeStamp); +		}  	}  	chatWindow_->show();  	chatWindow_->setUnreadMessageCount(unreadMessages_.size()); diff --git a/Swift/Controllers/Chat/ChatControllerBase.h b/Swift/Controllers/Chat/ChatControllerBase.h index 9573b1b..e0f1b94 100644 --- a/Swift/Controllers/Chat/ChatControllerBase.h +++ b/Swift/Controllers/Chat/ChatControllerBase.h @@ -26,6 +26,7 @@  #include "Swiften/Elements/ErrorPayload.h"  #include "Swiften/Presence/PresenceOracle.h"  #include "Swiften/Queries/IQRouter.h" +#include "Swiften/Base/IDGenerator.h"  namespace Swift {  	class IQRouter; @@ -47,6 +48,8 @@ namespace Swift {  			virtual void setOnline(bool online);  			virtual void setEnabled(bool enabled);  			virtual void setToJID(const JID& jid) {toJID_ = jid;}; +			/** Used for determining when something is recent.*/ +			boost::signal<void (const std::string& /*activity*/)> onActivity;  		protected:  			ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory); @@ -64,8 +67,10 @@ namespace Swift {  			virtual void dayTicked() {};  		private: +			IDGenerator idGenerator_; +			std::string lastSentMessageStanzaID_;  			void createDayChangeTimer(); -			void handleSendMessageRequest(const std::string &body); +			void handleSendMessageRequest(const std::string &body, bool isCorrectionMessage);  			void handleAllMessagesRead();  			void handleSecurityLabelsCatalogResponse(boost::shared_ptr<SecurityLabelsCatalog>, ErrorPayload::ref error);  			std::string getErrorMessage(boost::shared_ptr<ErrorPayload>); @@ -80,6 +85,8 @@ namespace Swift {  			ChatWindow* chatWindow_;  			JID toJID_;  			bool labelsEnabled_; +			bool replacedMessage_; +			std::string lastMessageUIID_;  			PresenceOracle* presenceOracle_;  			AvatarManager* avatarManager_;  			bool useDelayForLatency_; diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp index 94d4b9a..b60f9a3 100644 --- a/Swift/Controllers/Chat/ChatsManager.cpp +++ b/Swift/Controllers/Chat/ChatsManager.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -7,7 +7,9 @@  #include "Swift/Controllers/Chat/ChatsManager.h"  #include <boost/bind.hpp> +#include <boost/algorithm/string.hpp> +#include <Swiften/Base/foreach.h>  #include "Swift/Controllers/Chat/ChatController.h"  #include "Swift/Controllers/Chat/MUCSearchController.h"  #include "Swift/Controllers/XMPPEvents/EventController.h" @@ -26,12 +28,15 @@  #include "Swiften/MUC/MUCManager.h"  #include "Swiften/Elements/ChatState.h"  #include "Swiften/MUC/MUCBookmarkManager.h" +#include <Swift/Controllers/ProfileSettingsProvider.h>  namespace Swift {  typedef std::pair<JID, ChatController*> JIDChatControllerPair;  typedef std::pair<JID, MUCController*> JIDMUCControllerPair; +#define RECENT_CHATS "recent_chats" +  ChatsManager::ChatsManager(  		JID jid, StanzaChannel* stanzaChannel,   		IQRouter* iqRouter,  @@ -49,7 +54,7 @@ ChatsManager::ChatsManager(  		EntityCapsProvider* entityCapsProvider,   		MUCManager* mucManager,  		MUCSearchWindowFactory* mucSearchWindowFactory, -		SettingsProvider* settings) :  +		ProfileSettingsProvider* settings) :  			jid_(jid),   			joinMUCWindowFactory_(joinMUCWindowFactory),   			useDelayForLatency_(useDelayForLatency),  @@ -68,6 +73,7 @@ ChatsManager::ChatsManager(  	presenceSender_ = presenceSender;  	uiEventStream_ = uiEventStream;  	mucBookmarkManager_ = NULL; +	profileSettings_ = settings;  	presenceOracle_->onPresenceChange.connect(boost::bind(&ChatsManager::handlePresenceChange, this, _1));  	uiEventConnection_ = uiEventStream_->onUIEvent.connect(boost::bind(&ChatsManager::handleUIEvent, this, _1));  	chatListWindow_ = chatListWindowFactory->createChatListWindow(uiEventStream_); @@ -75,6 +81,7 @@ ChatsManager::ChatsManager(  	mucSearchController_ = new MUCSearchController(jid_, mucSearchWindowFactory, iqRouter, settings);  	mucSearchController_->onMUCSelected.connect(boost::bind(&ChatsManager::handleMUCSelectedAfterSearch, this, _1));  	setupBookmarks(); +	loadRecents();  }  ChatsManager::~ChatsManager() { @@ -89,6 +96,44 @@ ChatsManager::~ChatsManager() {  	delete mucSearchController_;  } +void ChatsManager::saveRecents() { +	std::string recents; +	foreach (ChatListWindow::Chat chat, recentChats_) { +		std::vector<std::string> activity; +		boost::split(activity, chat.activity, boost::is_any_of("\t\n")); +		std::string recent = chat.jid.toString() + "\t" + activity[0] + "\t" + (chat.isMUC ? "true" : "false") +  "\t" + chat.nick; +		recents += recent + "\n"; +	} +	profileSettings_->storeString(RECENT_CHATS, recents); +} + +void ChatsManager::loadRecents() { +	std::string recentsString(profileSettings_->getStringSetting(RECENT_CHATS)); +	std::vector<std::string> recents; +	boost::split(recents, recentsString, boost::is_any_of("\n")); +	int i = 0; +	foreach (std::string recentString, recents) { +		if (i++ > 30) { +			break; +		} +		std::vector<std::string> recent; +		boost::split(recent, recentString, boost::is_any_of("\t")); +		if (recent.size() < 4) { +			continue; +		} +		JID jid(recent[0]); +		if (!jid.isValid()) { +			continue; +		} +		std::string activity(recent[1]); +		bool isMUC = recent[2] == "true"; +		std::string nick(recent[3]); +		ChatListWindow::Chat chat(jid, nickResolver_->jidToNick(jid), activity, isMUC, nick); +		recentChats_.push_back(chat); +		chatListWindow_->setRecents(recentChats_); +	} +} +  void ChatsManager::setupBookmarks() {  	if (!mucBookmarkManager_) {  		mucBookmarkManager_ = new MUCBookmarkManager(iqRouter_); @@ -98,7 +143,7 @@ void ChatsManager::setupBookmarks() {  		if (chatListWindow_) {  			chatListWindow_->setBookmarksEnabled(false); -			chatListWindow_->clear(); +			chatListWindow_->clearBookmarks();  		}  	}  } @@ -122,6 +167,16 @@ void ChatsManager::handleMUCBookmarkRemoved(const MUCBookmark& bookmark) {  	chatListWindow_->removeMUCBookmark(bookmark);  } +void ChatsManager::handleChatActivity(const JID& jid, const std::string& activity) { +	/* FIXME: MUC use requires changes here. */ +	ChatListWindow::Chat chat(jid, nickResolver_->jidToNick(jid), activity, false); +	/* FIXME: handle nick changes */ +	recentChats_.erase(std::remove(recentChats_.begin(), recentChats_.end(), chat), recentChats_.end()); +	recentChats_.push_front(chat); +	chatListWindow_->setRecents(recentChats_); +	saveRecents(); +} +  void ChatsManager::handleUserLeftMUC(MUCController* mucController) {  	std::map<JID, MUCController*>::iterator it;  	for (it = mucControllers_.begin(); it != mucControllers_.end(); it++) { @@ -157,13 +212,13 @@ void ChatsManager::handleUIEvent(boost::shared_ptr<UIEvent> event) {  	else if (JoinMUCUIEvent::ref joinEvent = boost::dynamic_pointer_cast<JoinMUCUIEvent>(event)) {  		handleJoinMUCRequest(joinEvent->getJID(), joinEvent->getNick(), false);  	} -	else if (boost::dynamic_pointer_cast<RequestJoinMUCUIEvent>(event)) { +	else if (boost::shared_ptr<RequestJoinMUCUIEvent> joinEvent = boost::dynamic_pointer_cast<RequestJoinMUCUIEvent>(event)) {  		if (!joinMUCWindow_) {  			joinMUCWindow_ = joinMUCWindowFactory_->createJoinMUCWindow();  			joinMUCWindow_->onJoinMUC.connect(boost::bind(&ChatsManager::handleJoinMUCRequest, this, _1, _2, _3));  			joinMUCWindow_->onSearchMUC.connect(boost::bind(&ChatsManager::handleSearchMUCRequest, this));  		} -		joinMUCWindow_->setMUC(""); +		joinMUCWindow_->setMUC(joinEvent->getRoom());  		joinMUCWindow_->setNick(nickResolver_->jidToNick(jid_));  		joinMUCWindow_->show();  	} @@ -244,6 +299,7 @@ ChatController* ChatsManager::createNewChatController(const JID& contact) {  	ChatController* controller = new ChatController(jid_, stanzaChannel_, iqRouter_, chatWindowFactory_, contact, nickResolver_, presenceOracle_, avatarManager_, mucRegistry_->isMUC(contact.toBare()), useDelayForLatency_, uiEventStream_, eventController_, timerFactory_, entityCapsProvider_);  	chatControllers_[contact] = controller;  	controller->setAvailableServerFeatures(serverDiscoInfo_); +	controller->onActivity.connect(boost::bind(&ChatsManager::handleChatActivity, this, contact, _1));  	return controller;  } @@ -302,6 +358,7 @@ void ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::optional  		controller->onUserLeft.connect(boost::bind(&ChatsManager::handleUserLeftMUC, this, controller));  	}  	mucControllers_[mucJID]->activateChatWindow(); +	/* FIXME: handleChatActivity connection for recents, and changes to that method.*/  }  void ChatsManager::handleSearchMUCRequest() { diff --git a/Swift/Controllers/Chat/ChatsManager.h b/Swift/Controllers/Chat/ChatsManager.h index 3740186..f489034 100644 --- a/Swift/Controllers/Chat/ChatsManager.h +++ b/Swift/Controllers/Chat/ChatsManager.h @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -11,13 +11,14 @@  #include <boost/shared_ptr.hpp>  #include <string> -#include "Swiften/Elements/DiscoInfo.h" -#include "Swiften/Elements/Message.h" -#include "Swiften/Elements/Presence.h" -#include "Swiften/JID/JID.h" -#include "Swiften/MUC/MUCRegistry.h" -#include "Swift/Controllers/UIEvents/UIEventStream.h" -#include "Swiften/MUC/MUCBookmark.h" +#include <Swiften/Elements/DiscoInfo.h> +#include <Swiften/Elements/Message.h> +#include <Swiften/Elements/Presence.h> +#include <Swiften/JID/JID.h> +#include <Swiften/MUC/MUCRegistry.h> +#include <Swift/Controllers/UIEvents/UIEventStream.h> +#include <Swift/Controllers/UIInterfaces/ChatListWindow.h> +#include <Swiften/MUC/MUCBookmark.h>  namespace Swift {  	class EventController; @@ -34,18 +35,17 @@ namespace Swift {  	class IQRouter;  	class PresenceSender;  	class MUCBookmarkManager; -	class ChatListWindow;  	class ChatListWindowFactory;  	class TimerFactory;  	class EntityCapsProvider;  	class DirectedPresenceSender;  	class MUCSearchWindowFactory; -	class SettingsProvider; +	class ProfileSettingsProvider;  	class MUCSearchController;  	class ChatsManager {  		public: -			ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, JoinMUCWindowFactory* joinMUCWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsProvider* entityCapsProvider, MUCManager* mucManager, MUCSearchWindowFactory* mucSearchWindowFactory, SettingsProvider* settings); +			ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, JoinMUCWindowFactory* joinMUCWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsProvider* entityCapsProvider, MUCManager* mucManager, MUCSearchWindowFactory* mucSearchWindowFactory, ProfileSettingsProvider* settings);  			virtual ~ChatsManager();  			void setAvatarManager(AvatarManager* avatarManager);  			void setOnline(bool enabled); @@ -63,7 +63,11 @@ namespace Swift {  			void handleMUCBookmarkRemoved(const MUCBookmark& bookmark);  			void handleUserLeftMUC(MUCController* mucController);  			void handleBookmarksReady(); +			void handleChatActivity(const JID& jid, const std::string& activity);  			void setupBookmarks(); +			void loadRecents(); +			void saveRecents(); +			void handleChatMadeRecent();  			ChatController* getChatControllerOrFindAnother(const JID &contact);  			ChatController* createNewChatController(const JID &contact);  			ChatController* getChatControllerOrCreate(const JID &contact); @@ -92,5 +96,7 @@ namespace Swift {  			EntityCapsProvider* entityCapsProvider_;  			MUCManager* mucManager;  			MUCSearchController* mucSearchController_; +			std::list<ChatListWindow::Chat> recentChats_; +			ProfileSettingsProvider* profileSettings_;  	};  } diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp index 2914116..0604dee 100644 --- a/Swift/Controllers/Chat/MUCController.cpp +++ b/Swift/Controllers/Chat/MUCController.cpp @@ -4,7 +4,7 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#include "Swift/Controllers/Chat/MUCController.h" +#include <Swift/Controllers/Chat/MUCController.h>  #include <boost/bind.hpp>  #include <boost/regex.hpp> @@ -12,23 +12,24 @@  #include <Swift/Controllers/Intl.h>  #include <Swiften/Base/format.h> -#include "Swiften/Network/Timer.h" -#include "Swiften/Network/TimerFactory.h" -#include "Swiften/Base/foreach.h" -#include "SwifTools/TabComplete.h" -#include "Swiften/Base/foreach.h" -#include "Swift/Controllers/XMPPEvents/EventController.h" -#include "Swift/Controllers/UIInterfaces/ChatWindow.h" -#include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h" -#include "Swift/Controllers/UIEvents/UIEventStream.h" -#include "Swift/Controllers/UIEvents/RequestChatUIEvent.h" -#include "Swiften/Avatars/AvatarManager.h" -#include "Swiften/Elements/Delay.h" -#include "Swiften/MUC/MUC.h" -#include "Swiften/Client/StanzaChannel.h" -#include "Swift/Controllers/Roster/Roster.h" -#include "Swift/Controllers/Roster/SetAvatar.h" -#include "Swift/Controllers/Roster/SetPresence.h" +#include <Swiften/Network/Timer.h> +#include <Swiften/Network/TimerFactory.h> +#include <Swiften/Base/foreach.h> +#include <SwifTools/TabComplete.h> +#include <Swiften/Base/foreach.h> +#include <Swift/Controllers/XMPPEvents/EventController.h> +#include <Swift/Controllers/UIInterfaces/ChatWindow.h> +#include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h> +#include <Swift/Controllers/UIEvents/UIEventStream.h> +#include <Swift/Controllers/UIEvents/RequestChatUIEvent.h> +#include <Swift/Controllers/Roster/GroupRosterItem.h> +#include <Swiften/Avatars/AvatarManager.h> +#include <Swiften/Elements/Delay.h> +#include <Swiften/MUC/MUC.h> +#include <Swiften/Client/StanzaChannel.h> +#include <Swift/Controllers/Roster/Roster.h> +#include <Swift/Controllers/Roster/SetAvatar.h> +#include <Swift/Controllers/Roster/SetPresence.h>  #define MUC_JOIN_WARNING_TIMEOUT_MILLISECONDS 60000 @@ -206,7 +207,12 @@ void MUCController::handleOccupantJoined(const MUCOccupant& occupant) {  	currentOccupants_.insert(occupant.getNick());  	NickJoinPart event(occupant.getNick(), Join);  	appendToJoinParts(joinParts_, event); -	roster_->addContact(jid, realJID, occupant.getNick(), roleToGroupName(occupant.getRole()), avatarManager_->getAvatarPath(jid).string()); +	std::string groupName(roleToGroupName(occupant.getRole())); +	roster_->addContact(jid, realJID, occupant.getNick(), groupName, avatarManager_->getAvatarPath(jid).string()); +	if (addedRosterGroups_.count(groupName) == 0) { +		roster_->getGroup(groupName)->setManualSort(roleToSortName(occupant.getRole())); +		addedRosterGroups_.insert(groupName); +	}  	if (joined_) {  		std::string joinString;  		MUCOccupant::Role role = occupant.getRole(); @@ -248,6 +254,16 @@ std::string MUCController::roleToFriendlyName(MUCOccupant::Role role) {  	return "";  } +std::string MUCController::roleToSortName(MUCOccupant::Role role) { +	switch (role) { +	case MUCOccupant::Moderator: return "1"; +	case MUCOccupant::Participant: return "2"; +	case MUCOccupant::Visitor: return "3"; +	case MUCOccupant::NoRole: return "4"; +	} +	return "5"; +} +  JID MUCController::nickToJID(const std::string& nick) {  	return JID(toJID_.getNode(), toJID_.getDomain(), nick);  } diff --git a/Swift/Controllers/Chat/MUCController.h b/Swift/Controllers/Chat/MUCController.h index ebdd6cd..e876791 100644 --- a/Swift/Controllers/Chat/MUCController.h +++ b/Swift/Controllers/Chat/MUCController.h @@ -7,18 +7,18 @@  #pragma once  #include <boost/shared_ptr.hpp> -#include "Swiften/Base/boost_bsignals.h" +#include <Swiften/Base/boost_bsignals.h>  #include <boost/signals/connection.hpp>  #include <set>  #include <string> -#include "Swiften/Network/Timer.h" -#include "Swift/Controllers/Chat/ChatControllerBase.h" -#include "Swiften/Elements/Message.h" -#include "Swiften/Elements/DiscoInfo.h" -#include "Swiften/JID/JID.h" -#include "Swiften/MUC/MUC.h" -#include "Swiften/Elements/MUCOccupant.h" +#include <Swiften/Network/Timer.h> +#include <Swift/Controllers/Chat/ChatControllerBase.h> +#include <Swiften/Elements/Message.h> +#include <Swiften/Elements/DiscoInfo.h> +#include <Swiften/JID/JID.h> +#include <Swiften/MUC/MUC.h> +#include <Swiften/Elements/MUCOccupant.h>  namespace Swift {  	class StanzaChannel; @@ -71,6 +71,7 @@ namespace Swift {  			void handleJoinFailed(boost::shared_ptr<ErrorPayload> error);  			void handleJoinTimeoutTick();  			std::string roleToGroupName(MUCOccupant::Role role); +			std::string roleToSortName(MUCOccupant::Role role);  			JID nickToJID(const std::string& nick);  			std::string roleToFriendlyName(MUCOccupant::Role role);  			void receivedActivity(); @@ -97,6 +98,7 @@ namespace Swift {  			std::set<std::string> currentOccupants_;  			std::vector<NickJoinPart> joinParts_;  			boost::posix_time::ptime lastActivity_; +			std::set<std::string> addedRosterGroups_;  	};  } diff --git a/Swift/Controllers/Chat/MUCSearchController.cpp b/Swift/Controllers/Chat/MUCSearchController.cpp index 743aabb..2cb89b4 100644 --- a/Swift/Controllers/Chat/MUCSearchController.cpp +++ b/Swift/Controllers/Chat/MUCSearchController.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -11,6 +11,7 @@  #include <boost/bind.hpp>  #include <boost/shared_ptr.hpp> +#include <Swiften/Base/foreach.h>  #include <Swiften/Disco/GetDiscoItemsRequest.h>  #include <Swiften/Base/Log.h>  #include <Swiften/Base/String.h> @@ -23,7 +24,7 @@ namespace Swift {  static const std::string SEARCHED_SERVICES = "searchedServices"; -MUCSearchController::MUCSearchController(const JID& jid, MUCSearchWindowFactory* factory, IQRouter* iqRouter, SettingsProvider* settings) : jid_(jid), factory_(factory), iqRouter_(iqRouter), settings_(settings), window_(NULL), walker_(NULL) { +MUCSearchController::MUCSearchController(const JID& jid, MUCSearchWindowFactory* factory, IQRouter* iqRouter, ProfileSettingsProvider* settings) : jid_(jid), factory_(factory), iqRouter_(iqRouter), settings_(settings), window_(NULL), walker_(NULL) {  	itemsInProgress_ = 0;  	loadSavedServices();  } diff --git a/Swift/Controllers/Chat/MUCSearchController.h b/Swift/Controllers/Chat/MUCSearchController.h index c8040ed..f90e4a7 100644 --- a/Swift/Controllers/Chat/MUCSearchController.h +++ b/Swift/Controllers/Chat/MUCSearchController.h @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -16,7 +16,7 @@  #include "Swiften/JID/JID.h"  #include "Swift/Controllers/UIEvents/UIEvent.h" -#include "Swift/Controllers/Settings/SettingsProvider.h" +#include "Swift/Controllers/ProfileSettingsProvider.h"  #include "Swiften/Elements/DiscoInfo.h"  #include "Swiften/Elements/DiscoItems.h"  #include "Swiften/Elements/ErrorPayload.h" @@ -87,7 +87,7 @@ namespace Swift {  	class MUCSearchController {  		public: -			MUCSearchController(const JID& jid, MUCSearchWindowFactory* mucSearchWindowFactory, IQRouter* iqRouter, SettingsProvider* settings); +			MUCSearchController(const JID& jid, MUCSearchWindowFactory* mucSearchWindowFactory, IQRouter* iqRouter, ProfileSettingsProvider* settings);  			~MUCSearchController();  			void openSearchWindow(); @@ -112,7 +112,7 @@ namespace Swift {  			JID jid_;  			MUCSearchWindowFactory* factory_;  			IQRouter* iqRouter_; -			SettingsProvider* settings_; +			ProfileSettingsProvider* settings_;  			MUCSearchWindow* window_;  			DiscoServiceWalker* walker_;  			std::list<JID> services_; diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp index 40f7445..f8fda9a 100644 --- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp +++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -10,6 +10,7 @@  #include "Swift/Controllers/Chat/ChatsManager.h" +#include "Swift/Controllers/Chat/UnitTest/MockChatListWindow.h"  #include "Swift/Controllers/UIInterfaces/ChatWindow.h"  #include "Swift/Controllers/Settings/DummySettingsProvider.h"  #include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h" @@ -38,6 +39,7 @@  #include "Swift/Controllers/UIEvents/RequestChatUIEvent.h"  #include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h"  #include "Swift/Controllers/UIEvents/UIEventStream.h" +#include <Swift/Controllers/ProfileSettingsProvider.h>  using namespace Swift; @@ -82,8 +84,10 @@ public:  		chatListWindowFactory_ = mocks_->InterfaceMock<ChatListWindowFactory>();  		mucSearchWindowFactory_ = mocks_->InterfaceMock<MUCSearchWindowFactory>();  		settings_ = new DummySettingsProvider(); -		mocks_->ExpectCall(chatListWindowFactory_, ChatListWindowFactory::createChatListWindow).With(uiEventStream_).Return(NULL); -		manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, joinMUCWindowFactory_, nickResolver_, presenceOracle_, directedPresenceSender_, uiEventStream_, chatListWindowFactory_, true, NULL, mucRegistry_, entityCapsManager_, mucManager_, mucSearchWindowFactory_, settings_); +		profileSettings_ = new ProfileSettingsProvider("a", settings_); +		chatListWindow_ = new MockChatListWindow(); +		mocks_->ExpectCall(chatListWindowFactory_, ChatListWindowFactory::createChatListWindow).With(uiEventStream_).Return(chatListWindow_); +		manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, joinMUCWindowFactory_, nickResolver_, presenceOracle_, directedPresenceSender_, uiEventStream_, chatListWindowFactory_, true, NULL, mucRegistry_, entityCapsManager_, mucManager_, mucSearchWindowFactory_, profileSettings_);  		avatarManager_ = new NullAvatarManager();  		manager_->setAvatarManager(avatarManager_); @@ -92,6 +96,7 @@ public:  	void tearDown() {  		//delete chatListWindowFactory_;  		delete settings_; +		delete profileSettings_;  		delete mocks_;  		delete avatarManager_;  		delete manager_; @@ -109,6 +114,7 @@ public:  		delete xmppRoster_;  		delete entityCapsManager_;  		delete capsProvider_; +		delete chatListWindow_;  	}  	void testFirstOpenWindowIncoming() { @@ -346,6 +352,8 @@ private:  	CapsProvider* capsProvider_;  	MUCManager* mucManager_;  	DummySettingsProvider* settings_; +	ProfileSettingsProvider* profileSettings_; +	ChatListWindow* chatListWindow_;  };  CPPUNIT_TEST_SUITE_REGISTRATION(ChatsManagerTest); diff --git a/Swift/Controllers/Chat/UnitTest/MockChatListWindow.h b/Swift/Controllers/Chat/UnitTest/MockChatListWindow.h new file mode 100644 index 0000000..408a490 --- /dev/null +++ b/Swift/Controllers/Chat/UnitTest/MockChatListWindow.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include "Swift/Controllers/UIInterfaces/ChatListWindow.h" + +namespace Swift { + +	class MockChatListWindow : public ChatListWindow { +		public: +			MockChatListWindow() {}; +			virtual ~MockChatListWindow() {}; +			void addMUCBookmark(const MUCBookmark& /*bookmark*/) {} +			void removeMUCBookmark(const MUCBookmark& /*bookmark*/) {} +			void setBookmarksEnabled(bool /*enabled*/) {} +			void setRecents(const std::list<ChatListWindow::Chat>& /*recents*/) {} +			void clearBookmarks() {} +	}; + +} diff --git a/Swift/Controllers/Chat/UserSearchController.cpp b/Swift/Controllers/Chat/UserSearchController.cpp index b3403d7..deac2f9 100644 --- a/Swift/Controllers/Chat/UserSearchController.cpp +++ b/Swift/Controllers/Chat/UserSearchController.cpp @@ -9,9 +9,9 @@  #include <boost/bind.hpp>  #include <boost/shared_ptr.hpp> +#include <Swiften/Base/foreach.h>  #include <Swiften/Disco/GetDiscoInfoRequest.h>  #include <Swiften/Disco/GetDiscoItemsRequest.h> -  #include <Swift/Controllers/DiscoServiceWalker.h>  #include <Swift/Controllers/UIEvents/UIEventStream.h>  #include <Swift/Controllers/UIEvents/RequestChatWithUserDialogUIEvent.h> diff --git a/Swift/Controllers/ChatMessageSummarizer.cpp b/Swift/Controllers/ChatMessageSummarizer.cpp new file mode 100644 index 0000000..682d88b --- /dev/null +++ b/Swift/Controllers/ChatMessageSummarizer.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swift/Controllers/ChatMessageSummarizer.h> + +#include <Swiften/Base/format.h> +#include <Swift/Controllers/Intl.h> +#include <Swiften/Base/foreach.h> + +using namespace Swift; +using namespace std; + +string ChatMessageSummarizer::getSummary(const string& current, const vector<UnreadPair>& unreads) { +	vector<UnreadPair> others; +	int currentUnread = 0; +	int otherCount = 0; +	foreach (UnreadPair unread, unreads) { +		if (unread.first == current) { +			currentUnread += unread.second; +		} else { +			if (unread.second > 0) { +				otherCount += unread.second; +				others.push_back(unread); +			} +		} +	} +	string myString(current); + +	if (currentUnread > 0) { +		string result(QT_TRANSLATE_NOOP("", "%1% (%2%)")); +		myString = str(format(result) % current % currentUnread); +	} + +	if (others.size() > 1) { +		string result(QT_TRANSLATE_NOOP("", "%1% and %2% others (%3%)")); +		myString = str(format(result) % myString % others.size() % otherCount); +	} else if (others.size() > 0) { +		string result(QT_TRANSLATE_NOOP("", "%1%, %2% (%3%)")); +		myString = str(format(result) % myString % others[0].first % otherCount); +	} +	return myString; +} diff --git a/Swift/Controllers/ChatMessageSummarizer.h b/Swift/Controllers/ChatMessageSummarizer.h new file mode 100644 index 0000000..d4ff188 --- /dev/null +++ b/Swift/Controllers/ChatMessageSummarizer.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <vector> +#include <utility> +#include <string> + +namespace Swift { +typedef std::pair<std::string, int> UnreadPair; + +	class ChatMessageSummarizer { +		public: +			std::string getSummary(const std::string& current, const std::vector<UnreadPair>& unreads); +	}; +} diff --git a/Swift/Controllers/DiscoServiceWalker.cpp b/Swift/Controllers/DiscoServiceWalker.cpp index ce29927..6aed6eb 100644 --- a/Swift/Controllers/DiscoServiceWalker.cpp +++ b/Swift/Controllers/DiscoServiceWalker.cpp @@ -6,6 +6,7 @@  #include <Swift/Controllers/DiscoServiceWalker.h>  #include <Swiften/Base/Log.h> +#include <Swiften/Base/foreach.h>  #include <boost/bind.hpp> diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp index 9a35cc1..c00c080 100644 --- a/Swift/Controllers/MainController.cpp +++ b/Swift/Controllers/MainController.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -18,10 +18,8 @@  #include <Swift/Controllers/UIInterfaces/UIFactory.h>  #include "Swiften/Network/TimerFactory.h"  #include "Swift/Controllers/BuildVersion.h" -#include "Swift/Controllers/StoragesFactory.h"  #include "Swiften/Client/Storages.h"  #include "Swiften/VCards/VCardManager.h" -#include "Swift/Controllers/Chat/MUCSearchController.h"  #include "Swift/Controllers/Chat/UserSearchController.h"  #include "Swift/Controllers/Chat/ChatsManager.h"  #include "Swift/Controllers/XMPPEvents/EventController.h" @@ -41,6 +39,7 @@  #include "Swift/Controllers/UIEvents/UIEventStream.h"  #include "Swift/Controllers/PresenceNotifier.h"  #include "Swift/Controllers/EventNotifier.h" +#include "Swift/Controllers/Storages/StoragesFactory.h"  #include "SwifTools/Dock/Dock.h"  #include "SwifTools/Notifier/TogglableNotifier.h"  #include "Swiften/Base/foreach.h" @@ -60,11 +59,13 @@  #include "Swift/Controllers/UIEvents/RequestChatUIEvent.h"  #include "Swift/Controllers/UIEvents/ToggleNotificationsUIEvent.h"  #include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h" -#include "Swift/Controllers/CertificateStorageFactory.h" -#include "Swift/Controllers/CertificateStorageTrustChecker.h" +#include "Swift/Controllers/Storages/CertificateStorageFactory.h" +#include "Swift/Controllers/Storages/CertificateStorageTrustChecker.h"  #include "Swiften/Network/NetworkFactories.h"  #include <Swift/Controllers/ProfileController.h>  #include <Swift/Controllers/ContactEditController.h> +#include <Swift/Controllers/XMPPURIController.h> +#include "Swift/Controllers/AdHocManager.h"  namespace Swift { @@ -84,6 +85,7 @@ MainController::MainController(  		CertificateStorageFactory* certificateStorageFactory,  		Dock* dock,  		Notifier* notifier, +		URIHandler* uriHandler,  		bool useDelayForLatency) :  			eventLoop_(eventLoop),  			networkFactories_(networkFactories), @@ -92,6 +94,7 @@ MainController::MainController(  			storagesFactory_(storagesFactory),  			certificateStorageFactory_(certificateStorageFactory),  			settings_(settings), +			uriHandler_(uriHandler),  			loginWindow_(NULL) ,  			useDelayForLatency_(useDelayForLatency) {  	storages_ = NULL; @@ -121,6 +124,8 @@ MainController::MainController(  	loginWindow_ = uiFactory_->createLoginWindow(uiEventStream_);  	soundEventController_ = new SoundEventController(eventController_, soundPlayer, settings, uiEventStream_); +	xmppURIController_ = new XMPPURIController(uriHandler_, uiEventStream_); +  	std::string selectedLoginJID = settings_->getStringSetting("lastLoginJID");  	bool loginAutomatically = settings_->getBoolSetting("loginAutomatically", false);  	std::string cachedPassword; @@ -167,6 +172,7 @@ MainController::~MainController() {  	resetClient();  	delete xmlConsoleController_; +	delete xmppURIController_;  	delete soundEventController_;  	delete systemTrayController_;  	delete eventController_; @@ -247,7 +253,7 @@ void MainController::handleConnected() {  		contactEditController_ = new ContactEditController(rosterController_, uiFactory_, uiEventStream_); -		chatsManager_ = new ChatsManager(jid_, client_->getStanzaChannel(), client_->getIQRouter(), eventController_, uiFactory_, uiFactory_, client_->getNickResolver(), client_->getPresenceOracle(), client_->getPresenceSender(), uiEventStream_, uiFactory_, useDelayForLatency_, networkFactories_->getTimerFactory(), client_->getMUCRegistry(), client_->getEntityCapsProvider(), client_->getMUCManager(), uiFactory_, settings_); +		chatsManager_ = new ChatsManager(jid_, client_->getStanzaChannel(), client_->getIQRouter(), eventController_, uiFactory_, uiFactory_, client_->getNickResolver(), client_->getPresenceOracle(), client_->getPresenceSender(), uiEventStream_, uiFactory_, useDelayForLatency_, networkFactories_->getTimerFactory(), client_->getMUCRegistry(), client_->getEntityCapsProvider(), client_->getMUCManager(), uiFactory_, profileSettings_);  		client_->onMessageReceived.connect(boost::bind(&ChatsManager::handleIncomingMessage, chatsManager_, _1));  		chatsManager_->setAvatarManager(client_->getAvatarManager()); @@ -262,14 +268,14 @@ void MainController::handleConnected() {  		client_->getDiscoManager()->setCapsNode(CLIENT_NODE);  		client_->getDiscoManager()->setDiscoInfo(discoInfo); -  		userSearchControllerChat_ = new UserSearchController(UserSearchController::StartChat, jid_, uiEventStream_, uiFactory_, client_->getIQRouter(), rosterController_);  		userSearchControllerAdd_ = new UserSearchController(UserSearchController::AddContact, jid_, uiEventStream_, uiFactory_, client_->getIQRouter(), rosterController_); +		adHocManager_ = new AdHocManager(boundJID_, uiFactory_, client_->getIQRouter(), uiEventStream_, rosterController_->getWindow());  	}  	client_->requestRoster(); -	GetDiscoInfoRequest::ref discoInfoRequest = GetDiscoInfoRequest::create(JID(), client_->getIQRouter()); +	GetDiscoInfoRequest::ref discoInfoRequest = GetDiscoInfoRequest::create(boundJID_.toBare(), client_->getIQRouter());  	discoInfoRequest->onResponse.connect(boost::bind(&MainController::handleServerDiscoInfoResponse, this, _1, _2));  	discoInfoRequest->send(); @@ -356,19 +362,25 @@ void MainController::handleInputIdleChanged(bool idle) {  }  void MainController::handleLoginRequest(const std::string &username, const std::string &password, const std::string& certificateFile, bool remember, bool loginAutomatically) { -	loginWindow_->setMessage(""); -	loginWindow_->setIsLoggingIn(true); -	profileSettings_ = new ProfileSettingsProvider(username, settings_); -	profileSettings_->storeString("jid", username); -	profileSettings_->storeString("certificate", certificateFile); -	profileSettings_->storeString("pass", (remember || loginAutomatically) ? password : ""); -	settings_->storeString("lastLoginJID", username); -	settings_->storeBool("loginAutomatically", loginAutomatically); -	loginWindow_->addAvailableAccount(profileSettings_->getStringSetting("jid"), profileSettings_->getStringSetting("pass"), profileSettings_->getStringSetting("certificate"));  	jid_ = JID(username); -	password_ = password; -	certificateFile_ = certificateFile; -	performLoginFromCachedCredentials(); +	if (!jid_.isValid() || jid_.getNode().empty()) { +		loginWindow_->setMessage(QT_TRANSLATE_NOOP("", "User address invalid. User address should be of the form 'alice@wonderland.lit'")); +		loginWindow_->setIsLoggingIn(false); +	} else { +		loginWindow_->setMessage(""); +		loginWindow_->setIsLoggingIn(true); +		profileSettings_ = new ProfileSettingsProvider(username, settings_); +		profileSettings_->storeString("jid", username); +		profileSettings_->storeString("certificate", certificateFile); +		profileSettings_->storeString("pass", (remember || loginAutomatically) ? password : ""); +		settings_->storeString("lastLoginJID", username); +		settings_->storeBool("loginAutomatically", loginAutomatically); +		loginWindow_->addAvailableAccount(profileSettings_->getStringSetting("jid"), profileSettings_->getStringSetting("pass"), profileSettings_->getStringSetting("certificate")); + +		password_ = password; +		certificateFile_ = certificateFile; +		performLoginFromCachedCredentials(); +	}  }  void MainController::handlePurgeSavedLoginRequest(const std::string& username) { @@ -483,6 +495,7 @@ void MainController::handleDisconnected(const boost::optional<ClientError>& erro  		else if (!rosterController_) { //hasn't been logged in yet  			signOut();  			loginWindow_->setMessage(message); +			loginWindow_->setIsLoggingIn(false);  		} else {  			logout();  			setReconnectTimer(); @@ -496,6 +509,9 @@ void MainController::handleDisconnected(const boost::optional<ClientError>& erro  			eventController_->handleIncomingEvent(lastDisconnectError_);  		}  	} +	else if (!rosterController_) { //hasn't been logged in yet +		loginWindow_->setIsLoggingIn(false); +	}  }  void MainController::setReconnectTimer() { @@ -554,6 +570,7 @@ void MainController::setManagersOffline() {  void MainController::handleServerDiscoInfoResponse(boost::shared_ptr<DiscoInfo> info, ErrorPayload::ref error) {  	if (!error) {  		chatsManager_->setServerDiscoInfo(info); +		adHocManager_->setServerDiscoInfo(info);  	}  } diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h index f402f8f..757abb8 100644 --- a/Swift/Controllers/MainController.h +++ b/Swift/Controllers/MainController.h @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -62,6 +62,10 @@ namespace Swift {  	class Storages;  	class StoragesFactory;  	class NetworkFactories; +	class URIHandler; +	class XMPPURIController; +	class AdHocManager; +	class AdHocCommandWindowFactory;  	class MainController {  		public: @@ -76,6 +80,7 @@ namespace Swift {  					CertificateStorageFactory* certificateStorageFactory,  					Dock* dock,  					Notifier* notifier, +					URIHandler* uriHandler,  					bool useDelayForLatency);  			~MainController(); @@ -123,12 +128,14 @@ namespace Swift {  			SettingsProvider *settings_;  			ProfileSettingsProvider* profileSettings_;  			Dock* dock_; +			URIHandler* uriHandler_;  			TogglableNotifier* notifier_;  			PresenceNotifier* presenceNotifier_;  			EventNotifier* eventNotifier_;  			RosterController* rosterController_;  			EventController* eventController_;  			EventWindowController* eventWindowController_; +			AdHocManager* adHocManager_;  			LoginWindow* loginWindow_;  			UIEventStream* uiEventStream_;  			XMLConsoleController* xmlConsoleController_; @@ -139,6 +146,7 @@ namespace Swift {  			JID boundJID_;  			SystemTrayController* systemTrayController_;  			SoundEventController* soundEventController_; +			XMPPURIController* xmppURIController_;  			std::string vCardPhotoHash_;  			std::string password_;  			std::string certificateFile_; diff --git a/Swift/Controllers/ProfileSettingsProvider.h b/Swift/Controllers/ProfileSettingsProvider.h index 74bcd12..8ba250c 100644 --- a/Swift/Controllers/ProfileSettingsProvider.h +++ b/Swift/Controllers/ProfileSettingsProvider.h @@ -7,6 +7,7 @@  #pragma once  #include "Swift/Controllers/Settings/SettingsProvider.h" +#include <Swiften/Base/foreach.h>  namespace Swift { diff --git a/Swift/Controllers/Roster/ContactRosterItem.cpp b/Swift/Controllers/Roster/ContactRosterItem.cpp index 0fe88aa..894f64b 100644 --- a/Swift/Controllers/Roster/ContactRosterItem.cpp +++ b/Swift/Controllers/Roster/ContactRosterItem.cpp @@ -7,6 +7,8 @@  #include "Swift/Controllers/Roster/ContactRosterItem.h"  #include "Swift/Controllers/Roster/GroupRosterItem.h" +#include <Swiften/Base/foreach.h> +  namespace Swift { diff --git a/Swift/Controllers/Roster/GroupRosterItem.cpp b/Swift/Controllers/Roster/GroupRosterItem.cpp index f0a377a..b8fad07 100644 --- a/Swift/Controllers/Roster/GroupRosterItem.cpp +++ b/Swift/Controllers/Roster/GroupRosterItem.cpp @@ -12,7 +12,7 @@  namespace Swift { -GroupRosterItem::GroupRosterItem(const std::string& name, GroupRosterItem* parent, bool sortByStatus) : RosterItem(name, parent), sortByStatus_(sortByStatus) { +GroupRosterItem::GroupRosterItem(const std::string& name, GroupRosterItem* parent, bool sortByStatus) : RosterItem(name, parent), sortByStatus_(sortByStatus), manualSort_(false) {  	expanded_ = true;  } @@ -20,6 +20,19 @@ GroupRosterItem::~GroupRosterItem() {  } +void GroupRosterItem::setManualSort(const std::string& manualSortValue) { +	manualSort_ = true; +	bool changed = manualSortValue_ != manualSortValue; +	manualSortValue_ = manualSortValue; +	if (changed) { +		onDataChanged(); +	} +} + +const std::string& GroupRosterItem::getSortableDisplayName() const { +	return manualSort_ ? manualSortValue_ : RosterItem::getSortableDisplayName(); +} +  bool GroupRosterItem::isExpanded() const {  	return expanded_;  } diff --git a/Swift/Controllers/Roster/GroupRosterItem.h b/Swift/Controllers/Roster/GroupRosterItem.h index 57fa9fa..beb7705 100644 --- a/Swift/Controllers/Roster/GroupRosterItem.h +++ b/Swift/Controllers/Roster/GroupRosterItem.h @@ -31,6 +31,8 @@ class GroupRosterItem : public RosterItem {  		void setExpanded(bool expanded);  		bool isExpanded() const;  		boost::signal<void (bool)> onExpandedChanged; +		void setManualSort(const std::string& manualSortValue); +		virtual const std::string& getSortableDisplayName() const;  	private:  		void handleChildrenChanged(GroupRosterItem* group);  		void handleDataChanged(RosterItem* item); @@ -40,6 +42,8 @@ class GroupRosterItem : public RosterItem {  		std::vector<RosterItem*> children_;  		std::vector<RosterItem*> displayedChildren_;  		bool sortByStatus_; +		bool manualSort_; +		std::string manualSortValue_;  };  } diff --git a/Swift/Controllers/Roster/RosterGroupExpandinessPersister.cpp b/Swift/Controllers/Roster/RosterGroupExpandinessPersister.cpp index c1045ee..0a242ae 100644 --- a/Swift/Controllers/Roster/RosterGroupExpandinessPersister.cpp +++ b/Swift/Controllers/Roster/RosterGroupExpandinessPersister.cpp @@ -9,6 +9,7 @@  #include <boost/bind.hpp>  #include <vector> +#include <Swiften/Base/foreach.h>  #include "Swiften/Base/String.h"  #include "Swift/Controllers/Roster/GroupRosterItem.h" diff --git a/Swift/Controllers/Roster/RosterItem.cpp b/Swift/Controllers/Roster/RosterItem.cpp index 3f130bb..77db8a3 100644 --- a/Swift/Controllers/Roster/RosterItem.cpp +++ b/Swift/Controllers/Roster/RosterItem.cpp @@ -33,11 +33,11 @@ void RosterItem::setDisplayName(const std::string& name) {  	onDataChanged();  } -std::string RosterItem::getDisplayName() const { +const std::string& RosterItem::getDisplayName() const {  	return name_;  } -std::string RosterItem::getSortableDisplayName() const { +const std::string& RosterItem::getSortableDisplayName() const {  	return sortableDisplayName_;  } diff --git a/Swift/Controllers/Roster/RosterItem.h b/Swift/Controllers/Roster/RosterItem.h index ed8cb16..769f72d 100644 --- a/Swift/Controllers/Roster/RosterItem.h +++ b/Swift/Controllers/Roster/RosterItem.h @@ -20,8 +20,8 @@ class RosterItem {  		boost::signal<void ()> onDataChanged;  		GroupRosterItem* getParent() const;  		void setDisplayName(const std::string& name); -		std::string getDisplayName() const; -		std::string getSortableDisplayName() const; +		const std::string& getDisplayName() const; +		virtual const std::string& getSortableDisplayName() const;  	private:  		std::string name_;  		std::string sortableDisplayName_; diff --git a/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp b/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp index 16f2745..ca74dbb 100644 --- a/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp +++ b/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp @@ -8,6 +8,7 @@  #include <cppunit/extensions/HelperMacros.h>  #include <cppunit/extensions/TestFactoryRegistry.h> +#include <Swiften/Base/foreach.h>  #include "Swift/Controllers/Roster/RosterController.h"  #include "Swift/Controllers/UnitTest/MockMainWindowFactory.h"  // #include "Swiften/Elements/Payload.h" diff --git a/Swift/Controllers/SConscript b/Swift/Controllers/SConscript index 61da9fb..eed1f4d 100644 --- a/Swift/Controllers/SConscript +++ b/Swift/Controllers/SConscript @@ -44,16 +44,25 @@ if env["SCONS_STAGE"] == "build" :  			"StatusTracker.cpp",  			"PresenceNotifier.cpp",  			"EventNotifier.cpp", +			"AdHocManager.cpp",  			"XMPPEvents/EventController.cpp",  			"UIEvents/UIEvent.cpp",  			"UIInterfaces/XMLConsoleWidget.cpp",  			"UIInterfaces/ChatListWindow.cpp",  			"PreviousStatusStore.cpp", -			"CertificateStorageFactory.cpp", -			"CertificateStorage.cpp", -			"CertificateFileStorage.cpp", +			"Storages/CertificateStorageFactory.cpp", +			"Storages/CertificateStorage.cpp", +			"Storages/CertificateFileStorage.cpp", +			"Storages/CertificateMemoryStorage.cpp", +			"Storages/AvatarFileStorage.cpp", +			"Storages/FileStorages.cpp", +			"Storages/RosterFileStorage.cpp", +			"Storages/CapsFileStorage.cpp", +			"Storages/VCardFileStorage.cpp",  			"StatusUtil.cpp",  			"Translator.cpp", +			"XMPPURIController.cpp", +			"ChatMessageSummarizer.cpp",  		])  	env.Append(UNITTEST_SOURCES = [ @@ -64,4 +73,5 @@ if env["SCONS_STAGE"] == "build" :  			File("Chat/UnitTest/ChatsManagerTest.cpp"),  			File("Chat/UnitTest/MUCControllerTest.cpp"),  			File("UnitTest/MockChatWindow.cpp"), +			File("UnitTest/ChatMessageSummarizerTest.cpp"),  		]) diff --git a/Swift/Controllers/Storages/AvatarFileStorage.cpp b/Swift/Controllers/Storages/AvatarFileStorage.cpp new file mode 100644 index 0000000..288f6fd --- /dev/null +++ b/Swift/Controllers/Storages/AvatarFileStorage.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swift/Controllers/Storages/AvatarFileStorage.h> + +#include <iostream> +#include <boost/filesystem/fstream.hpp> +#include <boost/filesystem.hpp> + +#include <Swiften/Base/foreach.h> +#include <Swiften/Base/String.h> +#include <Swiften/StringCodecs/SHA1.h> +#include <Swiften/StringCodecs/Hexify.h> + +namespace Swift { + +AvatarFileStorage::AvatarFileStorage(const boost::filesystem::path& avatarsDir, const boost::filesystem::path& avatarsFile) : avatarsDir(avatarsDir), avatarsFile(avatarsFile) { +	if (boost::filesystem::exists(avatarsFile)) { +		try { +			boost::filesystem::ifstream file(avatarsFile); +			std::string line; +			if (file.is_open()) { +				while (!file.eof()) { +					getline(file, line); +					std::pair<std::string, std::string> r = String::getSplittedAtFirst(line, ' '); +					JID jid(r.second); +					if (jid.isValid()) { +						jidAvatars.insert(std::make_pair(jid, r.first)); +					} +					else if (!r.first.empty() || !r.second.empty()) { +						std::cerr << "Invalid entry in avatars file: " << r.second << std::endl; +					} +				} +			} +		} +		catch (...) { +			std::cerr << "Error reading avatars file" << std::endl; +		} +	} +} + +bool AvatarFileStorage::hasAvatar(const std::string& hash) const {  +	return boost::filesystem::exists(getAvatarPath(hash)); +} + +void AvatarFileStorage::addAvatar(const std::string& hash, const ByteArray& avatar) { +	assert(Hexify::hexify(SHA1::getHash(avatar)) == hash); + +	boost::filesystem::path avatarPath = getAvatarPath(hash); +	if (!boost::filesystem::exists(avatarPath.parent_path())) { +		try { +			boost::filesystem::create_directories(avatarPath.parent_path()); +		} +		catch (const boost::filesystem::filesystem_error& e) { +			std::cerr << "ERROR: " << e.what() << std::endl; +		} +	} +	boost::filesystem::ofstream file(avatarPath, boost::filesystem::ofstream::binary|boost::filesystem::ofstream::out); +	file.write(reinterpret_cast<const char*>(avatar.getData()), static_cast<std::streamsize>(avatar.getSize())); +	file.close(); +} + +boost::filesystem::path AvatarFileStorage::getAvatarPath(const std::string& hash) const { +	return avatarsDir / hash; +} + +ByteArray AvatarFileStorage::getAvatar(const std::string& hash) const { +	ByteArray data; +	data.readFromFile(getAvatarPath(hash).string()); +	return data; +} + +void AvatarFileStorage::setAvatarForJID(const JID& jid, const std::string& hash) { +	std::pair<JIDAvatarMap::iterator, bool> r = jidAvatars.insert(std::make_pair(jid, hash)); +	if (r.second) { +		saveJIDAvatars(); +	} +	else if (r.first->second != hash) { +		r.first->second = hash; +		saveJIDAvatars(); +	} +} + +std::string AvatarFileStorage::getAvatarForJID(const JID& jid) const { +	JIDAvatarMap::const_iterator i = jidAvatars.find(jid); +	return i == jidAvatars.end() ? "" : i->second; +} + +void AvatarFileStorage::saveJIDAvatars() { +	try { +		boost::filesystem::ofstream file(avatarsFile); +		for (JIDAvatarMap::const_iterator i = jidAvatars.begin(); i != jidAvatars.end(); ++i) { +			file << i->second << " " << i->first.toString() << std::endl; +		} +		file.close(); +	} +	catch (...) { +		std::cerr << "Error writing avatars file" << std::endl; +	} +} + +} diff --git a/Swift/Controllers/Storages/AvatarFileStorage.h b/Swift/Controllers/Storages/AvatarFileStorage.h new file mode 100644 index 0000000..b7e73f5 --- /dev/null +++ b/Swift/Controllers/Storages/AvatarFileStorage.h @@ -0,0 +1,41 @@ +/* + * 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 <string> +#include <boost/filesystem/path.hpp> + +#include <Swiften/JID/JID.h> +#include "Swiften/Base/ByteArray.h" +#include "Swiften/Avatars/AvatarStorage.h" + +namespace Swift { +	class AvatarFileStorage : public AvatarStorage { +		public: +			AvatarFileStorage(const boost::filesystem::path& avatarsDir, const boost::filesystem::path& avatarsFile); + +			virtual bool hasAvatar(const std::string& hash) const; +			virtual void addAvatar(const std::string& hash, const ByteArray& avatar); +			virtual ByteArray getAvatar(const std::string& hash) const; + +			virtual boost::filesystem::path getAvatarPath(const std::string& hash) const; + +			virtual void setAvatarForJID(const JID& jid, const std::string& hash); +			virtual std::string getAvatarForJID(const JID& jid) const; + +		private: +			void saveJIDAvatars(); + +		private: +			boost::filesystem::path avatarsDir; +			boost::filesystem::path avatarsFile; +			typedef std::map<JID, std::string> JIDAvatarMap; +			JIDAvatarMap jidAvatars; +	}; + +} diff --git a/Swift/Controllers/Storages/CapsFileStorage.cpp b/Swift/Controllers/Storages/CapsFileStorage.cpp new file mode 100644 index 0000000..b7593fd --- /dev/null +++ b/Swift/Controllers/Storages/CapsFileStorage.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "Swift/Controllers/Storages/CapsFileStorage.h" + +#include <Swiften/Entity/GenericPayloadPersister.h> +#include "Swiften/Serializer/PayloadSerializers/DiscoInfoSerializer.h" +#include "Swiften/Parser/PayloadParsers/DiscoInfoParser.h" +#include "Swiften/StringCodecs/Hexify.h" +#include "Swiften/StringCodecs/Base64.h" + +using namespace Swift; + +typedef GenericPayloadPersister<DiscoInfo, DiscoInfoParser, DiscoInfoSerializer> DiscoInfoPersister; + +CapsFileStorage::CapsFileStorage(const boost::filesystem::path& path) : path(path) { +} + +DiscoInfo::ref CapsFileStorage::getDiscoInfo(const std::string& hash) const { +	return DiscoInfoPersister().loadPayloadGeneric(getCapsPath(hash)); +} + +void CapsFileStorage::setDiscoInfo(const std::string& hash, DiscoInfo::ref discoInfo) { +	DiscoInfo::ref bareDiscoInfo(new DiscoInfo(*discoInfo.get())); +	bareDiscoInfo->setNode(""); +	DiscoInfoPersister().savePayload(bareDiscoInfo, getCapsPath(hash)); +} + +boost::filesystem::path CapsFileStorage::getCapsPath(const std::string& hash) const { +	return path / (Hexify::hexify(Base64::decode(hash)) + ".xml"); +} diff --git a/Swift/Controllers/Storages/CapsFileStorage.h b/Swift/Controllers/Storages/CapsFileStorage.h new file mode 100644 index 0000000..b3757e0 --- /dev/null +++ b/Swift/Controllers/Storages/CapsFileStorage.h @@ -0,0 +1,28 @@ +/* + * 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/filesystem/path.hpp> + +#include "Swiften/Disco/CapsStorage.h" +#include <string> + +namespace Swift { +	class CapsFileStorage : public CapsStorage { +		public: +			CapsFileStorage(const boost::filesystem::path& path); + +			virtual DiscoInfo::ref getDiscoInfo(const std::string& hash) const; +			virtual void setDiscoInfo(const std::string& hash, DiscoInfo::ref discoInfo); + +		private: +			boost::filesystem::path getCapsPath(const std::string& hash) const; + +		private: +			boost::filesystem::path path; +	}; +} diff --git a/Swift/Controllers/CertificateFileStorage.cpp b/Swift/Controllers/Storages/CertificateFileStorage.cpp index cf924ee..31af949 100644 --- a/Swift/Controllers/CertificateFileStorage.cpp +++ b/Swift/Controllers/Storages/CertificateFileStorage.cpp @@ -4,7 +4,7 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#include <Swift/Controllers/CertificateFileStorage.h> +#include <Swift/Controllers/Storages/CertificateFileStorage.h>  #include <iostream>  #include <boost/filesystem/fstream.hpp> diff --git a/Swift/Controllers/CertificateFileStorage.h b/Swift/Controllers/Storages/CertificateFileStorage.h index 2b853ed..f7a60b9 100644 --- a/Swift/Controllers/CertificateFileStorage.h +++ b/Swift/Controllers/Storages/CertificateFileStorage.h @@ -8,7 +8,7 @@  #include <boost/filesystem.hpp> -#include "Swift/Controllers/CertificateStorage.h" +#include "Swift/Controllers/Storages/CertificateStorage.h"  namespace Swift {  	class CertificateFactory; diff --git a/Swift/Controllers/CertificateFileStorageFactory.h b/Swift/Controllers/Storages/CertificateFileStorageFactory.h index 7ed8287..b215165 100644 --- a/Swift/Controllers/CertificateFileStorageFactory.h +++ b/Swift/Controllers/Storages/CertificateFileStorageFactory.h @@ -6,8 +6,8 @@  #pragma once -#include <Swift/Controllers/CertificateStorageFactory.h> -#include <Swift/Controllers/CertificateFileStorage.h> +#include <Swift/Controllers/Storages/CertificateStorageFactory.h> +#include <Swift/Controllers/Storages/CertificateFileStorage.h>  namespace Swift {  	class CertificateFactory; diff --git a/Swift/Controllers/Storages/CertificateMemoryStorage.cpp b/Swift/Controllers/Storages/CertificateMemoryStorage.cpp new file mode 100644 index 0000000..71d7c4a --- /dev/null +++ b/Swift/Controllers/Storages/CertificateMemoryStorage.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swift/Controllers/Storages/CertificateMemoryStorage.h> + +#include <Swiften/Base/foreach.h> + +using namespace Swift; + +CertificateMemoryStorage::CertificateMemoryStorage() { +} + +bool CertificateMemoryStorage::hasCertificate(Certificate::ref certificate) const { +	foreach(Certificate::ref storedCert, certificates) { +		if (storedCert->toDER() == certificate->toDER()) { +			return true; +		} +	} +	return false; +} + +void CertificateMemoryStorage::addCertificate(Certificate::ref certificate) { +	certificates.push_back(certificate); +} diff --git a/Swift/Controllers/Storages/CertificateMemoryStorage.h b/Swift/Controllers/Storages/CertificateMemoryStorage.h new file mode 100644 index 0000000..5c0333d --- /dev/null +++ b/Swift/Controllers/Storages/CertificateMemoryStorage.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 <vector> + +#include <Swift/Controllers/Storages/CertificateStorage.h> + +namespace Swift { +	class CertificateMemoryStorage : public CertificateStorage { +		public: +			CertificateMemoryStorage(); + +			virtual bool hasCertificate(Certificate::ref certificate) const; +			virtual void addCertificate(Certificate::ref certificate); + +		private: +			std::vector<Certificate::ref> certificates; +	}; + +} diff --git a/Swift/Controllers/CertificateStorage.cpp b/Swift/Controllers/Storages/CertificateStorage.cpp index 343fccd..ee942c0 100644 --- a/Swift/Controllers/CertificateStorage.cpp +++ b/Swift/Controllers/Storages/CertificateStorage.cpp @@ -4,7 +4,7 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#include "Swift/Controllers/CertificateStorage.h" +#include "Swift/Controllers/Storages/CertificateStorage.h"  namespace Swift { diff --git a/Swift/Controllers/CertificateStorage.h b/Swift/Controllers/Storages/CertificateStorage.h index f8c6fb5..f8c6fb5 100644 --- a/Swift/Controllers/CertificateStorage.h +++ b/Swift/Controllers/Storages/CertificateStorage.h diff --git a/Swift/Controllers/CertificateStorageFactory.cpp b/Swift/Controllers/Storages/CertificateStorageFactory.cpp index 613a8c3..ba0179a 100644 --- a/Swift/Controllers/CertificateStorageFactory.cpp +++ b/Swift/Controllers/Storages/CertificateStorageFactory.cpp @@ -4,7 +4,7 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#include <Swift/Controllers/CertificateStorageFactory.h> +#include <Swift/Controllers/Storages/CertificateStorageFactory.h>  namespace Swift { diff --git a/Swift/Controllers/CertificateStorageFactory.h b/Swift/Controllers/Storages/CertificateStorageFactory.h index 5b85757..5b85757 100644 --- a/Swift/Controllers/CertificateStorageFactory.h +++ b/Swift/Controllers/Storages/CertificateStorageFactory.h diff --git a/Swift/Controllers/CertificateStorageTrustChecker.h b/Swift/Controllers/Storages/CertificateStorageTrustChecker.h index f33287c..40838dd 100644 --- a/Swift/Controllers/CertificateStorageTrustChecker.h +++ b/Swift/Controllers/Storages/CertificateStorageTrustChecker.h @@ -7,7 +7,7 @@  #pragma once  #include <Swiften/TLS/CertificateTrustChecker.h> -#include <Swift/Controllers/CertificateStorage.h> +#include <Swift/Controllers/Storages/CertificateStorage.h>  namespace Swift {  	/** diff --git a/Swift/Controllers/Storages/FileStorages.cpp b/Swift/Controllers/Storages/FileStorages.cpp new file mode 100644 index 0000000..6447099 --- /dev/null +++ b/Swift/Controllers/Storages/FileStorages.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "Swift/Controllers/Storages/FileStorages.h" +#include "Swift/Controllers/Storages/VCardFileStorage.h" +#include "Swift/Controllers/Storages/AvatarFileStorage.h" +#include "Swift/Controllers/Storages/CapsFileStorage.h" +#include "Swift/Controllers/Storages/RosterFileStorage.h" + +namespace Swift { + +FileStorages::FileStorages(const boost::filesystem::path& baseDir, const JID& jid) { +	std::string profile = jid.toBare(); +	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; +} + +VCardStorage* FileStorages::getVCardStorage() const { +	return vcardStorage; +} + +CapsStorage* FileStorages::getCapsStorage() const { +	return capsStorage; +} + +AvatarStorage* FileStorages::getAvatarStorage() const { +	return avatarStorage; +} + +RosterStorage* FileStorages::getRosterStorage() const { +	return rosterStorage; +} + +} diff --git a/Swift/Controllers/Storages/FileStorages.h b/Swift/Controllers/Storages/FileStorages.h new file mode 100644 index 0000000..28df314 --- /dev/null +++ b/Swift/Controllers/Storages/FileStorages.h @@ -0,0 +1,53 @@ +/* + * 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/filesystem/path.hpp> + +#include "Swiften/Client/Storages.h" + +namespace Swift { +	class VCardFileStorage; +	class AvatarFileStorage; +	class CapsFileStorage; +	class RosterFileStorage; +	class JID; + +	/** +	 * A storages implementation that stores all controller data on disk. +	 */ +	class FileStorages : public Storages { +		public: +			/** +			 * Creates the storages interface. +			 * +			 * All data will be stored relative to a base directory, and +			 * for some controllers, in a subdirectory for the given profile. +			 * The data is stored in the following places: +			 * - Avatars: <basedir>/avatars +			 * - VCards: <basedir>/<profile>/vcards +			 * - Entity capabilities: <basedir>/caps +			 * +			 * \param baseDir the base dir to store data relative to +			 * \param jid the subdir in which profile-specific data will be stored.  +			 *   The bare JID will be used as the subdir name. +			 */ +			FileStorages(const boost::filesystem::path& baseDir, const JID& jid); +			~FileStorages(); + +			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/Swift/Controllers/FileStoragesFactory.h b/Swift/Controllers/Storages/FileStoragesFactory.h index bd7cdfb..0676bc3 100644 --- a/Swift/Controllers/FileStoragesFactory.h +++ b/Swift/Controllers/Storages/FileStoragesFactory.h @@ -6,8 +6,8 @@  #pragma once -#include "Swift/Controllers/StoragesFactory.h" -#include "Swiften/Client/FileStorages.h" +#include "Swift/Controllers/Storages/StoragesFactory.h" +#include "Swift/Controllers/Storages/FileStorages.h"  namespace Swift {  	class FileStoragesFactory : public StoragesFactory { diff --git a/Swift/Controllers/MemoryStoragesFactory.h b/Swift/Controllers/Storages/MemoryStoragesFactory.h index 8408e10..0dea349 100644 --- a/Swift/Controllers/MemoryStoragesFactory.h +++ b/Swift/Controllers/Storages/MemoryStoragesFactory.h @@ -6,10 +6,12 @@  #pragma once -#include "Swift/Controllers/StoragesFactory.h" +#include "Swift/Controllers/Storages/StoragesFactory.h"  #include "Swiften/Client/MemoryStorages.h"  namespace Swift { +	class JID; +	  	class MemoryStoragesFactory : public StoragesFactory {  		public:  			MemoryStoragesFactory() {} diff --git a/Swift/Controllers/Storages/RosterFileStorage.cpp b/Swift/Controllers/Storages/RosterFileStorage.cpp new file mode 100644 index 0000000..73e582f --- /dev/null +++ b/Swift/Controllers/Storages/RosterFileStorage.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swift/Controllers/Storages/RosterFileStorage.h> + +#include <Swiften/Entity/GenericPayloadPersister.h> +#include <Swiften/Serializer/PayloadSerializers/RosterSerializer.h> +#include <Swiften/Parser/PayloadParsers/RosterParser.h> + +using namespace Swift; + +typedef GenericPayloadPersister<RosterPayload, RosterParser, RosterSerializer> RosterPersister; + +RosterFileStorage::RosterFileStorage(const boost::filesystem::path& path) : path(path) { +} + +boost::shared_ptr<RosterPayload> RosterFileStorage::getRoster() const { +	return RosterPersister().loadPayloadGeneric(path); +} + +void RosterFileStorage::setRoster(boost::shared_ptr<RosterPayload> roster) { +	RosterPersister().savePayload(roster, path); +} diff --git a/Swift/Controllers/Storages/RosterFileStorage.h b/Swift/Controllers/Storages/RosterFileStorage.h new file mode 100644 index 0000000..cb00969 --- /dev/null +++ b/Swift/Controllers/Storages/RosterFileStorage.h @@ -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. + */ + +#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); + +			virtual boost::shared_ptr<RosterPayload> getRoster() const; +			virtual void setRoster(boost::shared_ptr<RosterPayload>); + +		private: +			boost::filesystem::path path; +	}; +} diff --git a/Swift/Controllers/StoragesFactory.h b/Swift/Controllers/Storages/StoragesFactory.h index 441a4e9..203f9c9 100644 --- a/Swift/Controllers/StoragesFactory.h +++ b/Swift/Controllers/Storages/StoragesFactory.h @@ -8,6 +8,7 @@  namespace Swift {  	class Storages; +	class JID;  	class StoragesFactory {  		public: diff --git a/Swift/Controllers/Storages/VCardFileStorage.cpp b/Swift/Controllers/Storages/VCardFileStorage.cpp new file mode 100644 index 0000000..4933d0c --- /dev/null +++ b/Swift/Controllers/Storages/VCardFileStorage.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "Swift/Controllers/Storages/VCardFileStorage.h" + +#include <boost/filesystem/fstream.hpp> +#include <boost/filesystem.hpp> +#include <iostream> + +#include <Swiften/Entity/GenericPayloadPersister.h> +#include <Swiften/Base/String.h> +#include <Swiften/StringCodecs/Hexify.h> +#include <Swiften/StringCodecs/SHA1.h> +#include <Swiften/Base/foreach.h> +#include "Swiften/JID/JID.h" +#include "Swiften/Elements/VCard.h" +#include "Swiften/Serializer/PayloadSerializers/VCardSerializer.h" +#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadParserTester.h" +#include "Swiften/Parser/PayloadParsers/VCardParser.h" + +using namespace Swift; + +typedef GenericPayloadPersister<VCard, VCardParser, VCardSerializer> VCardPersister; + +VCardFileStorage::VCardFileStorage(boost::filesystem::path dir) : vcardsPath(dir) { +	cacheFile = vcardsPath / "phashes"; +	if (boost::filesystem::exists(cacheFile)) { +		try { +			boost::filesystem::ifstream file(cacheFile); +			std::string line; +			if (file.is_open()) { +				while (!file.eof()) { +					getline(file, line); +					std::pair<std::string, std::string> r = String::getSplittedAtFirst(line, ' '); +					JID jid(r.second); +					if (jid.isValid()) { +						photoHashes.insert(std::make_pair(jid, r.first)); +					} +					else if (!r.first.empty() || !r.second.empty()) { +						std::cerr << "Invalid entry in phashes file" << std::endl; +					} +				} +			} +		} +		catch (...) { +			std::cerr << "Error reading phashes file" << std::endl; +		} +	} +} + +boost::shared_ptr<VCard> VCardFileStorage::getVCard(const JID& jid) const { +	return VCardPersister().loadPayloadGeneric(getVCardPath(jid)); +} + +void VCardFileStorage::setVCard(const JID& jid, VCard::ref v) { +	VCardPersister().savePayload(v, getVCardPath(jid)); +	getAndUpdatePhotoHash(jid, v); +} + +boost::filesystem::path VCardFileStorage::getVCardPath(const JID& jid) const { +	std::string file(jid.toString()); +	String::replaceAll(file, '/', "%2f"); +	return boost::filesystem::path(vcardsPath / (file + ".xml")); +} + +std::string VCardFileStorage::getPhotoHash(const JID& jid) const { +	PhotoHashMap::const_iterator i = photoHashes.find(jid); +	if (i != photoHashes.end()) { +		return i->second; +	} +	else { +		VCard::ref vCard = getVCard(jid); +		return getAndUpdatePhotoHash(jid, vCard); +	} +} + +std::string VCardFileStorage::getAndUpdatePhotoHash(const JID& jid, VCard::ref vCard) const { +	std::string hash; +	if (vCard && !vCard->getPhoto().isEmpty()) { +		hash = Hexify::hexify(SHA1::getHash(vCard->getPhoto())); +	} +	std::pair<PhotoHashMap::iterator, bool> r = photoHashes.insert(std::make_pair(jid, hash)); +	if (r.second) { +		savePhotoHashes(); +	} +	else if (r.first->second != hash) { +		r.first->second = hash; +		savePhotoHashes(); +	} +	return hash; +} + +void VCardFileStorage::savePhotoHashes() const { +	try { +		boost::filesystem::ofstream file(cacheFile); +		for (PhotoHashMap::const_iterator i = photoHashes.begin(); i != photoHashes.end(); ++i) { +			file << i->second << " " << i->first.toString() << std::endl; +		} +		file.close(); +	} +	catch (...) { +		std::cerr << "Error writing vcards file" << std::endl; +	} +} diff --git a/Swift/Controllers/Storages/VCardFileStorage.h b/Swift/Controllers/Storages/VCardFileStorage.h new file mode 100644 index 0000000..ba422f4 --- /dev/null +++ b/Swift/Controllers/Storages/VCardFileStorage.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 <boost/filesystem/path.hpp> +#include <string> +#include <map> + +#include "Swiften/VCards/VCardStorage.h" + +namespace Swift { +	class VCardFileStorage : public VCardStorage { +		public: +			VCardFileStorage(boost::filesystem::path dir); + +			virtual VCard::ref getVCard(const JID& jid) const; +			virtual void setVCard(const JID& jid, VCard::ref v); + +			virtual std::string getPhotoHash(const JID&) const; + +		private: +			boost::filesystem::path getVCardPath(const JID&) const; + +			std::string getAndUpdatePhotoHash(const JID& jid, VCard::ref vcard) const; +			void savePhotoHashes() const; + +		private: +			boost::filesystem::path vcardsPath; +			boost::filesystem::path cacheFile; +			typedef std::map<JID, std::string> PhotoHashMap; +			mutable PhotoHashMap photoHashes; +	}; +} diff --git a/Swift/Controllers/UIEvents/RequestAdHocUIEvent.h b/Swift/Controllers/UIEvents/RequestAdHocUIEvent.h new file mode 100644 index 0000000..c3b4b49 --- /dev/null +++ b/Swift/Controllers/UIEvents/RequestAdHocUIEvent.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swift/Controllers/UIInterfaces/MainWindow.h> + +#include <Swift/Controllers/UIEvents/UIEvent.h> + +namespace Swift { +	class RequestAdHocUIEvent : public UIEvent { +		public: +			RequestAdHocUIEvent(const DiscoItems::Item& command) : command_(command) {}; +			const DiscoItems::Item& getCommand() const {return command_;} +		private: +			DiscoItems::Item command_; +	}; +} diff --git a/Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h b/Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h index dd2ff6c..2c7b105 100644 --- a/Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h +++ b/Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h @@ -6,18 +6,25 @@  #pragma once -#include <boost/optional.hpp>  #include <boost/shared_ptr.hpp> -  #include <string> +  #include <Swift/Controllers/UIEvents/UIEvent.h> +#include <Swiften/JID/JID.h>  namespace Swift {  	class RequestJoinMUCUIEvent : public UIEvent {  		public:  			typedef boost::shared_ptr<RequestJoinMUCUIEvent> ref; -			RequestJoinMUCUIEvent() { +			RequestJoinMUCUIEvent(const JID& room = JID()) : room(room) {  			} + +			const JID& getRoom() const { +				return room; +			} + +		private: +			JID room;  	};  } diff --git a/Swift/Controllers/UIInterfaces/AdHocCommandWindow.h b/Swift/Controllers/UIInterfaces/AdHocCommandWindow.h new file mode 100644 index 0000000..f7a5d39 --- /dev/null +++ b/Swift/Controllers/UIInterfaces/AdHocCommandWindow.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +namespace Swift { +	class AdHocCommandWindow { +		public: +			virtual ~AdHocCommandWindow() {}; +	}; +} diff --git a/Swift/Controllers/UIInterfaces/AdHocCommandWindowFactory.h b/Swift/Controllers/UIInterfaces/AdHocCommandWindowFactory.h new file mode 100644 index 0000000..ae77180 --- /dev/null +++ b/Swift/Controllers/UIInterfaces/AdHocCommandWindowFactory.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2010-2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swift/Controllers/UIInterfaces/AdHocCommandWindow.h> +#include <Swiften/AdHoc/OutgoingAdHocCommandSession.h> + +namespace Swift { +	class AdHocCommandWindowFactory { +		public: +			virtual ~AdHocCommandWindowFactory() {} +			/** +			 * The UI should deal with the lifetime of this window (i.e. DeleteOnClose), +			 * so the result isn't returned. +			 */ +			virtual void createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command) = 0; +	}; +} diff --git a/Swift/Controllers/UIInterfaces/ChatListWindow.h b/Swift/Controllers/UIInterfaces/ChatListWindow.h index a2a0874..f717684 100644 --- a/Swift/Controllers/UIInterfaces/ChatListWindow.h +++ b/Swift/Controllers/UIInterfaces/ChatListWindow.h @@ -1,23 +1,35 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */  #pragma once +#include <list>  #include <boost/shared_ptr.hpp> - -#include "Swiften/MUC/MUCBookmark.h" +#include <Swiften/MUC/MUCBookmark.h>  namespace Swift {  	class ChatListWindow {  		public: +			class Chat { +				public: +					Chat(const JID& jid, const std::string& chatName, const std::string& activity, bool isMUC, const std::string& nick = "") : jid(jid), chatName(chatName), activity(activity), isMUC(isMUC), nick(nick) {} +					/** Assume that nicks aren't important for equality */ +					bool operator==(const Chat& other) const {return jid == other.jid && isMUC == other.isMUC;}; +					JID jid; +					std::string chatName; +					std::string activity; +					bool isMUC; +					std::string nick; +			};  			virtual ~ChatListWindow();  			virtual void setBookmarksEnabled(bool enabled) = 0;  			virtual void addMUCBookmark(const MUCBookmark& bookmark) = 0;  			virtual void removeMUCBookmark(const MUCBookmark& bookmark) = 0; -			virtual void clear() = 0; +			virtual void setRecents(const std::list<Chat>& recents) = 0; +			virtual void clearBookmarks() = 0;  	};  } diff --git a/Swift/Controllers/UIInterfaces/ChatWindow.h b/Swift/Controllers/UIInterfaces/ChatWindow.h index c7bcf1e..aa4416b 100644 --- a/Swift/Controllers/UIInterfaces/ChatWindow.h +++ b/Swift/Controllers/UIInterfaces/ChatWindow.h @@ -40,6 +40,7 @@ namespace Swift {  			virtual void addSystemMessage(const std::string& message) = 0;  			virtual void addPresenceMessage(const std::string& message) = 0;  			virtual void addErrorMessage(const std::string& message) = 0; +			virtual void replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) = 0;  			virtual void setContactChatState(ChatState::ChatStateType state) = 0;  			virtual void setName(const std::string& name) = 0; @@ -61,9 +62,11 @@ namespace Swift {  			boost::signal<void ()> onClosed;  			boost::signal<void ()> onAllMessagesRead; -			boost::signal<void (const std::string&)> onSendMessageRequest; +			boost::signal<void (const std::string&, bool isCorrection)> onSendMessageRequest; +			boost::signal<void ()> onSendCorrectionMessageRequest;  			boost::signal<void ()> onUserTyping;  			boost::signal<void ()> onUserCancelsTyping; +			boost::signal<void (bool correction)> onSendMessageCorrection;  	};  }  #endif diff --git a/Swift/Controllers/UIInterfaces/MainWindow.h b/Swift/Controllers/UIInterfaces/MainWindow.h index 2fd463b..93584e7 100644 --- a/Swift/Controllers/UIInterfaces/MainWindow.h +++ b/Swift/Controllers/UIInterfaces/MainWindow.h @@ -9,6 +9,7 @@  #include <string>  #include "Swiften/JID/JID.h"  #include "Swiften/Elements/StatusShow.h" +#include "Swiften/Elements/DiscoItems.h"  #include "Swiften/Base/boost_bsignals.h"  #include <boost/shared_ptr.hpp> @@ -33,6 +34,7 @@ namespace Swift {  			/** Must be able to cope with NULL to clear the roster */  			virtual void setRosterModel(Roster* roster) = 0;  			virtual void setConnecting() = 0; +			virtual void setAvailableAdHocCommands(const std::vector<DiscoItems::Item>& commands) = 0;  			boost::signal<void (StatusShow::Type, const std::string&)> onChangeStatusRequest;  			boost::signal<void ()> onSignOutRequest; diff --git a/Swift/Controllers/UIInterfaces/UIFactory.h b/Swift/Controllers/UIInterfaces/UIFactory.h index 9b36ac5..57f55d0 100644 --- a/Swift/Controllers/UIInterfaces/UIFactory.h +++ b/Swift/Controllers/UIInterfaces/UIFactory.h @@ -17,6 +17,7 @@  #include <Swift/Controllers/UIInterfaces/XMLConsoleWidgetFactory.h>  #include <Swift/Controllers/UIInterfaces/ProfileWindowFactory.h>  #include <Swift/Controllers/UIInterfaces/ContactEditWindowFactory.h> +#include <Swift/Controllers/UIInterfaces/AdHocCommandWindowFactory.h>  namespace Swift {  	class UIFactory :  @@ -30,7 +31,8 @@ namespace Swift {  			public UserSearchWindowFactory,   			public JoinMUCWindowFactory,  			public ProfileWindowFactory, -			public ContactEditWindowFactory { +			public ContactEditWindowFactory, +			public AdHocCommandWindowFactory {  		public:  			virtual ~UIFactory() {}  	}; diff --git a/Swift/Controllers/UnitTest/ChatMessageSummarizerTest.cpp b/Swift/Controllers/UnitTest/ChatMessageSummarizerTest.cpp new file mode 100644 index 0000000..ee0ee9f --- /dev/null +++ b/Swift/Controllers/UnitTest/ChatMessageSummarizerTest.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2011 Kevin Smith + * 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 "Swift/Controllers/ChatMessageSummarizer.h" + +using namespace Swift; +using namespace std; + +class ChatMessageSummarizerTest : public CppUnit::TestFixture { +	CPPUNIT_TEST_SUITE(ChatMessageSummarizerTest); +	CPPUNIT_TEST(testEmpty); +	CPPUNIT_TEST(testCurrentNone); +	CPPUNIT_TEST(testCurrentCount); +	CPPUNIT_TEST(testCurrentCountOthersNone); +	CPPUNIT_TEST(testCurrentCountOtherCount); +	CPPUNIT_TEST(testCurrentNoneOtherCount); +	CPPUNIT_TEST(testCurrentCountOthersCount); +	CPPUNIT_TEST(testCurrentNoneOthersCount); +	CPPUNIT_TEST(testCurrentCountSomeOthersCount); +	CPPUNIT_TEST_SUITE_END(); + +public: +	ChatMessageSummarizerTest() {}; + +	void setUp() { + +	} + +	void testEmpty() { +		string current(""); +		vector<UnreadPair> unreads; +		ChatMessageSummarizer summary; +		CPPUNIT_ASSERT_EQUAL(current, summary.getSummary(current, unreads)); +	} + +	void testCurrentNone() { +		string current("Bob"); +		vector<UnreadPair> unreads; +		unreads.push_back(UnreadPair("Bob", 0)); +		ChatMessageSummarizer summary; +		CPPUNIT_ASSERT_EQUAL(current, summary.getSummary(current, unreads)); +	} + +	void testCurrentCount() { +		string current("Bob"); +		vector<UnreadPair> unreads; +		unreads.push_back(UnreadPair("Bob", 3)); +		ChatMessageSummarizer summary; +		CPPUNIT_ASSERT_EQUAL(string("Bob (3)"), summary.getSummary(current, unreads)); +	} + +	void testCurrentCountOthersNone() { +		string current("Bob"); +		vector<UnreadPair> unreads; +		unreads.push_back(UnreadPair("Bert", 0)); +		unreads.push_back(UnreadPair("Bob", 3)); +		unreads.push_back(UnreadPair("Betty", 0)); +		ChatMessageSummarizer summary; +		CPPUNIT_ASSERT_EQUAL(string("Bob (3)"), summary.getSummary(current, unreads)); +	} + +	void testCurrentCountOtherCount() { +		string current("Bob"); +		vector<UnreadPair> unreads; +		unreads.push_back(UnreadPair("Bert", 0)); +		unreads.push_back(UnreadPair("Bob", 3)); +		unreads.push_back(UnreadPair("Betty", 7)); +		ChatMessageSummarizer summary; +		CPPUNIT_ASSERT_EQUAL(string("Bob (3), Betty (7)"), summary.getSummary(current, unreads)); +	} + +	void testCurrentNoneOtherCount() { +		string current("Bob"); +		vector<UnreadPair> unreads; +		unreads.push_back(UnreadPair("Bert", 0)); +		unreads.push_back(UnreadPair("Bob", 0)); +		unreads.push_back(UnreadPair("Betty", 7)); +		ChatMessageSummarizer summary; +		CPPUNIT_ASSERT_EQUAL(string("Bob, Betty (7)"), summary.getSummary(current, unreads)); +	} + +	void testCurrentNoneOthersCount() { +		string current("Bob"); +		vector<UnreadPair> unreads; +		unreads.push_back(UnreadPair("Bert", 2)); +		unreads.push_back(UnreadPair("Bob", 0)); +		unreads.push_back(UnreadPair("Betty", 7)); +		ChatMessageSummarizer summary; +		CPPUNIT_ASSERT_EQUAL(string("Bob and 2 others (9)"), summary.getSummary(current, unreads)); +	} + +	void testCurrentCountOthersCount() { +		string current("Bob"); +		vector<UnreadPair> unreads; +		unreads.push_back(UnreadPair("Bert", 2)); +		unreads.push_back(UnreadPair("Bob", 11)); +		unreads.push_back(UnreadPair("Betty", 7)); +		ChatMessageSummarizer summary; +		CPPUNIT_ASSERT_EQUAL(string("Bob (11) and 2 others (9)"), summary.getSummary(current, unreads)); +	} + +	void testCurrentCountSomeOthersCount() { +		string current("Bob"); +		vector<UnreadPair> unreads; +		unreads.push_back(UnreadPair("Bert", 2)); +		unreads.push_back(UnreadPair("Beverly", 0)); +		unreads.push_back(UnreadPair("Bob", 11)); +		unreads.push_back(UnreadPair("Beatrice", 0)); +		unreads.push_back(UnreadPair("Betty", 7)); +		ChatMessageSummarizer summary; +		CPPUNIT_ASSERT_EQUAL(string("Bob (11) and 2 others (9)"), summary.getSummary(current, unreads)); +	} + +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(ChatMessageSummarizerTest); diff --git a/Swift/Controllers/UnitTest/MockChatWindow.h b/Swift/Controllers/UnitTest/MockChatWindow.h index 53a90a7..82c5b52 100644 --- a/Swift/Controllers/UnitTest/MockChatWindow.h +++ b/Swift/Controllers/UnitTest/MockChatWindow.h @@ -34,12 +34,13 @@ namespace Swift {  			virtual void setRosterModel(Roster* /*roster*/) {};  			virtual void setTabComplete(TabComplete*) {};  			virtual void replaceLastMessage(const std::string&) {}; +			virtual void replaceMessage(const std::string&, const std::string&, const boost::posix_time::ptime&) {};  			void setAckState(const std::string& /*id*/, AckState /*state*/) {};  			virtual void flash() {};  			boost::signal<void ()> onClosed;  			boost::signal<void ()> onAllMessagesRead; -			boost::signal<void (const std::string&)> onSendMessageRequest; +			boost::signal<void (const std::string&, bool isCorrection)> onSendMessageRequest;  			std::string name_;  			std::string lastMessageBody_; diff --git a/Swift/Controllers/UnitTest/MockMainWindow.h b/Swift/Controllers/UnitTest/MockMainWindow.h index afa5c2a..f773062 100644 --- a/Swift/Controllers/UnitTest/MockMainWindow.h +++ b/Swift/Controllers/UnitTest/MockMainWindow.h @@ -20,6 +20,7 @@ namespace Swift {  			virtual void setMyAvatarPath(const std::string& /*path*/) {};  			virtual void setMyStatusText(const std::string& /*status*/) {};  			virtual void setMyStatusType(StatusShow::Type /*type*/) {}; +			virtual void setAvailableAdHocCommands(const std::vector<DiscoItems::Item>& /*commands*/) {};  			virtual void setConnecting() {};  			Roster* roster; diff --git a/Swift/Controllers/XMPPEvents/EventController.cpp b/Swift/Controllers/XMPPEvents/EventController.cpp index 7f8f216..98fd634 100644 --- a/Swift/Controllers/XMPPEvents/EventController.cpp +++ b/Swift/Controllers/XMPPEvents/EventController.cpp @@ -9,6 +9,7 @@  #include <boost/bind.hpp>  #include <algorithm> +#include <Swiften/Base/foreach.h>  #include "Swift/Controllers/XMPPEvents/MessageEvent.h"  #include "Swift/Controllers/XMPPEvents/ErrorEvent.h"  #include "Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h" diff --git a/Swift/Controllers/XMPPURIController.cpp b/Swift/Controllers/XMPPURIController.cpp new file mode 100644 index 0000000..00759b8 --- /dev/null +++ b/Swift/Controllers/XMPPURIController.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swift/Controllers/XMPPURIController.h> + +#include <boost/bind.hpp> +#include <boost/smart_ptr/make_shared.hpp> + +#include <SwifTools/URIHandler/URIHandler.h> +#include <SwifTools/URIHandler/XMPPURI.h> +#include <Swift/Controllers/UIEvents/UIEventStream.h> +#include <Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h> +#include <Swift/Controllers/UIEvents/RequestChatUIEvent.h> + +using namespace Swift; + +XMPPURIController::XMPPURIController(URIHandler* uriHandler, UIEventStream* uiEventStream) : uriHandler(uriHandler), uiEventStream(uiEventStream) { +	uriHandler->onURI.connect(boost::bind(&XMPPURIController::handleURI, this, _1)); +} + +XMPPURIController::~XMPPURIController() { +	uriHandler->onURI.disconnect(boost::bind(&XMPPURIController::handleURI, this, _1)); +} + +void XMPPURIController::handleURI(const std::string& s) { +	XMPPURI uri = XMPPURI::fromString(s); +	if (!uri.isNull()) { +		if (uri.getQueryType() == "join") { +			uiEventStream->send(boost::make_shared<RequestJoinMUCUIEvent>(uri.getPath())); +		} +		else { +			uiEventStream->send(boost::make_shared<RequestChatUIEvent>(uri.getPath())); +		} +	} +} diff --git a/Swift/Controllers/XMPPURIController.h b/Swift/Controllers/XMPPURIController.h new file mode 100644 index 0000000..54534d4 --- /dev/null +++ b/Swift/Controllers/XMPPURIController.h @@ -0,0 +1,32 @@ +/* + * 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 <string> +#include <Swiften/Base/boost_bsignals.h> + +namespace Swift { +	class URIHandler; +	class JID; +	class UIEventStream; + +	class XMPPURIController { +		public: +			XMPPURIController(URIHandler* uriHandler, UIEventStream* uiEventStream); +			~XMPPURIController(); + +			boost::signal<void (const JID&)> onStartChat; +			boost::signal<void (const JID&)> onJoinMUC; + +		private: +			void handleURI(const std::string&); + +		private: +			URIHandler* uriHandler; +			UIEventStream* uiEventStream; +	}; +} | 
 Swift
 Swift