Changeset - fbfb07e8ba22
[Not reviewed]
0 2 0
Jan Kaluza - 10 years ago 2016-02-19 15:14:49
jkaluza@redhat.com
Libtransport: Try to leave only MUC conversations when disconnecting the user
2 files changed with 24 insertions and 1 deletions:
0 comments (0 inline, 0 general)
libtransport/ConversationManager.cpp
Show inline comments
 
@@ -12,151 +12,155 @@
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "transport/ConversationManager.h"
 
#include "transport/Conversation.h"
 
#include "transport/Buddy.h"
 
#include "transport/Factory.h"
 
#include "transport/User.h"
 
#include "transport/Logging.h"
 
#include "transport/Transport.h"
 
#include "Swiften/Roster/SetRosterRequest.h"
 
#include "Swiften/Elements/RosterPayload.h"
 
#include "Swiften/Elements/RosterItemPayload.h"
 

	
 
namespace Transport {
 

	
 
DEFINE_LOGGER(logger, "ConversationManager");
 

	
 
ConversationManager::ConversationManager(User *user, Component *component){
 
	m_user = user;
 
	m_component = component;
 
}
 

	
 
ConversationManager::~ConversationManager() {
 
	deleteAllConversations();
 
}
 

	
 
void ConversationManager::sendCachedChatMessages() {
 
	for (std::map<std::string, Conversation *>::const_iterator it = m_convs.begin(); it != m_convs.end(); it++) {
 
		if ((*it).second->isMUC()) {
 
			continue;
 
		}
 
		(*it).second->sendCachedMessages();
 
	}
 
}
 

	
 
void ConversationManager::deleteAllConversations() {
 
	while(!m_convs.empty()) {
 
		LOG4CXX_INFO(logger, m_user->getJID().toString() << ": Removing conversation " << (*m_convs.begin()).first);
 
		(*m_convs.begin()).second->destroyRoom();
 
		delete (*m_convs.begin()).second;
 
		m_convs.erase(m_convs.begin());
 
	}
 
}
 

	
 
Conversation *ConversationManager::getConversation(const std::string &name) {
 
	if (m_convs.find(name) != m_convs.end())
 
		return m_convs[name];
 

	
 
	if (name.find("/") == std::string::npos) {
 
		return NULL;
 
	}
 

	
 
	// handle PMs
 
	std::string room = name.substr(0, name.find("/"));
 
	std::string nick = name.substr(name.find("/") + 1);
 

	
 
	if (getConversation(room) == NULL) {
 
		return NULL;
 
	}
 

	
 
	return getConversation(nick);
 
}
 

	
 
void ConversationManager::addConversation(Conversation *conv) {
 
	m_convs[conv->getLegacyName()] = conv;
 
	LOG4CXX_INFO(logger, m_user->getJID().toString() << ": Adding conversation " << conv->getLegacyName());
 
}
 

	
 
void ConversationManager::removeConversation(Conversation *conv) {
 
	for (std::map<std::string, Conversation *>::const_iterator it = m_convs.begin(); it != m_convs.end(); it++) {
 
		if ((*it).second->getRoom() == conv->getLegacyName()) {
 
			(*it).second->setRoom("");
 
		}
 
	}
 
	m_convs.erase(conv->getLegacyName());
 
}
 

	
 
void ConversationManager::resetResources() {
 
	for (std::map<std::string, Conversation *>::const_iterator it = m_convs.begin(); it != m_convs.end(); it++) {
 
		if ((*it).second->isMUC()) {
 
			continue;
 
		}
 
		(*it).second->setJID(m_user->getJID().toBare());
 
	}
 
}
 

	
 
void ConversationManager::removeJID(const Swift::JID &jid) {
 
	std::vector<std::string> toRemove;
 
	for (std::map<std::string, Conversation *>::const_iterator it = m_convs.begin(); it != m_convs.end(); it++) {
 
		if (it->first.empty() || !it->second) {
 
			continue;
 
		}
 

	
 
		(*it).second->removeJID(jid);
 
		if (it->second->getJIDs().empty()) {
 
		if (it->second->getJIDs().empty() && (*it).second->isMUC()) {
 
			toRemove.push_back(it->first);
 
		}
 
	}
 

	
 
	if (m_user->getUserSetting("stay_connected") != "1") {
 
		while(!toRemove.empty()) {
 
			LOG4CXX_INFO(logger, m_user->getJID().toString() << ": Leaving room " << toRemove.back() << ".");
 
			m_user->leaveRoom(toRemove.back());
 
			toRemove.pop_back();
 
		}
 
	}
 
}
 

	
 
void ConversationManager::clearJIDs() {
 
	for (std::map<std::string, Conversation *>::const_iterator it = m_convs.begin(); it != m_convs.end(); it++) {
 
		(*it).second->clearJIDs();
 
	}
 
}
 

	
 
void ConversationManager::handleMessageReceived(Swift::Message::ref message) {
 
// 	std::string name = message->getTo().getUnescapedNode();
 
// 	if (name.find_last_of("%") != std::string::npos) { // OK when commented
 
// 		name.replace(name.find_last_of("%"), 1, "@"); // OK when commented
 
// 	}
 
	std::string name = Buddy::JIDToLegacyName(message->getTo(), m_user);
 
	if (name.empty()) {
 
		LOG4CXX_WARN(logger, m_user->getJID().toString() << ": Tried to create empty conversation");
 
		return;
 
	}
 

	
 
	// create conversation if it does not exist.
 
	if (!m_convs[name]) {
 
		Conversation *conv = m_component->getFactory()->createConversation(this, name);
 
		addConversation(conv);
 
	}
 
	// if it exists and it's MUC, but this message is PM, get PM conversation or create new one.
 
	else if (m_convs[name]->isMUC() && message->getType() != Swift::Message::Groupchat) {
 
		std::string room_name = name;
 
		name = room_name + "/" + message->getTo().getResource();
 
		if (m_convs.find(name) == m_convs.end()) {
 
			Conversation *conv = m_component->getFactory()->createConversation(this, message->getTo().getResource());
 
			conv->setRoom(room_name);
 
			conv->setNickname(name);
 
			addConversation(conv);
 
		}
 
	}
 

	
 
	// update resource and send the message
 
	m_convs[name]->setJID(message->getFrom());
 
	m_convs[name]->sendMessage(message);
 
}
 

	
 
}
tests/libtransport/user.cpp
Show inline comments
 
#include <cppunit/TestFixture.h>
 
#include <cppunit/extensions/HelperMacros.h>
 
#include <Swiften/Swiften.h>
 
#include <Swiften/EventLoop/DummyEventLoop.h>
 
#include <Swiften/Server/Server.h>
 
#include <Swiften/Network/DummyNetworkFactories.h>
 
#include <Swiften/Network/DummyConnectionServer.h>
 
#include "Swiften/Server/ServerStanzaChannel.h"
 
#include "Swiften/Server/ServerFromClientSession.h"
 
#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h"
 
#include "basictest.h"
 

	
 
using namespace Transport;
 

	
 
#if !HAVE_SWIFTEN_3
 
#define get_value_or(X) substr()
 
#endif
 

	
 
class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest {
 
	CPPUNIT_TEST_SUITE(UserTest);
 
	CPPUNIT_TEST(sendCurrentPresence);
 
    CPPUNIT_TEST(handlePresence);
 
	CPPUNIT_TEST(handlePresenceJoinRoom);
 
	CPPUNIT_TEST(handlePresenceJoinRoomTwoResources);
 
// 	CPPUNIT_TEST(handlePresenceLeaveRoom); // tested as part of other tests
 
	CPPUNIT_TEST(handlePresenceLeaveRoomTwoResources);
 
	CPPUNIT_TEST(handlePresenceLeaveRoomTwoResourcesOneDisconnects);
 
	CPPUNIT_TEST(handlePresenceLeaveRoomBouncer);
 
	CPPUNIT_TEST(handlePresenceLeaveRoomTwoResourcesBouncer);
 
	CPPUNIT_TEST(handlePresenceLeaveRoomTwoResourcesOneDisconnectsBouncer);
 
	CPPUNIT_TEST(handlePresenceLeaveRoomTwoResourcesAnotherOneDisconnects);
 
	CPPUNIT_TEST(leaveJoinedRoom);
 
	CPPUNIT_TEST(doNotLeaveNormalChat);
 
	CPPUNIT_TEST(joinRoomBeforeConnected);
 
	CPPUNIT_TEST(handleDisconnected);
 
	CPPUNIT_TEST(handleDisconnectedReconnect);
 
	CPPUNIT_TEST(joinRoomHandleDisconnectedRejoin);
 
	CPPUNIT_TEST(joinRoomAfterFlagNotAuthorized);
 
	CPPUNIT_TEST(requestVCard);
 
	CPPUNIT_TEST_SUITE_END();
 

	
 
	public:
 
		std::string room;
 
		std::string roomNickname;
 
		std::string roomPassword;
 
		std::string photo;
 
		bool readyToConnect;
 
		bool disconnected;
 
		Swift::Presence::ref changedPresence;
 

	
 
		void setUp (void) {
 
			disconnected = false;
 
			readyToConnect = false;
 
			changedPresence = Swift::Presence::ref();
 
			room = "";
 
			roomNickname = "";
 
			roomPassword = "";
 
			photo = "";
 

	
 
			setMeUp();
 
			userManager->onUserCreated.connect(boost::bind(&UserTest::handleUserCreated, this, _1));
 
			connectUser();
 
			received.clear();
 

	
 
			frontend->onVCardUpdated.connect(boost::bind(&UserTest::handleVCardUpdated, this, _1, _2));
 
		}
 

	
 
		void tearDown (void) {
 
			received.clear();
 
			if (!disconnected) {
 
				disconnectUser();
 
			}
 
			userManager->removeAllUsers();
 
			tearMeDown();
 
		}
 

	
 
	void handleVCardUpdated(User *user, boost::shared_ptr<Swift::VCard> v) {
 
		photo = Swift::byteArrayToString(v->getPhoto());
 
	}
 

	
 
	void handleUserCreated(User *user) {
 
		user->onReadyToConnect.connect(boost::bind(&UserTest::handleUserReadyToConnect, this, user));
 
		user->onPresenceChanged.connect(boost::bind(&UserTest::handleUserPresenceChanged, this, user, _1));
 
		user->onRoomJoined.connect(boost::bind(&UserTest::handleRoomJoined, this, user, _1, _2, _3, _4));
 
		user->onRoomLeft.connect(boost::bind(&UserTest::handleRoomLeft, this, user, _1));
 
	}
 

	
 
	void handleUserReadyToConnect(User *user) {
 
		readyToConnect = true;
 
	}
 

	
 
	void handleUserPresenceChanged(User *user, Swift::Presence::ref presence) {
 
		changedPresence = presence;
 
	}
 

	
 
	void handleRoomJoined(User *user, const std::string &jid, const std::string &r, const std::string &nickname, const std::string &password) {
 
		room = r;
 
		roomNickname = nickname;
 
		roomPassword = password;
 
	}
 

	
 
	void handleRoomLeft(User *user, const std::string &r) {
 
		room = r;
 
	}
 

	
 

	
 
	void sendCurrentPresence() {
 
		User *user = userManager->getUser("user@localhost");
 
		user->sendCurrentPresence();
 

	
 
		// We're not forwarding current presence in server-mode
 
		CPPUNIT_ASSERT_EQUAL(0, (int) received.size());
 
	}
 

	
 
	void handlePresence() {
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setTo("localhost");
 
		response->setFrom("user@localhost/resource");
 
		response->setShow(Swift::StatusShow::Away);
 

	
 
		injectPresence(response);
 
		loop->processEvents();
 

	
 
		// no presence received in server mode, just disco#info
 
		CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
 
		CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::DiscoInfo>());
 

	
 
		CPPUNIT_ASSERT(changedPresence);
 
		CPPUNIT_ASSERT_EQUAL(Swift::StatusShow::Away, changedPresence->getShow());
 
@@ -327,192 +328,210 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest {
 
		response->setType(Swift::Presence::Unavailable);
 

	
 
		Swift::MUCPayload *payload = new Swift::MUCPayload();
 
		payload->setPassword("password");
 
		response->addPayload(boost::shared_ptr<Swift::Payload>(payload));
 
		injectPresence(response);
 
		loop->processEvents();
 

	
 
		CPPUNIT_ASSERT_EQUAL(0, (int) received.size());
 

	
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), room);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword);
 
	}
 

	
 
	void handlePresenceLeaveRoomTwoResourcesBouncer() {
 
		User *user = userManager->getUser("user@localhost");
 
		user->addUserSetting("stay_connected", "1");
 
		handlePresenceJoinRoomTwoResources();
 
		received.clear();
 

	
 
		// User is still connected from resource2, so he should not leave the room
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setTo("room@localhost/hanzz");
 
		response->setFrom("user@localhost/resource");
 
		response->setType(Swift::Presence::Unavailable);
 

	
 
		Swift::MUCPayload *payload = new Swift::MUCPayload();
 
		payload->setPassword("password");
 
		response->addPayload(boost::shared_ptr<Swift::Payload>(payload));
 
		injectPresence(response);
 
		loop->processEvents();
 

	
 
		CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
 

	
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), room);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword);
 

	
 
		room = "something";
 
		// disconnect also from resource
 
		// User is still connected from resource2, so he should not leave the room
 
		response = Swift::Presence::create();
 
		response->setTo("room@localhost/hanzz");
 
		response->setFrom("user@localhost/resource2");
 
		response->setType(Swift::Presence::Unavailable);
 

	
 
		payload = new Swift::MUCPayload();
 
		payload->setPassword("password");
 
		response->addPayload(boost::shared_ptr<Swift::Payload>(payload));
 
		injectPresence(response);
 
		loop->processEvents();
 

	
 
		CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
 

	
 
		CPPUNIT_ASSERT_EQUAL(std::string("something"), room);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword);
 
	}
 

	
 
	void handlePresenceLeaveRoomTwoResourcesOneDisconnectsBouncer() {
 
		room = "something";
 
		handlePresenceJoinRoomTwoResources();
 
		received.clear();
 
		User *user = userManager->getUser("user@localhost");
 

	
 
		// User is still connected from resource2, so he should not leave the room
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setTo("localhost/hanzz");
 
		response->setFrom("user@localhost/resource");
 
		response->setType(Swift::Presence::Unavailable);
 
		injectPresence(response);
 
		loop->processEvents();
 

	
 

	
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), room);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword);
 

	
 
		Conversation *conv = user->getConversationManager()->getConversation("room");
 
		CPPUNIT_ASSERT_EQUAL(1, (int) conv->getJIDs().size());
 
		CPPUNIT_ASSERT_EQUAL(Swift::JID("user@localhost/resource2"), conv->getJIDs().front());
 
	}
 

	
 
	void leaveJoinedRoom() {
 
		User *user = userManager->getUser("user@localhost");
 
		handlePresenceJoinRoom();
 

	
 
		CPPUNIT_ASSERT(user->getConversationManager()->getConversation("room"));
 

	
 
		received.clear();
 
		handlePresenceLeaveRoom();
 

	
 
		CPPUNIT_ASSERT(!user->getConversationManager()->getConversation("room"));
 
	}
 

	
 
	void doNotLeaveNormalChat() {
 
		User *user = userManager->getUser("user@localhost");
 

	
 
		TestingConversation *conv = new TestingConversation(user->getConversationManager(), "buddy1@test");
 
		user->getConversationManager()->addConversation(conv);
 

	
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), room);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword);
 

	
 
		user->getConversationManager()->removeJID("user@localhost/resource");
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), room);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword);
 
		Conversation *cv = user->getConversationManager()->getConversation("buddy1@test");
 
		CPPUNIT_ASSERT(cv);
 
	}
 

	
 
	void handleDisconnected() {
 
		User *user = userManager->getUser("user@localhost");
 
		user->handleDisconnected("Connection error", Swift::SpectrumErrorPayload::CONNECTION_ERROR_AUTHENTICATION_FAILED);
 
		loop->processEvents();
 

	
 
		CPPUNIT_ASSERT(streamEnded);
 
		user = userManager->getUser("user@localhost");
 
		CPPUNIT_ASSERT(!user);
 

	
 
		CPPUNIT_ASSERT_EQUAL(2, (int) received.size());
 
		Swift::Message *m = dynamic_cast<Swift::Message *>(getStanza(received[0]));
 
		CPPUNIT_ASSERT_EQUAL(std::string("Connection error"), m->getBody().get_value_or(""));
 

	
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::StreamError *>(received[1].get()));
 
		CPPUNIT_ASSERT_EQUAL(std::string("Connection error"), dynamic_cast<Swift::StreamError *>(received[1].get())->getText());
 

	
 
		disconnected = true;
 
	}
 

	
 
	void handleDisconnectedReconnect() {
 
		readyToConnect = false;
 
		User *user = userManager->getUser("user@localhost");
 
		user->handleDisconnected("Connection error");
 
		loop->processEvents();
 

	
 
		CPPUNIT_ASSERT(!streamEnded);
 
		user = userManager->getUser("user@localhost");
 
		CPPUNIT_ASSERT(user);
 
		CPPUNIT_ASSERT(readyToConnect);
 

	
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setTo("localhost");
 
		response->setFrom("user@localhost/resource");
 
		injectPresence(response);
 
		loop->processEvents();
 
	}
 

	
 
	void joinRoomBeforeConnected() {
 
		User *user = userManager->getUser("user@localhost");
 
		user->setConnected(false);
 

	
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setTo("room@localhost/hanzz");
 
		response->setFrom("user@localhost/resource");
 

	
 
		Swift::MUCPayload *payload = new Swift::MUCPayload();
 
		payload->setPassword("password");
 
		response->addPayload(boost::shared_ptr<Swift::Payload>(payload));
 
		injectPresence(response);
 
		loop->processEvents();
 

	
 
		CPPUNIT_ASSERT_EQUAL(0, (int) received.size());
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), room);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword);
 

	
 
		user->setConnected(true);
 
		CPPUNIT_ASSERT_EQUAL(std::string("room"), room);
 
		CPPUNIT_ASSERT_EQUAL(std::string("hanzz"), roomNickname);
 
		CPPUNIT_ASSERT_EQUAL(std::string("password"), roomPassword);
 
	}
 

	
 
	void joinRoomHandleDisconnectedRejoin() {
 
		User *user = userManager->getUser("user@localhost");
 
		handlePresenceJoinRoom();
 
		handleDisconnectedReconnect();
 
		room = "";
 
		roomNickname = "";
 
		roomPassword = "";
 
		received.clear();
 
		user->setConnected(true);
 

	
 
		CPPUNIT_ASSERT_EQUAL(std::string("room"), room);
 
		CPPUNIT_ASSERT_EQUAL(std::string("hanzz"), roomNickname);
 
		CPPUNIT_ASSERT_EQUAL(std::string("password"), roomPassword);
 
		CPPUNIT_ASSERT_EQUAL(0, (int) received.size());
 
	}
 

	
 
	void joinRoomAfterFlagNotAuthorized() {
 
		User *user = userManager->getUser("user@localhost");
 
		handlePresenceJoinRoom();
 

	
 
		Conversation *conv = user->getConversationManager()->getConversation("room");
 
		conv->handleParticipantChanged("hanzz", Conversation::PARTICIPANT_FLAG_NOT_AUTHORIZED, Swift::StatusShow::Away, "my status message");
 
		loop->processEvents();
 

	
 
		CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::Presence *>(getStanza(received[0])));
 
		CPPUNIT_ASSERT_EQUAL(Swift::Presence::Error, dynamic_cast<Swift::Presence *>(getStanza(received[0]))->getType());
 
		CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::ErrorPayload>());
 
		CPPUNIT_ASSERT_EQUAL(Swift::ErrorPayload::NotAuthorized, getStanza(received[0])->getPayload<Swift::ErrorPayload>()->getCondition());
 

	
 
		received.clear();
 
		handlePresenceJoinRoom();
 
	}
 

	
0 comments (0 inline, 0 general)