summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Swift/Controllers/Chat/ChatsManager.cpp21
-rw-r--r--Swift/Controllers/MainController.cpp5
-rw-r--r--Swift/Controllers/Roster/RosterController.cpp30
-rw-r--r--Swift/Controllers/Roster/RosterController.h6
-rw-r--r--Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp135
-rw-r--r--Swiften/Base/SConscript18
-rw-r--r--Swiften/Disco/FeatureOracle.cpp194
-rw-r--r--Swiften/Disco/FeatureOracle.h16
-rw-r--r--Swiften/Disco/UnitTest/FeatureOracleTest.cpp151
-rw-r--r--Swiften/Elements/Stanza.h5
-rw-r--r--Swiften/FileTransfer/FileTransferManager.cpp15
-rw-r--r--Swiften/FileTransfer/FileTransferManager.h2
-rw-r--r--Swiften/FileTransfer/FileTransferManagerImpl.cpp31
-rw-r--r--Swiften/Presence/PresenceOracle.cpp21
-rw-r--r--Swiften/SConscript3
15 files changed, 484 insertions, 169 deletions
diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp
index 232d903..b9e2cf4 100644
--- a/Swift/Controllers/Chat/ChatsManager.cpp
+++ b/Swift/Controllers/Chat/ChatsManager.cpp
@@ -1,91 +1,93 @@
/*
* Copyright (c) 2010-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swift/Controllers/Chat/ChatsManager.h>
#include <memory>
#include <boost/algorithm/string.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/bind.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/optional.hpp>
#include <boost/serialization/split_free.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/vector.hpp>
#include <Swiften/Avatars/AvatarManager.h>
#include <Swiften/Base/Log.h>
#include <Swiften/Client/ClientBlockListManager.h>
#include <Swiften/Client/NickResolver.h>
#include <Swiften/Client/StanzaChannel.h>
#include <Swiften/Disco/DiscoServiceWalker.h>
+#include <Swiften/Disco/FeatureOracle.h>
#include <Swiften/Elements/CarbonsReceived.h>
#include <Swiften/Elements/CarbonsSent.h>
#include <Swiften/Elements/ChatState.h>
#include <Swiften/Elements/DeliveryReceipt.h>
#include <Swiften/Elements/DeliveryReceiptRequest.h>
#include <Swiften/Elements/Forwarded.h>
#include <Swiften/Elements/MUCInvitationPayload.h>
#include <Swiften/Elements/MUCUserPayload.h>
#include <Swiften/MUC/MUCBookmarkManager.h>
#include <Swiften/MUC/MUCManager.h>
#include <Swiften/Presence/PresenceSender.h>
#include <Swiften/Roster/XMPPRoster.h>
#include <Swiften/StringCodecs/Base64.h>
#include <Swiften/VCards/VCardManager.h>
#include <Swift/Controllers/Chat/AutoAcceptMUCInviteDecider.h>
#include <Swift/Controllers/Chat/ChatController.h>
#include <Swift/Controllers/Chat/ChatControllerBase.h>
#include <Swift/Controllers/Chat/ChatMessageParser.h>
#include <Swift/Controllers/Chat/MUCController.h>
#include <Swift/Controllers/Chat/MUCSearchController.h>
#include <Swift/Controllers/Chat/UserSearchController.h>
#include <Swift/Controllers/FileTransfer/FileTransferController.h>
#include <Swift/Controllers/FileTransfer/FileTransferOverview.h>
#include <Swift/Controllers/ProfileSettingsProvider.h>
#include <Swift/Controllers/SettingConstants.h>
#include <Swift/Controllers/Settings/SettingsProvider.h>
#include <Swift/Controllers/UIEvents/AddMUCBookmarkUIEvent.h>
#include <Swift/Controllers/UIEvents/CreateImpromptuMUCUIEvent.h>
#include <Swift/Controllers/UIEvents/EditMUCBookmarkUIEvent.h>
#include <Swift/Controllers/UIEvents/InviteToMUCUIEvent.h>
#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h>
#include <Swift/Controllers/UIEvents/RemoveMUCBookmarkUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestChatUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h>
+#include <Swift/Controllers/UIEvents/SendFileUIEvent.h>
#include <Swift/Controllers/UIInterfaces/ChatListWindowFactory.h>
#include <Swift/Controllers/UIInterfaces/JoinMUCWindow.h>
#include <Swift/Controllers/UIInterfaces/JoinMUCWindowFactory.h>
#include <Swift/Controllers/WhiteboardManager.h>
#include <Swift/Controllers/XMPPEvents/EventController.h>
BOOST_CLASS_VERSION(Swift::ChatListWindow::Chat, 1)
namespace boost {
namespace serialization {
template<class Archive> void save(Archive& ar, const Swift::JID& jid, const unsigned int /*version*/) {
std::string jidStr = jid.toString();
ar << jidStr;
}
template<class Archive> void load(Archive& ar, Swift::JID& jid, const unsigned int /*version*/) {
std::string stringJID;
ar >> stringJID;
jid = Swift::JID(stringJID);
}
template<class Archive> inline void serialize(Archive& ar, Swift::JID& t, const unsigned int file_version){
split_free(ar, t, file_version);
}
template<class Archive> void serialize(Archive& ar, Swift::ChatListWindow::Chat& chat, const unsigned int version) {
ar & chat.jid;
ar & chat.chatName;
ar & chat.activity;
ar & chat.isMUC;
@@ -172,60 +174,61 @@ ChatsManager::ChatsManager(
joinMUCWindow_ = nullptr;
mucSearchController_ = new MUCSearchController(jid_, mucSearchWindowFactory, iqRouter, profileSettings_);
mucSearchController_->onMUCSelected.connect(boost::bind(&ChatsManager::handleMUCSelectedAfterSearch, this, _1));
ftOverview_->onNewFileTransferController.connect(boost::bind(&ChatsManager::handleNewFileTransferController, this, _1));
whiteboardManager_->onSessionRequest.connect(boost::bind(&ChatsManager::handleWhiteboardSessionRequest, this, _1, _2));
whiteboardManager_->onRequestAccepted.connect(boost::bind(&ChatsManager::handleWhiteboardStateChange, this, _1, ChatWindow::WhiteboardAccepted));
whiteboardManager_->onSessionTerminate.connect(boost::bind(&ChatsManager::handleWhiteboardStateChange, this, _1, ChatWindow::WhiteboardTerminated));
whiteboardManager_->onRequestRejected.connect(boost::bind(&ChatsManager::handleWhiteboardStateChange, this, _1, ChatWindow::WhiteboardRejected));
roster_->onJIDAdded.connect(boost::bind(&ChatsManager::handleJIDAddedToRoster, this, _1));
roster_->onJIDRemoved.connect(boost::bind(&ChatsManager::handleJIDRemovedFromRoster, this, _1));
roster_->onJIDUpdated.connect(boost::bind(&ChatsManager::handleJIDUpdatedInRoster, this, _1));
roster_->onRosterCleared.connect(boost::bind(&ChatsManager::handleRosterCleared, this));
settings_->onSettingChanged.connect(boost::bind(&ChatsManager::handleSettingChanged, this, _1));
userWantsReceipts_ = settings_->getSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS);
setupBookmarks();
loadRecents();
autoAcceptMUCInviteDecider_ = new AutoAcceptMUCInviteDecider(jid.getDomain(), roster_, settings_);
}
ChatsManager::~ChatsManager() {
settings_->onSettingChanged.disconnect(boost::bind(&ChatsManager::handleSettingChanged, this, _1));
roster_->onJIDAdded.disconnect(boost::bind(&ChatsManager::handleJIDAddedToRoster, this, _1));
roster_->onJIDRemoved.disconnect(boost::bind(&ChatsManager::handleJIDRemovedFromRoster, this, _1));
roster_->onJIDUpdated.disconnect(boost::bind(&ChatsManager::handleJIDUpdatedInRoster, this, _1));
roster_->onRosterCleared.disconnect(boost::bind(&ChatsManager::handleRosterCleared, this));
+ ftOverview_->onNewFileTransferController.disconnect(boost::bind(&ChatsManager::handleNewFileTransferController, this, _1));
delete joinMUCWindow_;
for (JIDChatControllerPair controllerPair : chatControllers_) {
delete controllerPair.second;
}
for (JIDMUCControllerPair controllerPair : mucControllers_) {
delete controllerPair.second;
}
delete mucBookmarkManager_;
delete mucSearchController_;
delete autoAcceptMUCInviteDecider_;
}
void ChatsManager::saveRecents() {
std::stringstream serializeStream;
boost::archive::text_oarchive oa(serializeStream);
std::vector<ChatListWindow::Chat> recentsLimited = std::vector<ChatListWindow::Chat>(recentChats_.begin(), recentChats_.end());
if (recentsLimited.size() > 25) {
recentsLimited.erase(recentsLimited.begin() + 25, recentsLimited.end());
}
if (eagleMode_) {
for (ChatListWindow::Chat& chat : recentsLimited) {
chat.activity = "";
}
}
class RemoveRecent {
public:
static bool ifPrivateMessage(const ChatListWindow::Chat& chat) {
return chat.isPrivateMessage;
}
@@ -535,60 +538,78 @@ void ChatsManager::finalizeImpromptuJoin(MUC::ref muc, const std::vector<JID>& j
boost::optional<JID> realJID = occupant.second.getRealJID();
if (realJID) {
missingJIDsToInvite.erase(std::remove(missingJIDsToInvite.begin(), missingJIDsToInvite.end(), realJID->toBare()), missingJIDsToInvite.end());
}
}
if (reuseChatJID) {
muc->invitePerson(reuseChatJID.get(), reason, true, true);
}
for (const JID& jid : missingJIDsToInvite) {
muc->invitePerson(jid, reason, true);
}
}
void ChatsManager::handleUIEvent(std::shared_ptr<UIEvent> event) {
std::shared_ptr<RequestChatUIEvent> chatEvent = std::dynamic_pointer_cast<RequestChatUIEvent>(event);
if (chatEvent) {
handleChatRequest(chatEvent->getContact());
return;
}
std::shared_ptr<RemoveMUCBookmarkUIEvent> removeMUCBookmarkEvent = std::dynamic_pointer_cast<RemoveMUCBookmarkUIEvent>(event);
if (removeMUCBookmarkEvent) {
mucBookmarkManager_->removeBookmark(removeMUCBookmarkEvent->getBookmark());
return;
}
std::shared_ptr<AddMUCBookmarkUIEvent> addMUCBookmarkEvent = std::dynamic_pointer_cast<AddMUCBookmarkUIEvent>(event);
if (addMUCBookmarkEvent) {
mucBookmarkManager_->addBookmark(addMUCBookmarkEvent->getBookmark());
return;
}
+ std::shared_ptr<SendFileUIEvent> sendFileEvent = std::dynamic_pointer_cast<SendFileUIEvent>(event);
+ if (sendFileEvent) {
+ JID fileReceiver = sendFileEvent->getJID();
+ if (fileReceiver.isBare()) {
+ // See if there is a chat controller for a conversation with a bound
+ // full JID. Check if this JID supports file transfer and use it instead
+ // of the bare JID.
+ ChatController* controller = getChatControllerIfExists(fileReceiver, false);
+ if (controller) {
+ JID controllerJID = controller->getToJID();
+ if (!controllerJID.isBare() && (FeatureOracle(entityCapsProvider_, presenceOracle_).isFileTransferSupported(controllerJID) == Yes)) {
+ fileReceiver = controllerJID;
+ }
+ }
+ }
+ ftOverview_->sendFile(fileReceiver, sendFileEvent->getFilename());
+ return;
+ }
std::shared_ptr<CreateImpromptuMUCUIEvent> createImpromptuMUCEvent = std::dynamic_pointer_cast<CreateImpromptuMUCUIEvent>(event);
if (createImpromptuMUCEvent) {
assert(!localMUCServiceJID_.toString().empty());
// create new muc
JID roomJID = createImpromptuMUCEvent->getRoomJID().toString().empty() ? JID(idGenerator_.generateID(), localMUCServiceJID_) : createImpromptuMUCEvent->getRoomJID();
// join muc
MUC::ref muc = handleJoinMUCRequest(roomJID, boost::optional<std::string>(), nickResolver_->jidToNick(jid_), false, true, true);
mucControllers_[roomJID]->onImpromptuConfigCompleted.connect(boost::bind(&ChatsManager::finalizeImpromptuJoin, this, muc, createImpromptuMUCEvent->getJIDs(), createImpromptuMUCEvent->getReason(), boost::optional<JID>()));
mucControllers_[roomJID]->activateChatWindow();
}
std::shared_ptr<EditMUCBookmarkUIEvent> editMUCBookmarkEvent = std::dynamic_pointer_cast<EditMUCBookmarkUIEvent>(event);
if (editMUCBookmarkEvent) {
mucBookmarkManager_->replaceBookmark(editMUCBookmarkEvent->getOldBookmark(), editMUCBookmarkEvent->getNewBookmark());
}
else if (JoinMUCUIEvent::ref joinEvent = std::dynamic_pointer_cast<JoinMUCUIEvent>(event)) {
handleJoinMUCRequest(joinEvent->getJID(), joinEvent->getPassword(), joinEvent->getNick(), joinEvent->getShouldJoinAutomatically(), joinEvent->getCreateAsReservedRoomIfNew(), joinEvent->isImpromptu());
mucControllers_[joinEvent->getJID()]->activateChatWindow();
}
else if (std::shared_ptr<RequestJoinMUCUIEvent> joinEvent = std::dynamic_pointer_cast<RequestJoinMUCUIEvent>(event)) {
if (!joinMUCWindow_) {
joinMUCWindow_ = joinMUCWindowFactory_->createJoinMUCWindow(uiEventStream_);
joinMUCWindow_->onSearchMUC.connect(boost::bind(&ChatsManager::handleSearchMUCRequest, this));
}
joinMUCWindow_->setMUC(joinEvent->getRoom());
joinMUCWindow_->setNick(nickResolver_->jidToNick(jid_));
joinMUCWindow_->show();
}
diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index a9d3f5c..0d9f1b8 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -1,48 +1,47 @@
/*
* Copyright (c) 2010-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swift/Controllers/MainController.h>
#include <cstdlib>
#include <memory>
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <Swiften/Base/Algorithm.h>
#include <Swiften/Base/Log.h>
#include <Swiften/Base/String.h>
-#include <Swiften/Base/foreach.h>
#include <Swiften/Base/format.h>
#include <Swiften/Client/Client.h>
#include <Swiften/Client/ClientBlockListManager.h>
#include <Swiften/Client/ClientXMLTracer.h>
#include <Swiften/Client/NickResolver.h>
#include <Swiften/Client/StanzaChannel.h>
#include <Swiften/Client/Storages.h>
#include <Swiften/Crypto/CryptoProvider.h>
#include <Swiften/Disco/CapsInfoGenerator.h>
#include <Swiften/Disco/ClientDiscoManager.h>
#include <Swiften/Disco/GetDiscoInfoRequest.h>
#include <Swiften/Elements/ChatState.h>
#include <Swiften/Elements/DiscoInfo.h>
#include <Swiften/Elements/Presence.h>
#include <Swiften/Elements/VCardUpdate.h>
#include <Swiften/FileTransfer/FileTransferManager.h>
#include <Swiften/Network/NetworkFactories.h>
#include <Swiften/Network/TimerFactory.h>
#include <Swiften/Presence/PresenceSender.h>
#include <Swiften/Queries/Requests/EnableCarbonsRequest.h>
#include <Swiften/StringCodecs/Base64.h>
#include <Swiften/StringCodecs/Hexify.h>
#include <Swiften/VCards/GetVCardRequest.h>
#include <Swiften/VCards/VCardManager.h>
#ifdef SWIFTEN_PLATFORM_WIN32
#include <Swiften/SASL/WindowsAuthentication.h>
#endif
#include <Swift/Controllers/AdHocManager.h>
@@ -153,61 +152,61 @@ MainController::MainController(
clientInitialized_ = false;
offlineRequested_ = false;
timeBeforeNextReconnect_ = -1;
dock_ = dock;
uiEventStream_ = new UIEventStream();
notifier_ = new TogglableNotifier(notifier);
notifier_->setPersistentEnabled(settings_->getSetting(SettingConstants::SHOW_NOTIFICATIONS));
eventController_ = new EventController();
eventController_->onEventQueueLengthChange.connect(boost::bind(&MainController::handleEventQueueLengthChange, this, _1));
systemTrayController_ = new SystemTrayController(eventController_, systemTray);
loginWindow_ = uiFactory_->createLoginWindow(uiEventStream_);
loginWindow_->setShowNotificationToggle(!notifier->isExternallyConfigured());
highlightManager_ = new HighlightManager(settings_);
highlightEditorController_ = new HighlightEditorController(uiEventStream_, uiFactory_, highlightManager_);
soundEventController_ = new SoundEventController(eventController_, soundPlayer, settings, highlightManager_);
xmppURIController_ = new XMPPURIController(uriHandler_, uiEventStream_);
std::string selectedLoginJID = settings_->getSetting(SettingConstants::LAST_LOGIN_JID);
bool loginAutomatically = settings_->getSetting(SettingConstants::LOGIN_AUTOMATICALLY);
std::string cachedPassword;
std::string cachedCertificate;
ClientOptions cachedOptions;
bool eagle = settings_->getSetting(SettingConstants::FORGET_PASSWORDS);
if (!eagle) {
- foreach (std::string profile, settings->getAvailableProfiles()) {
+ for (auto&& profile : settings->getAvailableProfiles()) {
ProfileSettingsProvider profileSettings(profile, settings);
std::string password = profileSettings.getStringSetting("pass");
std::string certificate = profileSettings.getStringSetting("certificate");
std::string jid = profileSettings.getStringSetting("jid");
ClientOptions clientOptions = parseClientOptions(profileSettings.getStringSetting("options"));
#ifdef SWIFTEN_PLATFORM_WIN32
clientOptions.singleSignOn = settings_->getSetting(SettingConstants::SINGLE_SIGN_ON);
#endif
loginWindow_->addAvailableAccount(jid, password, certificate, clientOptions);
if (jid == selectedLoginJID) {
cachedPassword = password;
cachedCertificate = certificate;
cachedOptions = clientOptions;
}
}
loginWindow_->selectUser(selectedLoginJID);
loginWindow_->setLoginAutomatically(loginAutomatically);
}
loginWindow_->onLoginRequest.connect(boost::bind(&MainController::handleLoginRequest, this, _1, _2, _3, _4, _5, _6, _7));
loginWindow_->onPurgeSavedLoginRequest.connect(boost::bind(&MainController::handlePurgeSavedLoginRequest, this, _1));
loginWindow_->onCancelLoginRequest.connect(boost::bind(&MainController::handleCancelLoginRequest, this));
loginWindow_->onQuitRequest.connect(boost::bind(&MainController::handleQuitRequest, this));
idleDetector_->setIdleTimeSeconds(settings->getSetting(SettingConstants::IDLE_TIMEOUT));
idleDetector_->onIdleChanged.connect(boost::bind(&MainController::handleInputIdleChanged, this, _1));
@@ -322,61 +321,61 @@ void MainController::resetPendingReconnects() {
if (reconnectTimer_) {
reconnectTimer_->stop();
reconnectTimer_.reset();
}
resetCurrentError();
}
void MainController::resetCurrentError() {
if (lastDisconnectError_) {
lastDisconnectError_->conclude();
lastDisconnectError_ = std::shared_ptr<ErrorEvent>();
}
}
void MainController::handleConnected() {
boundJID_ = client_->getJID();
resetCurrentError();
resetPendingReconnects();
if (settings_->getSetting(SettingConstants::FORGET_PASSWORDS)) {
purgeCachedCredentials();
}
bool freshLogin = rosterController_ == nullptr;
myStatusLooksOnline_ = true;
if (freshLogin) {
profileController_ = new ProfileController(client_->getVCardManager(), uiFactory_, uiEventStream_);
showProfileController_ = new ShowProfileController(client_->getVCardManager(), uiFactory_, uiEventStream_);
ftOverview_ = new FileTransferOverview(client_->getFileTransferManager());
fileTransferListController_->setFileTransferOverview(ftOverview_);
- rosterController_ = new RosterController(boundJID_, client_->getRoster(), client_->getAvatarManager(), uiFactory_, client_->getNickManager(), client_->getNickResolver(), client_->getPresenceOracle(), client_->getSubscriptionManager(), eventController_, uiEventStream_, client_->getIQRouter(), settings_, client_->getEntityCapsProvider(), ftOverview_, client_->getClientBlockListManager(), client_->getVCardManager());
+ rosterController_ = new RosterController(boundJID_, client_->getRoster(), client_->getAvatarManager(), uiFactory_, client_->getNickManager(), client_->getNickResolver(), client_->getPresenceOracle(), client_->getSubscriptionManager(), eventController_, uiEventStream_, client_->getIQRouter(), settings_, client_->getEntityCapsProvider(), client_->getClientBlockListManager(), client_->getVCardManager());
rosterController_->onChangeStatusRequest.connect(boost::bind(&MainController::handleChangeStatusRequest, this, _1, _2));
rosterController_->onSignOutRequest.connect(boost::bind(&MainController::signOut, this));
rosterController_->getWindow()->onShowCertificateRequest.connect(boost::bind(&MainController::handleShowCertificateRequest, this));
blockListController_ = new BlockListController(client_->getClientBlockListManager(), uiEventStream_, uiFactory_, eventController_);
contactEditController_ = new ContactEditController(rosterController_, client_->getVCardManager(), uiFactory_, uiEventStream_);
whiteboardManager_ = new WhiteboardManager(uiFactory_, uiEventStream_, client_->getNickResolver(), client_->getWhiteboardSessionManager());
/* Doing this early as an ordering fix. Various things later will
* want to have the user's nick available and this means it will
* be before they receive stanzas that need it (e.g. bookmarks).*/
client_->getVCardManager()->requestOwnVCard();
contactSuggesterWithoutRoster_ = new ContactSuggester();
contactSuggesterWithRoster_ = new ContactSuggester();
userSearchControllerInvite_ = new UserSearchController(UserSearchController::InviteToChat, jid_, uiEventStream_, client_->getVCardManager(), uiFactory_, client_->getIQRouter(), rosterController_, contactSuggesterWithRoster_, client_->getAvatarManager(), client_->getPresenceOracle(), profileSettings_);
#ifdef SWIFT_EXPERIMENTAL_HISTORY
historyController_ = new HistoryController(storages_->getHistoryStorage());
historyViewController_ = new HistoryViewController(jid_, uiEventStream_, historyController_, client_->getNickResolver(), client_->getAvatarManager(), client_->getPresenceOracle(), uiFactory_);
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_, ftOverview_, client_->getRoster(), !settings_->getSetting(SettingConstants::REMEMBER_RECENT_CHATS), settings_, historyController_, whiteboardManager_, highlightManager_, client_->getClientBlockListManager(), emoticons_, client_->getVCardManager());
#else
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_, ftOverview_, client_->getRoster(), !settings_->getSetting(SettingConstants::REMEMBER_RECENT_CHATS), settings_, nullptr, whiteboardManager_, highlightManager_, client_->getClientBlockListManager(), emoticons_, client_->getVCardManager());
#endif
contactsFromRosterProvider_ = new ContactsFromXMPPRoster(client_->getRoster(), client_->getAvatarManager(), client_->getPresenceOracle());
contactSuggesterWithoutRoster_->addContactProvider(chatsManager_);
contactSuggesterWithRoster_->addContactProvider(chatsManager_);
contactSuggesterWithRoster_->addContactProvider(contactsFromRosterProvider_);
highlightEditorController_->setContactSuggester(contactSuggesterWithoutRoster_);
diff --git a/Swift/Controllers/Roster/RosterController.cpp b/Swift/Controllers/Roster/RosterController.cpp
index 116ef2e..1d20c4a 100644
--- a/Swift/Controllers/Roster/RosterController.cpp
+++ b/Swift/Controllers/Roster/RosterController.cpp
@@ -1,113 +1,113 @@
/*
* Copyright (c) 2010-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swift/Controllers/Roster/RosterController.h>
#include <memory>
#include <boost/bind.hpp>
#include <Swiften/Avatars/AvatarManager.h>
#include <Swiften/Base/Path.h>
#include <Swiften/Base/format.h>
#include <Swiften/Client/ClientBlockListManager.h>
#include <Swiften/Client/NickManager.h>
#include <Swiften/Client/NickResolver.h>
#include <Swiften/Disco/EntityCapsManager.h>
+#include <Swiften/Disco/FeatureOracle.h>
#include <Swiften/Elements/DiscoInfo.h>
#include <Swiften/FileTransfer/FileTransferManager.h>
#include <Swiften/JID/JID.h>
#include <Swiften/Jingle/JingleSessionManager.h>
#include <Swiften/Presence/PresenceOracle.h>
#include <Swiften/Presence/SubscriptionManager.h>
#include <Swiften/Queries/IQRouter.h>
#include <Swiften/Roster/GetRosterRequest.h>
#include <Swiften/Roster/SetRosterRequest.h>
#include <Swiften/Roster/XMPPRoster.h>
#include <Swiften/Roster/XMPPRosterItem.h>
#include <Swiften/VCards/VCardManager.h>
-#include <Swift/Controllers/FileTransfer/FileTransferOverview.h>
#include <Swift/Controllers/Intl.h>
#include <Swift/Controllers/Roster/GroupRosterItem.h>
#include <Swift/Controllers/Roster/ItemOperations/AppearOffline.h>
#include <Swift/Controllers/Roster/ItemOperations/SetAvailableFeatures.h>
#include <Swift/Controllers/Roster/ItemOperations/SetAvatar.h>
#include <Swift/Controllers/Roster/ItemOperations/SetBlockingState.h>
#include <Swift/Controllers/Roster/ItemOperations/SetName.h>
#include <Swift/Controllers/Roster/ItemOperations/SetPresence.h>
#include <Swift/Controllers/Roster/ItemOperations/SetVCard.h>
#include <Swift/Controllers/Roster/OfflineRosterFilter.h>
#include <Swift/Controllers/Roster/Roster.h>
#include <Swift/Controllers/Roster/RosterGroupExpandinessPersister.h>
#include <Swift/Controllers/Roster/RosterVCardProvider.h>
#include <Swift/Controllers/SettingConstants.h>
#include <Swift/Controllers/UIEvents/AddContactUIEvent.h>
#include <Swift/Controllers/UIEvents/RemoveRosterItemUIEvent.h>
#include <Swift/Controllers/UIEvents/RenameGroupUIEvent.h>
#include <Swift/Controllers/UIEvents/RenameRosterItemUIEvent.h>
-#include <Swift/Controllers/UIEvents/SendFileUIEvent.h>
#include <Swift/Controllers/UIInterfaces/MainWindow.h>
#include <Swift/Controllers/UIInterfaces/MainWindowFactory.h>
#include <Swift/Controllers/XMPPEvents/ErrorEvent.h>
#include <Swift/Controllers/XMPPEvents/EventController.h>
#include <Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h>
namespace Swift {
/**
* The controller does not gain ownership of these parameters.
*/
-RosterController::RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickManager* nickManager, NickResolver* nickResolver, PresenceOracle* presenceOracle, SubscriptionManager* subscriptionManager, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter, SettingsProvider* settings, EntityCapsProvider* entityCapsManager, FileTransferOverview* fileTransferOverview, ClientBlockListManager* clientBlockListManager, VCardManager* vcardManager)
- : myJID_(jid), xmppRoster_(xmppRoster), mainWindowFactory_(mainWindowFactory), mainWindow_(mainWindowFactory_->createMainWindow(uiEventStream)), roster_(new Roster()), offlineFilter_(new OfflineRosterFilter()), vcardManager_(vcardManager), avatarManager_(avatarManager), nickManager_(nickManager), nickResolver_(nickResolver), presenceOracle_(presenceOracle), uiEventStream_(uiEventStream), entityCapsManager_(entityCapsManager), ftOverview_(fileTransferOverview), clientBlockListManager_(clientBlockListManager) {
- assert(fileTransferOverview);
+RosterController::RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickManager* nickManager, NickResolver* nickResolver, PresenceOracle* presenceOracle, SubscriptionManager* subscriptionManager, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter, SettingsProvider* settings, EntityCapsProvider* entityCapsManager, ClientBlockListManager* clientBlockListManager, VCardManager* vcardManager)
+ : myJID_(jid), xmppRoster_(xmppRoster), mainWindowFactory_(mainWindowFactory), mainWindow_(mainWindowFactory_->createMainWindow(uiEventStream)), roster_(new Roster()), offlineFilter_(new OfflineRosterFilter()), vcardManager_(vcardManager), avatarManager_(avatarManager), nickManager_(nickManager), nickResolver_(nickResolver), presenceOracle_(presenceOracle), uiEventStream_(uiEventStream), entityCapsManager_(entityCapsManager), clientBlockListManager_(clientBlockListManager) {
iqRouter_ = iqRouter;
subscriptionManager_ = subscriptionManager;
eventController_ = eventController;
settings_ = settings;
expandiness_ = new RosterGroupExpandinessPersister(roster_, settings);
mainWindow_->setRosterModel(roster_);
rosterVCardProvider_ = new RosterVCardProvider(roster_, vcardManager, JID::WithoutResource);
changeStatusConnection_ = mainWindow_->onChangeStatusRequest.connect(boost::bind(&RosterController::handleChangeStatusRequest, this, _1, _2));
signOutConnection_ = mainWindow_->onSignOutRequest.connect(boost::bind(boost::ref(onSignOutRequest)));
xmppRoster_->onJIDAdded.connect(boost::bind(&RosterController::handleOnJIDAdded, this, _1));
xmppRoster_->onJIDUpdated.connect(boost::bind(&RosterController::handleOnJIDUpdated, this, _1, _2, _3));
xmppRoster_->onJIDRemoved.connect(boost::bind(&RosterController::handleOnJIDRemoved, this, _1));
xmppRoster_->onRosterCleared.connect(boost::bind(&RosterController::handleRosterCleared, this));
subscriptionManager_->onPresenceSubscriptionRequest.connect(boost::bind(&RosterController::handleSubscriptionRequest, this, _1, _2));
uiEventConnection_ = uiEventStream->onUIEvent.connect(boost::bind(&RosterController::handleUIEvent, this, _1));
+ featureOracle_ = std::unique_ptr<FeatureOracle>(new FeatureOracle(entityCapsManager_, presenceOracle_));
+
vcardManager_->onOwnVCardChanged.connect(boost::bind(&RosterController::handleOwnVCardChanged, this, _1));
avatarManager_->onAvatarChanged.connect(boost::bind(&RosterController::handleAvatarChanged, this, _1));
presenceOracle_->onPresenceChange.connect(boost::bind(&RosterController::handlePresenceChanged, this, _1));
mainWindow_->setMyAvatarPath(pathToString(avatarManager_->getAvatarPath(myJID_.toBare())));
nickManager_->onOwnNickChanged.connect(boost::bind(&MainWindow::setMyNick, mainWindow_, _1));
mainWindow_->setMyJID(jid);
mainWindow_->setMyNick(nickManager_->getOwnNick());
entityCapsManager_->onCapsChanged.connect(boost::bind(&RosterController::handleOnCapsChanged, this, _1));
settings_->onSettingChanged.connect(boost::bind(&RosterController::handleSettingChanged, this, _1));
handleShowOfflineToggled(settings_->getSetting(SettingConstants::SHOW_OFFLINE));
ownContact_ = std::make_shared<ContactRosterItem>(myJID_.toBare(), myJID_.toBare(), nickManager_->getOwnNick(), static_cast<GroupRosterItem*>(nullptr));
ownContact_->setVCard(vcardManager_->getVCard(myJID_.toBare()));
ownContact_->setAvatarPath(pathToString(avatarManager_->getAvatarPath(myJID_.toBare())));
mainWindow_->setMyContactRosterItem(ownContact_);
}
RosterController::~RosterController() {
settings_->onSettingChanged.disconnect(boost::bind(&RosterController::handleSettingChanged, this, _1));
nickManager_->onOwnNickChanged.disconnect(boost::bind(&MainWindow::setMyNick, mainWindow_, _1));
delete offlineFilter_;
delete expandiness_;
mainWindow_->setRosterModel(nullptr);
@@ -240,63 +240,60 @@ void RosterController::handleUIEvent(std::shared_ptr<UIEvent> event) {
}
else if (std::shared_ptr<RenameRosterItemUIEvent> renameEvent = std::dynamic_pointer_cast<RenameRosterItemUIEvent>(event)) {
JID contact(renameEvent->getJID());
RosterItemPayload item(contact, renameEvent->getNewName(), xmppRoster_->getSubscriptionStateForJID(contact));
item.setGroups(xmppRoster_->getGroupsForJID(contact));
std::shared_ptr<RosterPayload> roster(new RosterPayload());
roster->addItem(item);
SetRosterRequest::ref request = SetRosterRequest::create(roster, iqRouter_);
request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
request->send();
}
else if (std::shared_ptr<RenameGroupUIEvent> renameGroupEvent = std::dynamic_pointer_cast<RenameGroupUIEvent>(event)) {
std::vector<XMPPRosterItem> items = xmppRoster_->getItems();
std::string group = renameGroupEvent->getGroup();
// FIXME: We should handle contacts groups specially to avoid clashes
if (group == QT_TRANSLATE_NOOP("", "Contacts")) {
group = "";
}
for (auto& item : items) {
std::vector<std::string> groups = item.getGroups();
if ( (group.empty() && groups.empty()) || std::find(groups.begin(), groups.end(), group) != groups.end()) {
groups.erase(std::remove(groups.begin(), groups.end(), group), groups.end());
if (std::find(groups.begin(), groups.end(), renameGroupEvent->getNewName()) == groups.end()) {
groups.push_back(renameGroupEvent->getNewName());
}
item.setGroups(groups);
updateItem(item);
}
}
}
- else if (std::shared_ptr<SendFileUIEvent> sendFileEvent = std::dynamic_pointer_cast<SendFileUIEvent>(event)) {
- ftOverview_->sendFile(sendFileEvent->getJID(), sendFileEvent->getFilename());
- }
}
void RosterController::setContactGroups(const JID& jid, const std::vector<std::string>& groups) {
updateItem(XMPPRosterItem(jid, xmppRoster_->getNameForJID(jid), groups, xmppRoster_->getSubscriptionStateForJID(jid)));
}
void RosterController::updateItem(const XMPPRosterItem& item) {
RosterItemPayload itemPayload(item.getJID(), item.getName(), item.getSubscription());
itemPayload.setGroups(item.getGroups());
RosterPayload::ref roster = std::make_shared<RosterPayload>();
roster->addItem(itemPayload);
SetRosterRequest::ref request = SetRosterRequest::create(roster, iqRouter_);
request->onResponse.connect(boost::bind(&RosterController::handleRosterItemUpdated, this, _1, roster));
request->send();
}
void RosterController::initBlockingCommand() {
std::shared_ptr<BlockList> blockList = clientBlockListManager_->requestBlockList();
blockingOnStateChangedConnection_ = blockList->onStateChanged.connect(boost::bind(&RosterController::handleBlockingStateChanged, this));
blockingOnItemAddedConnection_ = blockList->onItemAdded.connect(boost::bind(&RosterController::handleBlockingItemAdded, this, _1));
blockingOnItemRemovedConnection_ = blockList->onItemRemoved.connect(boost::bind(&RosterController::handleBlockingItemRemoved, this, _1));
roster_->setBlockingSupported(true);
if (blockList->getState() == BlockList::Available) {
for (const auto& jid : blockList->getItems()) {
roster_->applyOnItems(SetBlockingState(jid, ContactRosterItem::IsBlocked));
}
}
@@ -369,44 +366,41 @@ void RosterController::handleOwnVCardChanged(VCard::ref vcard) {
mainWindow_->setMyContactRosterItem(ownContact_);
}
void RosterController::handleAvatarChanged(const JID& jid) {
boost::filesystem::path path = avatarManager_->getAvatarPath(jid);
roster_->applyOnItems(SetAvatar(jid, path));
if (jid.equals(myJID_, JID::WithoutResource)) {
mainWindow_->setMyAvatarPath(pathToString(path));
ownContact_->setAvatarPath(pathToString(path));
mainWindow_->setMyContactRosterItem(ownContact_);
}
}
void RosterController::handlePresenceChanged(Presence::ref presence) {
if (presence->getFrom().equals(myJID_, JID::WithResource)) {
ownContact_->applyPresence(presence);
mainWindow_->setMyContactRosterItem(ownContact_);
}
handleIncomingPresence(presence);
}
boost::optional<XMPPRosterItem> RosterController::getItem(const JID& jid) const {
return xmppRoster_->getItem(jid);
}
std::set<std::string> RosterController::getGroups() const {
return xmppRoster_->getGroups();
}
void RosterController::handleOnCapsChanged(const JID& jid) {
- DiscoInfo::ref info = entityCapsManager_->getCaps(jid);
- if (info) {
- std::set<ContactRosterItem::Feature> features;
- if (FileTransferManager::isSupportedBy(info)) {
- features.insert(ContactRosterItem::FileTransferFeature);
- }
- if (info->hasFeature(DiscoInfo::WhiteboardFeature)) {
- features.insert(ContactRosterItem::WhiteboardFeature);
- }
- roster_->applyOnItems(SetAvailableFeatures(jid, features));
+ std::set<ContactRosterItem::Feature> features;
+ if (featureOracle_->isFileTransferSupported(jid.toBare()) == Tristate::Yes || featureOracle_->isFileTransferSupported(jid.toBare()) == Tristate::Maybe) {
+ features.insert(ContactRosterItem::FileTransferFeature);
+ }
+ if (featureOracle_->isWhiteboardSupported(jid.toBare()) == Tristate::Yes) {
+ features.insert(ContactRosterItem::WhiteboardFeature);
}
+ roster_->applyOnItems(SetAvailableFeatures(jid, features));
}
}
diff --git a/Swift/Controllers/Roster/RosterController.h b/Swift/Controllers/Roster/RosterController.h
index 980c545..ca2ecdc 100644
--- a/Swift/Controllers/Roster/RosterController.h
+++ b/Swift/Controllers/Roster/RosterController.h
@@ -1,82 +1,82 @@
/*
* Copyright (c) 2010-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
#include <memory>
#include <set>
#include <string>
#include <boost/signals2.hpp>
#include <Swiften/Avatars/AvatarManager.h>
#include <Swiften/Elements/ErrorPayload.h>
#include <Swiften/Elements/Presence.h>
#include <Swiften/Elements/RosterPayload.h>
#include <Swiften/Elements/VCard.h>
#include <Swiften/JID/JID.h>
#include <Swift/Controllers/Roster/ContactRosterItem.h>
#include <Swift/Controllers/UIEvents/UIEvent.h>
namespace Swift {
class AvatarManager;
class ClientBlockListManager;
class EntityCapsProvider;
class EventController;
+ class FeatureOracle;
class FileTransferManager;
- class FileTransferOverview;
class IQRouter;
class MainWindow;
class MainWindowFactory;
class NickManager;
class NickResolver;
class OfflineRosterFilter;
class PresenceOracle;
class Roster;
class RosterGroupExpandinessPersister;
class RosterVCardProvider;
class SettingsProvider;
class SubscriptionManager;
class SubscriptionRequestEvent;
class UIEventStream;
class VCardManager;
class XMPPRoster;
class XMPPRosterItem;
class RosterController {
public:
- RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickManager* nickManager, NickResolver* nickResolver, PresenceOracle* presenceOracle, SubscriptionManager* subscriptionManager, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter, SettingsProvider* settings, EntityCapsProvider* entityCapsProvider, FileTransferOverview* fileTransferOverview, ClientBlockListManager* clientBlockListManager, VCardManager* vcardManager);
+ RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickManager* nickManager, NickResolver* nickResolver, PresenceOracle* presenceOracle, SubscriptionManager* subscriptionManager, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter, SettingsProvider* settings, EntityCapsProvider* entityCapsProvider, ClientBlockListManager* clientBlockListManager, VCardManager* vcardManager);
~RosterController();
void showRosterWindow();
void setJID(const JID& jid) { myJID_ = jid; }
MainWindow* getWindow() {return mainWindow_;}
boost::signals2::signal<void (StatusShow::Type, const std::string&)> onChangeStatusRequest;
boost::signals2::signal<void ()> onSignOutRequest;
void handleOwnVCardChanged(VCard::ref vcard);
void handleAvatarChanged(const JID& jid);
void handlePresenceChanged(Presence::ref presence);
void setEnabled(bool enabled);
boost::optional<XMPPRosterItem> getItem(const JID&) const;
std::set<std::string> getGroups() const;
void setContactGroups(const JID& jid, const std::vector<std::string>& groups);
void updateItem(const XMPPRosterItem&);
void initBlockingCommand();
private:
void handleOnJIDAdded(const JID &jid);
void handleRosterCleared();
void handleOnJIDRemoved(const JID &jid);
void handleOnJIDUpdated(const JID &jid, const std::string& oldName, const std::vector<std::string>& oldGroups);
void handleStartChatRequest(const JID& contact);
void handleChangeStatusRequest(StatusShow::Type show, const std::string &statusText);
void handleShowOfflineToggled(bool state);
void handleIncomingPresence(std::shared_ptr<Presence> newPresence);
void handleSubscriptionRequest(const JID& jid, const std::string& message);
void handleSubscriptionRequestAccepted(SubscriptionRequestEvent* event);
@@ -84,43 +84,43 @@ namespace Swift {
void handleUIEvent(std::shared_ptr<UIEvent> event);
void handleRosterItemUpdated(ErrorPayload::ref error, std::shared_ptr<RosterPayload> rosterPayload);
void handleRosterSetError(ErrorPayload::ref error, std::shared_ptr<RosterPayload> rosterPayload);
void applyAllPresenceTo(const JID& jid);
void handleEditProfileRequest();
void handleOnCapsChanged(const JID& jid);
void handleSettingChanged(const std::string& settingPath);
void handleBlockingStateChanged();
void handleBlockingItemAdded(const JID& jid);
void handleBlockingItemRemoved(const JID& jid);
JID myJID_;
XMPPRoster* xmppRoster_;
MainWindowFactory* mainWindowFactory_;
MainWindow* mainWindow_;
Roster* roster_;
OfflineRosterFilter* offlineFilter_;
VCardManager* vcardManager_;
AvatarManager* avatarManager_;
NickManager* nickManager_;
NickResolver* nickResolver_;
PresenceOracle* presenceOracle_;
SubscriptionManager* subscriptionManager_;
EventController* eventController_;
RosterGroupExpandinessPersister* expandiness_;
IQRouter* iqRouter_;
SettingsProvider* settings_;
UIEventStream* uiEventStream_;
EntityCapsProvider* entityCapsManager_;
- FileTransferOverview* ftOverview_;
ClientBlockListManager* clientBlockListManager_;
RosterVCardProvider* rosterVCardProvider_;
std::shared_ptr<ContactRosterItem> ownContact_;
+ std::unique_ptr<FeatureOracle> featureOracle_;
boost::signals2::scoped_connection blockingOnStateChangedConnection_;
boost::signals2::scoped_connection blockingOnItemAddedConnection_;
boost::signals2::scoped_connection blockingOnItemRemovedConnection_;
boost::signals2::scoped_connection changeStatusConnection_;
boost::signals2::scoped_connection signOutConnection_;
boost::signals2::scoped_connection uiEventConnection_;
};
}
diff --git a/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp b/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp
index 0cd4080..ddbd7d3 100644
--- a/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp
+++ b/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp
@@ -1,414 +1,481 @@
/*
* Copyright (c) 2010-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <Swiften/Avatars/NullAvatarManager.h>
#include <Swiften/Base/Algorithm.h>
#include <Swiften/Client/ClientBlockListManager.h>
#include <Swiften/Client/DummyNickManager.h>
#include <Swiften/Client/DummyStanzaChannel.h>
+#include <Swiften/Client/MemoryStorages.h>
#include <Swiften/Client/NickResolver.h>
#include <Swiften/Crypto/CryptoProvider.h>
#include <Swiften/Crypto/PlatformCryptoProvider.h>
+#include <Swiften/Disco/CapsInfoGenerator.h>
+#include <Swiften/Disco/CapsManager.h>
#include <Swiften/Disco/CapsProvider.h>
+#include <Swiften/Disco/ClientDiscoManager.h>
#include <Swiften/Disco/EntityCapsManager.h>
#include <Swiften/EventLoop/DummyEventLoop.h>
-#include <Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h>
#include <Swiften/Jingle/JingleSessionManager.h>
#include <Swiften/MUC/MUCRegistry.h>
#include <Swiften/Presence/PresenceOracle.h>
#include <Swiften/Presence/SubscriptionManager.h>
#include <Swiften/Queries/DummyIQChannel.h>
#include <Swiften/Queries/IQRouter.h>
#include <Swiften/Roster/XMPPRosterImpl.h>
#include <Swiften/VCards/VCardManager.h>
#include <Swiften/VCards/VCardMemoryStorage.h>
-#include <Swift/Controllers/FileTransfer/FileTransferOverview.h>
#include <Swift/Controllers/Roster/ContactRosterItem.h>
#include <Swift/Controllers/Roster/GroupRosterItem.h>
#include <Swift/Controllers/Roster/Roster.h>
#include <Swift/Controllers/Roster/RosterController.h>
#include <Swift/Controllers/Settings/DummySettingsProvider.h>
#include <Swift/Controllers/UIEvents/RenameRosterItemUIEvent.h>
#include <Swift/Controllers/UIEvents/UIEventStream.h>
#include <Swift/Controllers/UnitTest/MockMainWindowFactory.h>
#include <Swift/Controllers/XMPPEvents/EventController.h>
using namespace Swift;
-#define CHILDREN mainWindow_->roster->getRoot()->getChildren()
-
class DummyCapsProvider : public CapsProvider {
DiscoInfo::ref getCaps(const std::string&) const {return DiscoInfo::ref(new DiscoInfo());}
};
class RosterControllerTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(RosterControllerTest);
CPPUNIT_TEST(testAdd);
CPPUNIT_TEST(testAddSubscription);
CPPUNIT_TEST(testReceiveRename);
CPPUNIT_TEST(testReceiveRegroup);
CPPUNIT_TEST(testSendRename);
CPPUNIT_TEST(testPresence);
CPPUNIT_TEST(testHighestPresence);
CPPUNIT_TEST(testNotHighestPresence);
CPPUNIT_TEST(testUnavailablePresence);
CPPUNIT_TEST(testRemoveResultsInUnavailablePresence);
CPPUNIT_TEST(testOwnContactInRosterPresence);
+ CPPUNIT_TEST(testMultiResourceFileTransferFeature);
CPPUNIT_TEST_SUITE_END();
public:
void setUp() {
jid_ = JID("testjid@swift.im/swift");
xmppRoster_ = new XMPPRosterImpl();
avatarManager_ = new NullAvatarManager();
mainWindowFactory_ = new MockMainWindowFactory();
mucRegistry_ = new MUCRegistry();
+ crypto_ = PlatformCryptoProvider::create();
+ storages_ = std::unique_ptr<MemoryStorages>(new MemoryStorages(crypto_));
nickResolver_ = new NickResolver(jid_.toBare(), xmppRoster_, nullptr, mucRegistry_);
channel_ = new DummyIQChannel();
router_ = new IQRouter(channel_);
stanzaChannel_ = new DummyStanzaChannel();
presenceOracle_ = new PresenceOracle(stanzaChannel_, xmppRoster_);
subscriptionManager_ = new SubscriptionManager(stanzaChannel_);
eventController_ = new EventController();
uiEventStream_ = new UIEventStream();
settings_ = new DummySettingsProvider();
nickManager_ = new DummyNickManager();
- capsProvider_ = new DummyCapsProvider();
- entityCapsManager_ = new EntityCapsManager(capsProvider_, stanzaChannel_);
+ capsManager_ = std::unique_ptr<CapsManager>(new CapsManager(storages_->getCapsStorage(), stanzaChannel_, router_, crypto_));
+ entityCapsManager_ = new EntityCapsManager(capsManager_.get(), stanzaChannel_);
jingleSessionManager_ = new JingleSessionManager(router_);
- ftManager_ = new DummyFileTransferManager();
- ftOverview_ = new FileTransferOverview(ftManager_);
clientBlockListManager_ = new ClientBlockListManager(router_);
- crypto_ = PlatformCryptoProvider::create();
vcardStorage_ = new VCardMemoryStorage(crypto_);
vcardManager_ = new VCardManager(jid_, router_, vcardStorage_);
- rosterController_ = new RosterController(jid_, xmppRoster_, avatarManager_, mainWindowFactory_, nickManager_, nickResolver_, presenceOracle_, subscriptionManager_, eventController_, uiEventStream_, router_, settings_, entityCapsManager_, ftOverview_, clientBlockListManager_, vcardManager_);
+ rosterController_ = new RosterController(jid_, xmppRoster_, avatarManager_, mainWindowFactory_, nickManager_, nickResolver_, presenceOracle_, subscriptionManager_, eventController_, uiEventStream_, router_, settings_, entityCapsManager_, clientBlockListManager_, vcardManager_);
mainWindow_ = mainWindowFactory_->last;
+ capsInfoGenerator_ = std::unique_ptr<CapsInfoGenerator>(new CapsInfoGenerator("", crypto_));
}
void tearDown() {
delete rosterController_;
delete vcardManager_;
delete vcardStorage_;
delete crypto_;
delete clientBlockListManager_;
- delete ftOverview_;
- delete ftManager_;
delete jingleSessionManager_;
delete entityCapsManager_;
- delete capsProvider_;
delete nickManager_;
delete nickResolver_;
delete mucRegistry_;
delete mainWindowFactory_;
delete avatarManager_;
delete router_;
delete channel_;
delete eventController_;
delete subscriptionManager_;
delete presenceOracle_;
delete stanzaChannel_;
delete uiEventStream_;
delete settings_;
delete xmppRoster_;
}
GroupRosterItem* groupChild(size_t i) {
- return dynamic_cast<GroupRosterItem*>(CHILDREN[i]);
+ return dynamic_cast<GroupRosterItem*>(getUIRosterChildren()[i]);
}
JID withResource(const JID& jid, const std::string& resource) {
return JID(jid.toBare().toString() + "/" + resource);
}
void testPresence() {
std::vector<std::string> groups;
groups.push_back("testGroup1");
groups.push_back("testGroup2");
JID from("test@testdomain.com");
xmppRoster_->addContact(from, "name", groups, RosterItemPayload::Both);
Presence::ref presence(new Presence());
presence->setFrom(withResource(from, "bob"));
presence->setPriority(2);
presence->setStatus("So totally here");
stanzaChannel_->onPresenceReceived(presence);
- ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(CHILDREN[0])->getChildren()[0]);
+ ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(getUIRosterChildren()[0])->getChildren()[0]);
CPPUNIT_ASSERT(item);
CPPUNIT_ASSERT_EQUAL(presence->getStatus(), item->getStatusText());
- ContactRosterItem* item2 = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(CHILDREN[1])->getChildren()[0]);
+ ContactRosterItem* item2 = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(getUIRosterChildren()[1])->getChildren()[0]);
CPPUNIT_ASSERT(item2);
CPPUNIT_ASSERT_EQUAL(presence->getStatus(), item2->getStatusText());
}
void testHighestPresence() {
std::vector<std::string> groups;
groups.push_back("testGroup1");
JID from("test@testdomain.com");
xmppRoster_->addContact(from, "name", groups, RosterItemPayload::Both);
Presence::ref lowPresence(new Presence());
lowPresence->setFrom(withResource(from, "bob"));
lowPresence->setPriority(2);
lowPresence->setStatus("Not here");
Presence::ref highPresence(new Presence());
highPresence->setFrom(withResource(from, "bert"));
highPresence->setPriority(10);
highPresence->setStatus("So totally here");
stanzaChannel_->onPresenceReceived(lowPresence);
stanzaChannel_->onPresenceReceived(highPresence);
- ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(CHILDREN[0])->getChildren()[0]);
+ ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(getUIRosterChildren()[0])->getChildren()[0]);
CPPUNIT_ASSERT(item);
CPPUNIT_ASSERT_EQUAL(highPresence->getStatus(), item->getStatusText());
}
void testNotHighestPresence() {
std::vector<std::string> groups;
groups.push_back("testGroup1");
JID from("test@testdomain.com");
xmppRoster_->addContact(from, "name", groups, RosterItemPayload::Both);
Presence::ref lowPresence(new Presence());
lowPresence->setFrom(withResource(from, "bob"));
lowPresence->setPriority(2);
lowPresence->setStatus("Not here");
Presence::ref highPresence(new Presence());
highPresence->setFrom(withResource(from, "bert"));
highPresence->setPriority(10);
highPresence->setStatus("So totally here");
stanzaChannel_->onPresenceReceived(highPresence);
stanzaChannel_->onPresenceReceived(lowPresence);
- ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(CHILDREN[0])->getChildren()[0]);
+ ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(getUIRosterChildren()[0])->getChildren()[0]);
CPPUNIT_ASSERT(item);
CPPUNIT_ASSERT_EQUAL(highPresence->getStatus(), item->getStatusText());
}
void testUnavailablePresence() {
std::vector<std::string> groups;
groups.push_back("testGroup1");
JID from("test@testdomain.com");
xmppRoster_->addContact(from, "name", groups, RosterItemPayload::Both);
Presence::ref lowPresence(new Presence());
lowPresence->setFrom(withResource(from, "bob"));
lowPresence->setPriority(2);
lowPresence->setShow(StatusShow::Away);
lowPresence->setStatus("Not here");
Presence::ref lowPresenceOffline(new Presence());
lowPresenceOffline->setFrom(withResource(from, "bob"));
lowPresenceOffline->setStatus("Signing out");
lowPresenceOffline->setType(Presence::Unavailable);
Presence::ref highPresence(new Presence());
highPresence->setFrom(withResource(from, "bert"));
highPresence->setPriority(10);
highPresence->setStatus("So totally here");
Presence::ref highPresenceOffline(new Presence());
highPresenceOffline->setFrom(withResource(from, "bert"));
highPresenceOffline->setType(Presence::Unavailable);
stanzaChannel_->onPresenceReceived(lowPresence);
Presence::ref accountPresence = presenceOracle_->getAccountPresence(from);
CPPUNIT_ASSERT_EQUAL(StatusShow::Away, accountPresence->getShow());
stanzaChannel_->onPresenceReceived(highPresence);
accountPresence = presenceOracle_->getAccountPresence(from);
CPPUNIT_ASSERT_EQUAL(StatusShow::Online, accountPresence->getShow());
stanzaChannel_->onPresenceReceived(highPresenceOffline);
// After this, the roster should show the low presence.
- ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(CHILDREN[0])->getChildren()[0]);
+ ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(getUIRosterChildren()[0])->getChildren()[0]);
CPPUNIT_ASSERT(item);
Presence::ref low = presenceOracle_->getAccountPresence(from);
CPPUNIT_ASSERT_EQUAL(Presence::Available, low->getType());
CPPUNIT_ASSERT_EQUAL(lowPresence->getStatus(), low->getStatus());
CPPUNIT_ASSERT_EQUAL(lowPresence->getShow(), item->getStatusShow());
CPPUNIT_ASSERT_EQUAL(lowPresence->getStatus(), item->getStatusText());
stanzaChannel_->onPresenceReceived(lowPresenceOffline);
- item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(CHILDREN[0])->getChildren()[0]);
+ item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(getUIRosterChildren()[0])->getChildren()[0]);
CPPUNIT_ASSERT(item);
/* A verification that if the test fails, it's the RosterController, not the PresenceOracle. */
low = presenceOracle_->getHighestPriorityPresence(from);
CPPUNIT_ASSERT_EQUAL(Presence::Unavailable, low->getType());
CPPUNIT_ASSERT_EQUAL(lowPresenceOffline->getStatus(), low->getStatus());
CPPUNIT_ASSERT_EQUAL(StatusShow::None, item->getStatusShow());
CPPUNIT_ASSERT_EQUAL(lowPresenceOffline->getStatus(), item->getStatusText());
}
void testAdd() {
std::vector<std::string> groups;
groups.push_back("testGroup1");
groups.push_back("testGroup2");
xmppRoster_->addContact(JID("test@testdomain.com/bob"), "name", groups, RosterItemPayload::Both);
- CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(CHILDREN.size()));
+ CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(getUIRosterChildren().size()));
//CPPUNIT_ASSERT_EQUAL(std::string("Bob"), xmppRoster_->getNameForJID(JID("foo@bar.com")));
}
void testAddSubscription() {
std::vector<std::string> groups;
JID jid("test@testdomain.com");
xmppRoster_->addContact(jid, "name", groups, RosterItemPayload::None);
- CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(CHILDREN.size()));
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(getUIRosterChildren().size()));
CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(groupChild(0)->getChildren().size()));
xmppRoster_->addContact(jid, "name", groups, RosterItemPayload::To);
- CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(CHILDREN.size()));
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(getUIRosterChildren().size()));
CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(groupChild(0)->getChildren().size()));
xmppRoster_->addContact(jid, "name", groups, RosterItemPayload::Both);
- CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(CHILDREN.size()));
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(getUIRosterChildren().size()));
CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(groupChild(0)->getChildren().size()));
}
void testReceiveRename() {
std::vector<std::string> groups;
JID jid("test@testdomain.com");
xmppRoster_->addContact(jid, "name", groups, RosterItemPayload::Both);
- CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(CHILDREN.size()));
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(getUIRosterChildren().size()));
CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(groupChild(0)->getChildren().size()));
CPPUNIT_ASSERT_EQUAL(std::string("name"), groupChild(0)->getChildren()[0]->getDisplayName());
xmppRoster_->addContact(jid, "NewName", groups, RosterItemPayload::Both);
- CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(CHILDREN.size()));
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(getUIRosterChildren().size()));
CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(groupChild(0)->getChildren().size()));
CPPUNIT_ASSERT_EQUAL(std::string("NewName"), groupChild(0)->getChildren()[0]->getDisplayName());
}
void testReceiveRegroup() {
std::vector<std::string> oldGroups;
std::vector<std::string> newGroups;
newGroups.push_back("A Group");
std::vector<std::string> newestGroups;
newestGroups.push_back("Best Group");
JID jid("test@testdomain.com");
xmppRoster_->addContact(jid, "", oldGroups, RosterItemPayload::Both);
- CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(CHILDREN.size()));
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(getUIRosterChildren().size()));
CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(groupChild(0)->getChildren().size()));
CPPUNIT_ASSERT_EQUAL(jid.toString(), groupChild(0)->getChildren()[0]->getDisplayName());
xmppRoster_->addContact(jid, "new name", newGroups, RosterItemPayload::Both);
- CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(CHILDREN.size()));
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(getUIRosterChildren().size()));
CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(groupChild(0)->getChildren().size()));
CPPUNIT_ASSERT_EQUAL(std::string("new name"), groupChild(0)->getChildren()[0]->getDisplayName());
CPPUNIT_ASSERT_EQUAL(std::string("A Group"), groupChild(0)->getDisplayName());
xmppRoster_->addContact(jid, "new name", newestGroups, RosterItemPayload::Both);
- CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(CHILDREN.size()));
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(getUIRosterChildren().size()));
CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(groupChild(0)->getChildren().size()));
CPPUNIT_ASSERT_EQUAL(std::string("new name"), groupChild(0)->getChildren()[0]->getDisplayName());
CPPUNIT_ASSERT_EQUAL(std::string("Best Group"), groupChild(0)->getDisplayName());
}
void testSendRename() {
JID jid("testling@wonderland.lit");
std::vector<std::string> groups;
groups.push_back("Friends");
groups.push_back("Enemies");
xmppRoster_->addContact(jid, "Bob", groups, RosterItemPayload::From);
CPPUNIT_ASSERT_EQUAL(groups.size(), xmppRoster_->getGroupsForJID(jid).size());
uiEventStream_->send(std::make_shared<RenameRosterItemUIEvent>(jid, "Robert"));
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), channel_->iqs_.size());
CPPUNIT_ASSERT_EQUAL(IQ::Set, channel_->iqs_[0]->getType());
std::shared_ptr<RosterPayload> payload = channel_->iqs_[0]->getPayload<RosterPayload>();
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), payload->getItems().size());
RosterItemPayload item = payload->getItems()[0];
CPPUNIT_ASSERT_EQUAL(jid, item.getJID());
CPPUNIT_ASSERT_EQUAL(std::string("Robert"), item.getName());
CPPUNIT_ASSERT_EQUAL(groups.size(), item.getGroups().size());
assertVectorsEqual(groups, item.getGroups(), __LINE__);
}
void testRemoveResultsInUnavailablePresence() {
std::vector<std::string> groups;
groups.push_back("testGroup1");
JID from("test@testdomain.com");
xmppRoster_->addContact(from, "name", groups, RosterItemPayload::Both);
Presence::ref lowPresence(new Presence());
lowPresence->setFrom(withResource(from, "bob"));
lowPresence->setPriority(2);
lowPresence->setStatus("Not here");
Presence::ref highPresence(new Presence());
highPresence->setFrom(withResource(from, "bert"));
highPresence->setPriority(10);
highPresence->setStatus("So totally here");
stanzaChannel_->onPresenceReceived(highPresence);
stanzaChannel_->onPresenceReceived(lowPresence);
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), presenceOracle_->getAllPresence("test@testdomain.com").size());
xmppRoster_->onJIDRemoved(JID("test@testdomain.com"));
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), presenceOracle_->getAllPresence("test@testdomain.com").size());
CPPUNIT_ASSERT_EQUAL(Presence::Unavailable, presenceOracle_->getAllPresence("test@testdomain.com")[0]->getType());
}
void testOwnContactInRosterPresence() {
std::vector<std::string> groups;
groups.push_back("testGroup1");
groups.push_back("testGroup2");
JID from = jid_;
xmppRoster_->addContact(from.toBare(), "name", groups, RosterItemPayload::Both);
Presence::ref presence(new Presence());
presence->setFrom(from);
presence->setPriority(2);
presence->setStatus("So totally here");
stanzaChannel_->onPresenceReceived(presence);
- ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(CHILDREN[0])->getChildren()[0]);
+ ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(getUIRosterChildren()[0])->getChildren()[0]);
CPPUNIT_ASSERT(item);
CPPUNIT_ASSERT_EQUAL(presence->getStatus(), item->getStatusText());
- ContactRosterItem* item2 = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(CHILDREN[1])->getChildren()[0]);
+ ContactRosterItem* item2 = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(getUIRosterChildren()[1])->getChildren()[0]);
CPPUNIT_ASSERT(item2);
CPPUNIT_ASSERT_EQUAL(presence->getStatus(), item2->getStatusText());
}
+ // This tests a scenario of a contact having a resource supporting Jingle File Transfer and
+ // one resource not supporting it, and the contact features being set correctly.
+ void testMultiResourceFileTransferFeature() {
+ JID contact("test@testdomain.com");
+ xmppRoster_->addContact(contact, "Name", {}, RosterItemPayload::Both);
+
+ auto sendPresenceAndAnswerCaps = [=](const JID& from, const DiscoInfo& discoInfo) {
+ auto capsInfo = capsInfoGenerator_->generateCapsInfo(discoInfo);
+
+ auto ftClientPresence = std::make_shared<Presence>();
+ ftClientPresence->setFrom(from);
+ ftClientPresence->setPriority(0);
+ ftClientPresence->setShow(StatusShow::Online);
+ ftClientPresence->addPayload(std::make_shared<CapsInfo>(capsInfo));
+ stanzaChannel_->onPresenceReceived(ftClientPresence);
+
+ // disco reply
+ auto discoRequest = channel_->iqs_.back();
+ CPPUNIT_ASSERT(discoRequest);
+ auto discoReply = IQ::createResult(discoRequest->getFrom(), ftClientPresence->getFrom(), discoRequest->getID(), std::make_shared<DiscoInfo>(discoInfo));
+ channel_->onIQReceived(discoReply);
+ };
+
+ auto ftDiscoInfo = DiscoInfo();
+ ftDiscoInfo.addFeature(DiscoInfo::JingleFeature);
+ ftDiscoInfo.addFeature(DiscoInfo::JingleFTFeature);
+ ftDiscoInfo.addFeature(DiscoInfo::JingleTransportsIBBFeature);
+
+ sendPresenceAndAnswerCaps(contact.withResource("ft-supported"), ftDiscoInfo);
+
+ auto* item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(getUIRosterChildren()[0])->getChildren()[0]);
+ CPPUNIT_ASSERT(item);
+ CPPUNIT_ASSERT_EQUAL(contact, item->getJID());
+ CPPUNIT_ASSERT_EQUAL(true, item->supportsFeature(ContactRosterItem::FileTransferFeature));
+
+ sendPresenceAndAnswerCaps(contact.withResource("ft-unsupported"), DiscoInfo());
+
+ item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(getUIRosterChildren()[0])->getChildren()[0]);
+ CPPUNIT_ASSERT(item);
+ CPPUNIT_ASSERT_EQUAL(contact, item->getJID());
+ CPPUNIT_ASSERT_EQUAL(true, item->supportsFeature(ContactRosterItem::FileTransferFeature));
+
+ auto unavailablePresence = std::make_shared<Presence>();
+ unavailablePresence->setFrom(contact.withResource("ft-unsupported"));
+ unavailablePresence->setPriority(0);
+ unavailablePresence->setType(Presence::Unavailable);
+ stanzaChannel_->onPresenceReceived(unavailablePresence);
+
+ item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(getUIRosterChildren()[0])->getChildren()[0]);
+ CPPUNIT_ASSERT(item);
+ CPPUNIT_ASSERT_EQUAL(contact, item->getJID());
+ CPPUNIT_ASSERT_EQUAL(true, item->supportsFeature(ContactRosterItem::FileTransferFeature));
+
+ unavailablePresence = std::make_shared<Presence>();
+ unavailablePresence->setFrom(contact.withResource("ft-supported"));
+ unavailablePresence->setPriority(0);
+ unavailablePresence->setType(Presence::Unavailable);
+ stanzaChannel_->onPresenceReceived(unavailablePresence);
+
+ item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(getUIRosterChildren()[0])->getChildren()[0]);
+ CPPUNIT_ASSERT(item);
+ CPPUNIT_ASSERT_EQUAL(contact, item->getJID());
+ CPPUNIT_ASSERT_EQUAL(false, item->supportsFeature(ContactRosterItem::FileTransferFeature));
+ }
+
void assertVectorsEqual(const std::vector<std::string>& v1, const std::vector<std::string>& v2, int line) {
for (const auto& entry : v1) {
if (std::find(v2.begin(), v2.end(), entry) == v2.end()) {
std::stringstream stream;
stream << "Couldn't find " << entry << " in v2 (line " << line << ")";
CPPUNIT_FAIL(stream.str());
}
}
}
+ const std::vector<RosterItem*>& getUIRosterChildren() const {
+ return mainWindow_->roster->getRoot()->getChildren();
+ }
+
private:
JID jid_;
+ std::unique_ptr<MemoryStorages> storages_;
XMPPRosterImpl* xmppRoster_;
MUCRegistry* mucRegistry_;
AvatarManager* avatarManager_;
MockMainWindowFactory* mainWindowFactory_;
NickManager* nickManager_;
NickResolver* nickResolver_;
RosterController* rosterController_;
DummyIQChannel* channel_;
DummyStanzaChannel* stanzaChannel_;
IQRouter* router_;
PresenceOracle* presenceOracle_;
SubscriptionManager* subscriptionManager_;
EventController* eventController_;
UIEventStream* uiEventStream_;
MockMainWindow* mainWindow_;
DummySettingsProvider* settings_;
- DummyCapsProvider* capsProvider_;
+ std::unique_ptr<CapsManager> capsManager_;
EntityCapsManager* entityCapsManager_;
JingleSessionManager* jingleSessionManager_;
- FileTransferManager* ftManager_;
- FileTransferOverview* ftOverview_;
ClientBlockListManager* clientBlockListManager_;
CryptoProvider* crypto_;
VCardStorage* vcardStorage_;
VCardManager* vcardManager_;
+ std::unique_ptr<CapsInfoGenerator> capsInfoGenerator_;
};
CPPUNIT_TEST_SUITE_REGISTRATION(RosterControllerTest);
diff --git a/Swiften/Base/SConscript b/Swiften/Base/SConscript
index 92e3da8..de502c2 100644
--- a/Swiften/Base/SConscript
+++ b/Swiften/Base/SConscript
@@ -1,23 +1,23 @@
Import("swiften_env")
objects = swiften_env.SwiftenObject([
+ "BoostRandomGenerator.cpp",
"ByteArray.cpp",
"DateTime.cpp",
- "SafeByteArray.cpp",
- "SafeAllocator.cpp",
"Error.cpp",
+ "FileSize.cpp",
+ "IDGenerator.cpp",
"Log.cpp",
"LogSerializers.cpp",
"Path.cpp",
"Paths.cpp",
- "String.cpp",
- "IDGenerator.cpp",
- "SimpleIDGenerator.cpp",
"RandomGenerator.cpp",
- "BoostRandomGenerator.cpp",
- "sleep.cpp",
- "URL.cpp",
"Regex.cpp",
- "FileSize.cpp"
+ "SafeAllocator.cpp",
+ "SafeByteArray.cpp",
+ "SimpleIDGenerator.cpp",
+ "String.cpp",
+ "URL.cpp",
+ "sleep.cpp",
])
swiften_env.Append(SWIFTEN_OBJECTS = [objects])
diff --git a/Swiften/Disco/FeatureOracle.cpp b/Swiften/Disco/FeatureOracle.cpp
index 8328984..2baf87c 100644
--- a/Swiften/Disco/FeatureOracle.cpp
+++ b/Swiften/Disco/FeatureOracle.cpp
@@ -1,99 +1,201 @@
/*
* Copyright (c) 2015-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swiften/Disco/FeatureOracle.h>
#include <algorithm>
#include <iterator>
+#include <unordered_set>
#include <vector>
-#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/Log.h>
#include <Swiften/Disco/EntityCapsProvider.h>
+#include <Swiften/Elements/Idle.h>
#include <Swiften/Elements/Presence.h>
#include <Swiften/FileTransfer/FileTransferManager.h>
#include <Swiften/JID/JID.h>
#include <Swiften/Presence/PresenceOracle.h>
namespace Swift {
FeatureOracle::FeatureOracle(EntityCapsProvider* capsProvider, PresenceOracle* presenceOracle) : capsProvider_(capsProvider), presenceOracle_(presenceOracle) {
-
}
Tristate FeatureOracle::isFileTransferSupported(const JID& jid) {
- DiscoInfo::ref discoInfo = getDiscoResultForJID(jid);
- if (discoInfo) {
- return FileTransferManager::isSupportedBy(discoInfo) ? Yes : No;
+ Tristate fileTransferSupported = No;
+
+ auto isYesOrMaybe = [](Tristate tristate) { return tristate == Yes || tristate == Maybe; };
+ auto isYes = [](Tristate tristate) { return tristate == Yes; };
+
+ auto supportedFeatures = getFeaturesForJID(jid);
+
+ auto jingleSupported = isFeatureSupported(supportedFeatures, DiscoInfo::JingleFeature);
+ auto jingleFTSupported = isFeatureSupported(supportedFeatures, DiscoInfo::JingleFTFeature);
+ auto jingleTransportIBBSupported = isFeatureSupported(supportedFeatures, DiscoInfo::JingleTransportsIBBFeature);
+ auto jingleTransportS5BSupported = isFeatureSupported(supportedFeatures, DiscoInfo::JingleTransportsS5BFeature);
+
+ if (isYes(jingleSupported) && isYes(jingleFTSupported) && (isYes(jingleTransportIBBSupported) || isYes(jingleTransportS5BSupported))) {
+ fileTransferSupported = Yes;
}
- else {
- return Maybe;
+ else if (isYesOrMaybe(jingleSupported) && isYesOrMaybe(jingleFTSupported) && (isYesOrMaybe(jingleTransportIBBSupported) || isYesOrMaybe(jingleTransportS5BSupported))) {
+ fileTransferSupported = Maybe;
}
+
+ return fileTransferSupported;
}
Tristate FeatureOracle::isMessageReceiptsSupported(const JID& jid) {
- return isFeatureSupported(jid, DiscoInfo::MessageDeliveryReceiptsFeature);
+ return isFeatureSupported(getFeaturesForJID(jid), DiscoInfo::MessageDeliveryReceiptsFeature);
}
Tristate FeatureOracle::isMessageCorrectionSupported(const JID& jid) {
- return isFeatureSupported(jid, DiscoInfo::MessageCorrectionFeature);
+ return isFeatureSupported(getFeaturesForJID(jid), DiscoInfo::MessageCorrectionFeature);
+}
+
+Tristate FeatureOracle::isWhiteboardSupported(const JID& jid) {
+ return isFeatureSupported(getFeaturesForJID(jid), DiscoInfo::WhiteboardFeature);
}
-DiscoInfo::ref FeatureOracle::getDiscoResultForJID(const JID& jid) {
- DiscoInfo::ref discoInfo;
+class PresenceFeatureAvailablityComparator {
+ public:
+ static int preferenceFromStatusShow(StatusShow::Type showType) {
+ switch (showType) {
+ case StatusShow::FFC:
+ return 5;
+ case StatusShow::Online:
+ return 4;
+ case StatusShow::DND:
+ return 3;
+ case StatusShow::Away:
+ return 2;
+ case StatusShow::XA:
+ return 1;
+ case StatusShow::None:
+ return 0;
+ }
+ assert(false);
+ return -1;
+ }
+
+ static int compareWithoutResource(const Presence::ref& a, const Presence::ref& b) {
+ int aPreference = preferenceFromStatusShow(a->getShow());
+ int bPreference = preferenceFromStatusShow(b->getShow());
+
+ if (aPreference != bPreference) {
+ return aPreference < bPreference ? 1 : -1;
+ }
+
+ Idle::ref aIdle = a->getPayload<Idle>();
+ Idle::ref bIdle = b->getPayload<Idle>();
+
+ if (aIdle && !bIdle) {
+ return -1;
+ }
+ else if (!aIdle && bIdle) {
+ return 1;
+ }
+
+ if (a->getPriority() != b->getPriority()) {
+ return a->getPriority() < b->getPriority() ? 1 : -1;
+ }
+
+ return 0;
+ }
+
+ /*
+ * This method returns true, if \ref Presence \p b is more available than
+ * \ref Presence \p a.
+ * Going by \ref StatusShow::Type first, then by idle state, then by
+ * presence priority and finally by resource name.
+ */
+ bool operator()(const Presence::ref& a, const Presence::ref& b) {
+ int aMoreAvailableThanB = compareWithoutResource(a, b);
+ if (aMoreAvailableThanB < 0) {
+ return true;
+ }
+ else if (aMoreAvailableThanB > 0) {
+ return false;
+ }
+
+ return a->getFrom().getResource() < b->getFrom().getResource();
+ }
+};
+
+JID FeatureOracle::getMostAvailableClientForFileTrasfer(const JID& bareJID) {
+ JID fullJID;
+ assert(bareJID.isBare());
+
+ std::vector<Presence::ref> allPresences = presenceOracle_->getAllPresence(bareJID);
+ std::sort(allPresences.begin(), allPresences.end(), PresenceFeatureAvailablityComparator());
+
+ for (const auto& presence : allPresences) {
+ if (presence->isAvailable()) {
+ if (isFileTransferSupported(presence->getFrom()) == Yes) {
+ fullJID = presence->getFrom();
+ break;
+ }
+ }
+ }
+
+ SWIFT_LOG_ASSERT(!fullJID.isBare(), error);
+ return fullJID;
+}
+
+std::unordered_map<std::string, Tristate> FeatureOracle::getFeaturesForJID(const JID& jid) {
+ std::unordered_map<std::string, Tristate> supportedFeatures;
if (jid.isBare()) {
- // Calculate the common subset of disco features of all available results and return that.
- std::vector<Presence::ref> availablePresences = presenceOracle_->getAllPresence(jid);
-
- bool commonFeaturesInitialized = false;
- std::vector<std::string> commonFeatures;
- foreach(Presence::ref presence, availablePresences) {
- DiscoInfo::ref presenceDiscoInfo = capsProvider_->getCaps(presence->getFrom());
- if (presenceDiscoInfo) {
- std::vector<std::string> features = presenceDiscoInfo->getFeatures();
- if (!commonFeaturesInitialized) {
- commonFeatures = features;
- commonFeaturesInitialized = true;
- }
- else {
- std::vector<std::string> featuresToRemove;
- foreach(const std::string& feature, commonFeatures) {
- if (std::find(features.begin(), features.end(), feature) == features.end()) {
- featuresToRemove.push_back(feature);
- }
- }
- foreach(const std::string& featureToRemove, featuresToRemove) {
- commonFeatures.erase(std::remove(commonFeatures.begin(), commonFeatures.end(), featureToRemove), commonFeatures.end());
- }
+ // Calculate the union of disco features of all most available results and return that.
+ std::vector<DiscoInfo::ref> onlineDiscoInfos;
+ std::unordered_set<std::string> features;
+
+ // Collect relevant disco info results and the set of features.
+ for (auto&& presence : presenceOracle_->getAllPresence(jid)) {
+ if (presence->getType() == Presence::Available) {
+ DiscoInfo::ref presenceDiscoInfo = capsProvider_->getCaps(presence->getFrom());
+ if (presenceDiscoInfo) {
+ onlineDiscoInfos.push_back(presenceDiscoInfo);
+ features.insert(presenceDiscoInfo->getFeatures().begin(), presenceDiscoInfo->getFeatures().end());
}
}
}
- discoInfo = std::make_shared<DiscoInfo>();
- foreach(const std::string& commonFeature, commonFeatures) {
- discoInfo->addFeature(commonFeature);
+ // Calculate supportedFeaturesMap.
+ for (auto&& feature : features) {
+ Tristate supported = Yes;
+ for (auto&& discoInfo : onlineDiscoInfos) {
+ if (!discoInfo->hasFeature(feature)) {
+ supported = Maybe;
+ break;
+ }
+ }
+ supportedFeatures[feature] = supported;
}
}
else {
// Return the disco result of the full JID.
- discoInfo = capsProvider_->getCaps(jid);
+ auto discoInfo = capsProvider_->getCaps(jid);
+ if (discoInfo) {
+ for (auto&& feature : discoInfo->getFeatures()) {
+ supportedFeatures[feature] = Yes;
+ }
+ }
}
- return discoInfo;
+ return supportedFeatures;
}
-Tristate FeatureOracle::isFeatureSupported(const JID& jid, const std::string& feature) {
- DiscoInfo::ref discoInfo = getDiscoResultForJID(jid);
- if (discoInfo) {
- return discoInfo->hasFeature(feature) ? Yes : No;
- }
- else {
- return Maybe;
+Tristate FeatureOracle::isFeatureSupported(const std::unordered_map<std::string, Tristate>& supportedFeatures, const std::string& feature) {
+ Tristate supported = No;
+ auto lookupResult = supportedFeatures.find(feature);
+ if (lookupResult != supportedFeatures.end()) {
+ supported = lookupResult->second;
}
+ return supported;
}
}
diff --git a/Swiften/Disco/FeatureOracle.h b/Swiften/Disco/FeatureOracle.h
index d434e86..be0cd6f 100644
--- a/Swiften/Disco/FeatureOracle.h
+++ b/Swiften/Disco/FeatureOracle.h
@@ -1,47 +1,53 @@
/*
- * Copyright (c) 2015 Isode Limited.
+ * Copyright (c) 2015-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
+#include <unordered_map>
+#include <unordered_set>
+
#include <Swiften/Base/API.h>
#include <Swiften/Base/Tristate.h>
#include <Swiften/Elements/DiscoInfo.h>
namespace Swift {
class EntityCapsProvider;
class JID;
class PresenceOracle;
/**
* @brief The FeatureOracle class enables direct feature support lookup for client features supported by Swiften.
*/
class SWIFTEN_API FeatureOracle {
public:
FeatureOracle(EntityCapsProvider* capsProvider, PresenceOracle* presenceOracle);
public:
Tristate isFileTransferSupported(const JID& jid);
Tristate isMessageReceiptsSupported(const JID& jid);
Tristate isMessageCorrectionSupported(const JID& jid);
+ Tristate isWhiteboardSupported(const JID& jid);
+
+ JID getMostAvailableClientForFileTrasfer(const JID& bareJID);
private:
/**
* @brief getDiscoResultForJID returns a shared reference to a DiscoInfo representing features supported by the jid.
- * @param jid The JID to return the DiscoInfo::ref for.
- * @return DiscoResult::ref
+ * @param jid The JID to return an std::unordered_map<std::string, Tristate> for.
+ * @return std::unordered_map<std::string, Tristate>
*/
- DiscoInfo::ref getDiscoResultForJID(const JID& jid);
+ std::unordered_map<std::string, Tristate> getFeaturesForJID(const JID& jid);
- Tristate isFeatureSupported(const JID& jid, const std::string& feature);
+ Tristate isFeatureSupported(const std::unordered_map<std::string, Tristate>& supportedFeatures, const std::string& feature);
private:
EntityCapsProvider* capsProvider_;
PresenceOracle* presenceOracle_;
};
}
diff --git a/Swiften/Disco/UnitTest/FeatureOracleTest.cpp b/Swiften/Disco/UnitTest/FeatureOracleTest.cpp
new file mode 100644
index 0000000..e5ff09b
--- /dev/null
+++ b/Swiften/Disco/UnitTest/FeatureOracleTest.cpp
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2016 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <vector>
+
+#include <boost/bind.hpp>
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include <Swiften/Base/Tristate.h>
+#include <Swiften/Client/DummyStanzaChannel.h>
+#include <Swiften/Crypto/CryptoProvider.h>
+#include <Swiften/Crypto/PlatformCryptoProvider.h>
+#include <Swiften/Disco/CapsInfoGenerator.h>
+#include <Swiften/Disco/CapsProvider.h>
+#include <Swiften/Disco/EntityCapsManager.h>
+#include <Swiften/Disco/FeatureOracle.h>
+#include <Swiften/Elements/CapsInfo.h>
+#include <Swiften/Presence/PresenceOracle.h>
+#include <Swiften/Roster/XMPPRosterImpl.h>
+
+using namespace Swift;
+
+class FeatureOracleTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(FeatureOracleTest);
+ CPPUNIT_TEST(testMergeAvailableResourcesForFeatures);
+ CPPUNIT_TEST(testMostAvailableFileTransferClient);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void setUp() {
+ crypto_ = std::shared_ptr<CryptoProvider>(PlatformCryptoProvider::create());
+ dummyStanzaChannel_ = new DummyStanzaChannel();
+ xmppRosterImpl_ = new XMPPRosterImpl();
+ dummyCapsProvider_ = new DummyCapsProvider();
+ entityCapsManager_ = new EntityCapsManager(dummyCapsProvider_, dummyStanzaChannel_);
+ presenceOracle_ = new PresenceOracle(dummyStanzaChannel_, xmppRosterImpl_);
+ featureOracle_ = new FeatureOracle(entityCapsManager_, presenceOracle_);
+ }
+
+ void tearDown() {
+ delete featureOracle_;
+ delete presenceOracle_;
+ delete entityCapsManager_;
+ delete dummyCapsProvider_;
+ delete xmppRosterImpl_;
+ delete dummyStanzaChannel_;
+ }
+
+ void simulateIncomingPresence(const JID& from, Presence::Type type, StatusShow::Type status, const DiscoInfo::ref& disco, const std::vector<Payload::ref>& additionalPayloads = {}) {
+ auto capsInfo = std::make_shared<CapsInfo>(CapsInfoGenerator("http://example.com", crypto_.get()).generateCapsInfo(*disco.get()));
+ dummyCapsProvider_->caps[capsInfo->getVersion()] = disco;
+
+ Presence::ref capsNotifyPresence = std::make_shared<Presence>();
+ capsNotifyPresence->setType(type);
+ capsNotifyPresence->setFrom(from);
+ capsNotifyPresence->setShow(status);
+ capsNotifyPresence->addPayload(capsInfo);
+
+ capsNotifyPresence->addPayloads(additionalPayloads);
+
+ xmppRosterImpl_->addContact(from, "Foo", {}, RosterItemPayload::Both);
+ dummyStanzaChannel_->onPresenceReceived(capsNotifyPresence);
+ }
+
+ DiscoInfo::ref fileTransferSupportingDisco() {
+ DiscoInfo::ref discoInfo = std::make_shared<DiscoInfo>();
+ discoInfo->addFeature(DiscoInfo::JingleFeature);
+ discoInfo->addFeature(DiscoInfo::JingleFTFeature);
+ discoInfo->addFeature(DiscoInfo::JingleTransportsS5BFeature);
+ discoInfo->addFeature(DiscoInfo::JingleTransportsIBBFeature);
+ return discoInfo;
+ }
+
+ DiscoInfo::ref noFileTransferSupportingDisco() {
+ DiscoInfo::ref discoInfo = std::make_shared<DiscoInfo>();
+ discoInfo->addFeature(DiscoInfo::JingleFeature);
+ return discoInfo;
+ }
+
+ void testMergeAvailableResourcesForFeatures() {
+ CPPUNIT_ASSERT_EQUAL(No, featureOracle_->isFileTransferSupported(baseJID));
+
+ simulateIncomingPresence(noFileTransferJID, Presence::Available, StatusShow::Online, noFileTransferSupportingDisco());
+
+ CPPUNIT_ASSERT_EQUAL(size_t(1), presenceOracle_->getAllPresence(baseJID).size());
+ CPPUNIT_ASSERT_EQUAL(No, featureOracle_->isFileTransferSupported(baseJID));
+
+ simulateIncomingPresence(fileTransferJID, Presence::Available, StatusShow::Online, fileTransferSupportingDisco());
+
+ CPPUNIT_ASSERT_EQUAL(size_t(2), presenceOracle_->getAllPresence(baseJID).size());
+ CPPUNIT_ASSERT_EQUAL(Maybe, featureOracle_->isFileTransferSupported(baseJID));
+
+ simulateIncomingPresence(noFileTransferJID, Presence::Unavailable, StatusShow::None, noFileTransferSupportingDisco());
+
+ CPPUNIT_ASSERT_EQUAL(size_t(1), presenceOracle_->getAllPresence(baseJID).size());
+ CPPUNIT_ASSERT_EQUAL(Yes, featureOracle_->isFileTransferSupported(baseJID));
+
+ simulateIncomingPresence(fileTransferJID, Presence::Unavailable, StatusShow::None, fileTransferSupportingDisco());
+
+ CPPUNIT_ASSERT_EQUAL(size_t(1), presenceOracle_->getAllPresence(baseJID).size());
+ CPPUNIT_ASSERT_EQUAL(No, featureOracle_->isFileTransferSupported(baseJID));
+ }
+
+ void testMostAvailableFileTransferClient() {
+ simulateIncomingPresence(fileTransferJID, Presence::Available, StatusShow::DND, fileTransferSupportingDisco());
+
+ CPPUNIT_ASSERT_EQUAL(fileTransferJID, featureOracle_->getMostAvailableClientForFileTrasfer(baseJID));
+
+ simulateIncomingPresence(noFileTransferJID, Presence::Available, StatusShow::Online, noFileTransferSupportingDisco());
+
+ CPPUNIT_ASSERT_EQUAL(fileTransferJID, featureOracle_->getMostAvailableClientForFileTrasfer(baseJID));
+
+ auto moreAvailableJID = baseJID.withResource("moreAvailableFt");
+ simulateIncomingPresence(moreAvailableJID, Presence::Available, StatusShow::Online, fileTransferSupportingDisco());
+
+ CPPUNIT_ASSERT_EQUAL(moreAvailableJID, featureOracle_->getMostAvailableClientForFileTrasfer(baseJID));
+ }
+
+ private:
+ struct DummyCapsProvider : public CapsProvider {
+ virtual DiscoInfo::ref getCaps(const std::string& hash) const {
+ std::map<std::string, DiscoInfo::ref>::const_iterator i = caps.find(hash);
+ if (i != caps.end()) {
+ return i->second;
+ }
+ return DiscoInfo::ref();
+ }
+
+ std::map<std::string, DiscoInfo::ref> caps;
+ };
+
+ private:
+ JID baseJID = "test@example.com";
+ JID fileTransferJID = baseJID.withResource("fileTransfer");
+ JID noFileTransferJID = baseJID.withResource("noFileTransfer");
+
+ std::shared_ptr<CryptoProvider> crypto_;
+ DummyCapsProvider* dummyCapsProvider_;
+ DummyStanzaChannel* dummyStanzaChannel_;
+ EntityCapsManager* entityCapsManager_;
+ FeatureOracle* featureOracle_;
+ PresenceOracle* presenceOracle_;
+ XMPPRosterImpl* xmppRosterImpl_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(FeatureOracleTest);
diff --git a/Swiften/Elements/Stanza.h b/Swiften/Elements/Stanza.h
index 9a69696..9284bc3 100644
--- a/Swiften/Elements/Stanza.h
+++ b/Swiften/Elements/Stanza.h
@@ -41,56 +41,61 @@ namespace Swift {
}
return std::shared_ptr<T>();
}
template<typename T>
std::vector< std::shared_ptr<T> > getPayloads() const {
std::vector< std::shared_ptr<T> > results;
for (const auto& payload : payloads_) {
std::shared_ptr<T> result(std::dynamic_pointer_cast<T>(payload));
if (result) {
results.push_back(result);
}
}
return results;
}
const std::vector< std::shared_ptr<Payload> >& getPayloads() const {
return payloads_;
}
void addPayload(std::shared_ptr<Payload> payload) {
payloads_.push_back(payload);
}
template<typename InputIterator>
void addPayloads(InputIterator begin, InputIterator end) {
payloads_.insert(payloads_.end(), begin, end);
}
+ template<typename Container>
+ void addPayloads(const Container& container) {
+ payloads_.insert(payloads_.end(), std::begin(container), std::end(container));
+ }
+
void updatePayload(std::shared_ptr<Payload> payload);
void removePayloadOfSameType(std::shared_ptr<Payload>);
std::shared_ptr<Payload> getPayloadOfSameType(std::shared_ptr<Payload>) const;
const JID& getFrom() const { return from_; }
void setFrom(const JID& from) { from_ = from; }
const JID& getTo() const { return to_; }
void setTo(const JID& to) { to_ = to; }
const std::string& getID() const { return id_; }
void setID(const std::string& id) { id_ = id; }
boost::optional<boost::posix_time::ptime> getTimestamp() const;
// Falls back to any timestamp if no specific timestamp for the given JID is found.
boost::optional<boost::posix_time::ptime> getTimestampFrom(const JID& jid) const;
private:
std::string id_;
JID from_;
JID to_;
std::vector< std::shared_ptr<Payload> > payloads_;
};
}
diff --git a/Swiften/FileTransfer/FileTransferManager.cpp b/Swiften/FileTransfer/FileTransferManager.cpp
index 94f4ab7..a5d7313 100644
--- a/Swiften/FileTransfer/FileTransferManager.cpp
+++ b/Swiften/FileTransfer/FileTransferManager.cpp
@@ -1,23 +1,20 @@
/*
* Copyright (c) 2011 Tobias Markmann
* Licensed under the simplified BSD license.
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/
+/*
+ * Copyright (c) 2016 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
#include <Swiften/FileTransfer/FileTransferManager.h>
namespace Swift {
FileTransferManager::~FileTransferManager() {
}
-bool FileTransferManager::isSupportedBy(const DiscoInfo::ref info) {
- if (info) {
- return info->hasFeature(DiscoInfo::JingleFeature)
- && info->hasFeature(DiscoInfo::JingleFTFeature)
- && (info->hasFeature(DiscoInfo::JingleTransportsIBBFeature) || info->hasFeature(DiscoInfo::JingleTransportsS5BFeature));
- }
- return false;
-}
-
}
diff --git a/Swiften/FileTransfer/FileTransferManager.h b/Swiften/FileTransfer/FileTransferManager.h
index e315c67..07cfc90 100644
--- a/Swiften/FileTransfer/FileTransferManager.h
+++ b/Swiften/FileTransfer/FileTransferManager.h
@@ -20,35 +20,33 @@
#include <Swiften/Base/API.h>
#include <Swiften/Elements/DiscoInfo.h>
#include <Swiften/FileTransfer/FileTransferOptions.h>
#include <Swiften/FileTransfer/IncomingFileTransfer.h>
#include <Swiften/FileTransfer/OutgoingFileTransfer.h>
#include <Swiften/JID/JID.h>
namespace Swift {
class ReadBytestream;
class SWIFTEN_API FileTransferManager {
public:
virtual ~FileTransferManager();
virtual OutgoingFileTransfer::ref createOutgoingFileTransfer(
const JID& to,
const boost::filesystem::path& filepath,
const std::string& description,
std::shared_ptr<ReadBytestream> bytestream,
const FileTransferOptions& = FileTransferOptions()) = 0;
virtual OutgoingFileTransfer::ref createOutgoingFileTransfer(
const JID& to,
const std::string& filename,
const std::string& description,
const boost::uintmax_t sizeInBytes,
const boost::posix_time::ptime& lastModified,
std::shared_ptr<ReadBytestream> bytestream,
const FileTransferOptions& = FileTransferOptions()) = 0;
- static bool isSupportedBy(const DiscoInfo::ref info);
-
boost::signals2::signal<void (IncomingFileTransfer::ref)> onIncomingFileTransfer;
};
}
diff --git a/Swiften/FileTransfer/FileTransferManagerImpl.cpp b/Swiften/FileTransfer/FileTransferManagerImpl.cpp
index d449179..05dd3bb 100644
--- a/Swiften/FileTransfer/FileTransferManagerImpl.cpp
+++ b/Swiften/FileTransfer/FileTransferManagerImpl.cpp
@@ -1,53 +1,53 @@
/*
* Copyright (c) 2011 Tobias Markmann
* Licensed under the simplified BSD license.
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/
/*
* Copyright (c) 2013-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swiften/FileTransfer/FileTransferManagerImpl.h>
#include <boost/bind.hpp>
#include <boost/cstdint.hpp>
#include <boost/filesystem.hpp>
#include <Swiften/Base/BoostFilesystemVersion.h>
#include <Swiften/Base/Log.h>
#include <Swiften/Base/Path.h>
-#include <Swiften/Base/foreach.h>
#include <Swiften/Disco/EntityCapsProvider.h>
+#include <Swiften/Disco/FeatureOracle.h>
#include <Swiften/Elements/JingleFileTransferFileInfo.h>
#include <Swiften/Elements/Presence.h>
#include <Swiften/FileTransfer/DefaultFileTransferTransporterFactory.h>
#include <Swiften/FileTransfer/IncomingFileTransferManager.h>
#include <Swiften/FileTransfer/OutgoingFileTransferManager.h>
#include <Swiften/FileTransfer/SOCKS5BytestreamProxiesManager.h>
#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
#include <Swiften/FileTransfer/SOCKS5BytestreamServerManager.h>
#include <Swiften/JID/JID.h>
#include <Swiften/Network/ConnectionFactory.h>
#include <Swiften/Network/ConnectionServerFactory.h>
#include <Swiften/Network/HostAddress.h>
#include <Swiften/Network/NATTraverser.h>
#include <Swiften/Presence/PresenceOracle.h>
#include <Swiften/Queries/IQRouter.h>
namespace Swift {
FileTransferManagerImpl::FileTransferManagerImpl(
const JID& ownJID,
JingleSessionManager* jingleSessionManager,
IQRouter* router,
EntityCapsProvider* capsProvider,
PresenceOracle* presOracle,
ConnectionFactory* connectionFactory,
ConnectionServerFactory* connectionServerFactory,
TimerFactory* timerFactory,
DomainNameResolver* domainNameResolver,
NetworkEnvironment* networkEnvironment,
NATTraverser* natTraverser,
@@ -72,115 +72,94 @@ FileTransferManagerImpl::FileTransferManagerImpl(
outgoingFTManager = new OutgoingFileTransferManager(
jingleSessionManager,
iqRouter,
transporterFactory,
timerFactory,
crypto);
incomingFTManager = new IncomingFileTransferManager(
jingleSessionManager,
transporterFactory,
timerFactory,
crypto);
incomingFTManager->onIncomingFileTransfer.connect(onIncomingFileTransfer);
}
FileTransferManagerImpl::~FileTransferManagerImpl() {
delete incomingFTManager;
delete outgoingFTManager;
delete transporterFactory;
delete bytestreamProxy;
delete s5bServerManager;
delete bytestreamRegistry;
}
void FileTransferManagerImpl::start() {
}
void FileTransferManagerImpl::stop() {
s5bServerManager->stop();
}
-boost::optional<JID> FileTransferManagerImpl::highestPriorityJIDSupportingFileTransfer(const JID& bareJID) {
- JID fullReceipientJID;
- int priority = INT_MIN;
-
- //getAllPresence(bareJID) gives you all presences for the bare JID (i.e. all resources) Isode Limited. @ 11:11
- std::vector<Presence::ref> presences = presenceOracle->getAllPresence(bareJID);
-
- //iterate over them
- foreach(Presence::ref pres, presences) {
- if (pres->getPriority() > priority) {
- // look up caps from the jid
- DiscoInfo::ref info = capsProvider->getCaps(pres->getFrom());
- if (isSupportedBy(info)) {
- priority = pres->getPriority();
- fullReceipientJID = pres->getFrom();
- }
- }
- }
-
- return fullReceipientJID.isValid() ? boost::optional<JID>(fullReceipientJID) : boost::optional<JID>();
-}
-
OutgoingFileTransfer::ref FileTransferManagerImpl::createOutgoingFileTransfer(
const JID& to,
const boost::filesystem::path& filepath,
const std::string& description,
std::shared_ptr<ReadBytestream> bytestream,
const FileTransferOptions& config) {
#if BOOST_FILESYSTEM_VERSION == 2 // TODO: Delete this when boost 1.44 becomes a minimum requirement, and we no longer need v2
std::string filename = filepath.filename();
#else
std::string filename = pathToString(filepath.filename());
#endif
boost::uintmax_t sizeInBytes = boost::filesystem::file_size(filepath);
boost::posix_time::ptime lastModified = boost::posix_time::from_time_t(boost::filesystem::last_write_time(filepath));
return createOutgoingFileTransfer(to, filename, description, sizeInBytes, lastModified, bytestream, config);
}
OutgoingFileTransfer::ref FileTransferManagerImpl::createOutgoingFileTransfer(
const JID& to,
const std::string& filename,
const std::string& description,
const boost::uintmax_t sizeInBytes,
const boost::posix_time::ptime& lastModified,
std::shared_ptr<ReadBytestream> bytestream,
const FileTransferOptions& config) {
JingleFileTransferFileInfo fileInfo;
fileInfo.setDate(lastModified);
fileInfo.setSize(sizeInBytes);
fileInfo.setName(filename);
fileInfo.setDescription(description);
JID receipient = to;
if(receipient.isBare()) {
- boost::optional<JID> fullJID = highestPriorityJIDSupportingFileTransfer(receipient);
- if (fullJID.is_initialized()) {
- receipient = fullJID.get();
+ auto featureOracle = FeatureOracle(capsProvider, presenceOracle);
+ JID fullJID = featureOracle.getMostAvailableClientForFileTrasfer(receipient);
+ if (!fullJID.toString().empty()) {
+ receipient = fullJID;
} else {
return OutgoingFileTransfer::ref();
}
}
assert(!iqRouter->getJID().isBare());
DiscoInfo::ref capabilities = capsProvider->getCaps(receipient);
FileTransferOptions options = config;
if (capabilities) {
if (!capabilities->hasFeature(DiscoInfo::JingleTransportsS5BFeature)) {
options = options.withAssistedAllowed(false).withDirectAllowed(false).withProxiedAllowed(false);
}
if (!capabilities->hasFeature(DiscoInfo::JingleTransportsIBBFeature)) {
options = options.withInBandAllowed(false);
}
}
else {
SWIFT_LOG(warning) << "No entity capabilities information for " << receipient.toString() << std::endl;
}
return outgoingFTManager->createOutgoingFileTransfer(iqRouter->getJID(), receipient, bytestream, fileInfo, options);
}
}
diff --git a/Swiften/Presence/PresenceOracle.cpp b/Swiften/Presence/PresenceOracle.cpp
index c2c1152..1c9d0ea 100644
--- a/Swiften/Presence/PresenceOracle.cpp
+++ b/Swiften/Presence/PresenceOracle.cpp
@@ -1,43 +1,42 @@
/*
- * Copyright (c) 2010-2015 Isode Limited.
+ * Copyright (c) 2010-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swiften/Presence/PresenceOracle.h>
#include <queue>
#include <boost/bind.hpp>
-#include <Swiften/Base/foreach.h>
#include <Swiften/Client/StanzaChannel.h>
#include <Swiften/Elements/StatusShow.h>
#include <Swiften/Roster/XMPPRoster.h>
namespace Swift {
PresenceOracle::PresenceOracle(StanzaChannel* stanzaChannel, XMPPRoster* roster) : stanzaChannel_(stanzaChannel), xmppRoster_(roster) {
stanzaChannel_->onPresenceReceived.connect(boost::bind(&PresenceOracle::handleIncomingPresence, this, _1));
stanzaChannel_->onAvailableChanged.connect(boost::bind(&PresenceOracle::handleStanzaChannelAvailableChanged, this, _1));
xmppRoster_->onJIDRemoved.connect(boost::bind(&PresenceOracle::handleJIDRemoved, this, _1));
}
PresenceOracle::~PresenceOracle() {
stanzaChannel_->onPresenceReceived.disconnect(boost::bind(&PresenceOracle::handleIncomingPresence, this, _1));
stanzaChannel_->onAvailableChanged.disconnect(boost::bind(&PresenceOracle::handleStanzaChannelAvailableChanged, this, _1));
xmppRoster_->onJIDRemoved.disconnect(boost::bind(&PresenceOracle::handleJIDRemoved, this, _1));
}
void PresenceOracle::handleStanzaChannelAvailableChanged(bool available) {
if (available) {
entries_.clear();
}
}
void PresenceOracle::handleIncomingPresence(Presence::ref presence) {
JID bareJID(presence->getFrom().toBare());
if (presence->getType() == Presence::Subscribe) {
}
else {
Presence::ref passedPresence = presence;
@@ -74,146 +73,142 @@ void PresenceOracle::handleJIDRemoved(const JID& removedJID) {
if (entries_.find(removedJID) != entries_.end()) {
entries_[removedJID].clear();
entries_[removedJID][removedJID] = unavailablePresence;
}
onPresenceChange(unavailablePresence);
}
Presence::ref PresenceOracle::getLastPresence(const JID& jid) const {
PresencesMap::const_iterator i = entries_.find(jid.toBare());
if (i == entries_.end()) {
return Presence::ref();
}
PresenceMap presenceMap = i->second;
PresenceMap::const_iterator j = presenceMap.find(jid);
if (j != presenceMap.end()) {
return j->second;
}
else {
return Presence::ref();
}
}
std::vector<Presence::ref> PresenceOracle::getAllPresence(const JID& bareJID) const {
std::vector<Presence::ref> results;
PresencesMap::const_iterator i = entries_.find(bareJID);
if (i == entries_.end()) {
return results;
}
- PresenceMap presenceMap = i->second;
- PresenceMap::const_iterator j = presenceMap.begin();
- for (; j != presenceMap.end(); ++j) {
- Presence::ref current = j->second;
- results.push_back(current);
+ for (const auto& jidPresence : i->second) {
+ if (jidPresence.second) {
+ results.push_back(jidPresence.second);
+ }
}
return results;
}
struct PresenceAccountCmp {
static int preferenceFromStatusShow(StatusShow::Type showType) {
switch (showType) {
case StatusShow::FFC:
return 5;
case StatusShow::Online:
return 4;
case StatusShow::DND:
return 3;
case StatusShow::Away:
return 2;
case StatusShow::XA:
return 1;
case StatusShow::None:
return 0;
}
assert(false);
return -1;
}
bool operator()(const Presence::ref& a, const Presence::ref& b) {
int aPreference = preferenceFromStatusShow(a->getShow());
int bPreference = preferenceFromStatusShow(b->getShow());
if (aPreference != bPreference) {
return aPreference < bPreference;
}
if (a->getPriority() != b->getPriority()) {
return a->getPriority() < b->getPriority();
}
return a->getFrom().getResource() < b->getFrom().getResource();
}
};
typedef std::priority_queue<Presence::ref, std::vector<Presence::ref>, PresenceAccountCmp> PresenceAccountPriorityQueue;
Presence::ref PresenceOracle::getActivePresence(const std::vector<Presence::ref> presences) {
Presence::ref accountPresence;
PresenceAccountPriorityQueue online;
PresenceAccountPriorityQueue away;
PresenceAccountPriorityQueue offline;
- foreach(Presence::ref presence, presences) {
+ for (auto&& presence : presences) {
switch (presence->getShow()) {
case StatusShow::Online:
online.push(presence);
break;
case StatusShow::Away:
away.push(presence);
break;
case StatusShow::FFC:
online.push(presence);
break;
case StatusShow::XA:
away.push(presence);
break;
case StatusShow::DND:
away.push(presence);
break;
case StatusShow::None:
offline.push(presence);
break;
}
}
if (!online.empty()) {
accountPresence = online.top();
}
else if (!away.empty()) {
accountPresence = away.top();
}
else if (!offline.empty()) {
accountPresence = offline.top();
}
return accountPresence;
}
Presence::ref PresenceOracle::getAccountPresence(const JID& jid) const {
Presence::ref accountPresence;
std::vector<Presence::ref> allPresences = getAllPresence(jid.toBare());
accountPresence = getActivePresence(allPresences);
return accountPresence;
}
Presence::ref PresenceOracle::getHighestPriorityPresence(const JID& bareJID) const {
PresencesMap::const_iterator i = entries_.find(bareJID);
if (i == entries_.end()) {
return Presence::ref();
}
- PresenceMap presenceMap = i->second;
- PresenceMap::const_iterator j = presenceMap.begin();
Presence::ref highest;
- for (; j != presenceMap.end(); ++j) {
- Presence::ref current = j->second;
+ for (const auto& jidPresence : i->second) {
+ Presence::ref current = jidPresence.second;
if (!highest
|| current->getPriority() > highest->getPriority()
|| (current->getPriority() == highest->getPriority()
&& StatusShow::typeToAvailabilityOrdering(current->getShow()) > StatusShow::typeToAvailabilityOrdering(highest->getShow()))) {
highest = current;
}
-
}
return highest;
}
}
diff --git a/Swiften/SConscript b/Swiften/SConscript
index eb7ae19..a8fb88a 100644
--- a/Swiften/SConscript
+++ b/Swiften/SConscript
@@ -360,63 +360,64 @@ if env["SCONS_STAGE"] == "build" :
os.unlink(str(target[0]))
os.symlink(source[0].get_contents(), str(target[0]))
for alias in myenv["SWIFTEN_LIBRARY_ALIASES"] :
myenv.Command(myenv.File(alias), [myenv.Value(swiften_lib[0].name), swiften_lib[0]], symlink)
env.Append(UNITTEST_SOURCES = [
File("Avatars/UnitTest/VCardUpdateAvatarManagerTest.cpp"),
File("Avatars/UnitTest/VCardAvatarManagerTest.cpp"),
File("Avatars/UnitTest/CombinedAvatarProviderTest.cpp"),
File("Avatars/UnitTest/AvatarManagerImplTest.cpp"),
File("Base/UnitTest/IDGeneratorTest.cpp"),
File("Base/UnitTest/SimpleIDGeneratorTest.cpp"),
File("Base/UnitTest/StringTest.cpp"),
File("Base/UnitTest/DateTimeTest.cpp"),
File("Base/UnitTest/ByteArrayTest.cpp"),
File("Base/UnitTest/URLTest.cpp"),
File("Base/UnitTest/PathTest.cpp"),
File("Chat/UnitTest/ChatStateNotifierTest.cpp"),
# File("Chat/UnitTest/ChatStateTrackerTest.cpp"),
File("Client/UnitTest/ClientSessionTest.cpp"),
File("Client/UnitTest/NickResolverTest.cpp"),
File("Client/UnitTest/ClientBlockListManagerTest.cpp"),
File("Client/UnitTest/BlockListImplTest.cpp"),
File("Compress/UnitTest/ZLibCompressorTest.cpp"),
File("Compress/UnitTest/ZLibDecompressorTest.cpp"),
File("Component/UnitTest/ComponentHandshakeGeneratorTest.cpp"),
File("Component/UnitTest/ComponentConnectorTest.cpp"),
File("Component/UnitTest/ComponentSessionTest.cpp"),
File("Disco/UnitTest/CapsInfoGeneratorTest.cpp"),
File("Disco/UnitTest/CapsManagerTest.cpp"),
+ File("Disco/UnitTest/DiscoInfoResponderTest.cpp"),
File("Disco/UnitTest/EntityCapsManagerTest.cpp"),
+ File("Disco/UnitTest/FeatureOracleTest.cpp"),
File("Disco/UnitTest/JIDDiscoInfoResponderTest.cpp"),
- File("Disco/UnitTest/DiscoInfoResponderTest.cpp"),
File("Elements/UnitTest/IQTest.cpp"),
File("Elements/UnitTest/StanzaTest.cpp"),
File("Elements/UnitTest/FormTest.cpp"),
File("EventLoop/UnitTest/EventLoopTest.cpp"),
File("EventLoop/UnitTest/SimpleEventLoopTest.cpp"),
# File("History/UnitTest/SQLiteHistoryManagerTest.cpp"),
File("JID/UnitTest/JIDTest.cpp"),
File("LinkLocal/UnitTest/LinkLocalConnectorTest.cpp"),
File("LinkLocal/UnitTest/LinkLocalServiceBrowserTest.cpp"),
File("LinkLocal/UnitTest/LinkLocalServiceInfoTest.cpp"),
File("LinkLocal/UnitTest/LinkLocalServiceTest.cpp"),
File("MUC/UnitTest/MUCTest.cpp"),
File("MUC/UnitTest/MockMUC.cpp"),
File("Network/UnitTest/HostAddressTest.cpp"),
File("Network/UnitTest/ConnectorTest.cpp"),
File("Network/UnitTest/ChainedConnectorTest.cpp"),
File("Network/UnitTest/DomainNameServiceQueryTest.cpp"),
File("Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp"),
File("Network/UnitTest/BOSHConnectionTest.cpp"),
File("Network/UnitTest/BOSHConnectionPoolTest.cpp"),
File("Parser/PayloadParsers/UnitTest/BlockParserTest.cpp"),
File("Parser/PayloadParsers/UnitTest/BodyParserTest.cpp"),
File("Parser/PayloadParsers/UnitTest/DiscoInfoParserTest.cpp"),
File("Parser/PayloadParsers/UnitTest/DiscoItemsParserTest.cpp"),
File("Parser/PayloadParsers/UnitTest/ErrorParserTest.cpp"),
File("Parser/PayloadParsers/UnitTest/FormParserTest.cpp"),
File("Parser/PayloadParsers/UnitTest/CommandParserTest.cpp"),
File("Parser/PayloadParsers/UnitTest/PriorityParserTest.cpp"),
File("Parser/PayloadParsers/UnitTest/RawXMLPayloadParserTest.cpp"),
File("Parser/PayloadParsers/UnitTest/ResourceBindParserTest.cpp"),