Changeset - 0b0e020cdf91
[Not reviewed]
0 13 0
Jan Kaluza - 13 years ago 2012-12-18 10:59:52
hanzz.k@gmail.com
Add general API to set per user settings using adhoc commands which can be defaulted from config file. Add 'Send messages as headlines' Transport setting
13 files changed with 196 insertions and 8 deletions:
0 comments (0 inline, 0 general)
include/transport/adhoccommandfactory.h
Show inline comments
 
@@ -41,9 +41,16 @@ class AdHocCommandFactory {
 

	
 
		virtual AdHocCommand *createAdHocCommand(Component *component, UserManager *userManager, StorageBackend *storageBackend, const Swift::JID &initiator, const Swift::JID &to) = 0;
 

	
 
		virtual std::string getNode() = 0;
 

	
 
		virtual std::string getName() = 0;
 

	
 
		virtual std::map<std::string, std::string> &getUserSettings() {
 
			return m_userSettings;
 
		}
 

	
 
	protected:
 
		std::map<std::string, std::string> m_userSettings;
 
};
 

	
 
}
include/transport/adhocmanager.h
Show inline comments
 
@@ -63,12 +63,14 @@ class AdHocManager : public Swift::Responder<Swift::Command> {
 
		void removeOldSessions();
 

	
 

	
 
	private:
 
		virtual bool handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::Command> payload);
 
		virtual bool handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::Command> payload);
 
		
 
		void handleUserCreated(User *user);
 

	
 
		Component *m_component;
 
		DiscoItemsResponder *m_discoItemsResponder;
 
		std::map<std::string, AdHocCommandFactory *> m_factories;
 
		SessionsMap m_sessions;
 
		Swift::Timer::ref m_collectTimer;
include/transport/protocol.proto
Show inline comments
 
@@ -67,12 +67,13 @@ message ConversationMessage {
 
	required string userName = 1;
 
	required string buddyName = 2;
 
	required string message = 3;
 
	optional string nickname = 4;
 
	optional string xhtml = 5;
 
	optional string timestamp = 6;
 
	optional bool headline = 7;
 
}
 

	
 
message Room {
 
	required string userName = 1;
 
	required string nickname = 2;
 
	required string room = 3;
include/transport/settingsadhoccommand.h
Show inline comments
 
@@ -50,13 +50,16 @@ class SettingsAdHocCommand : public AdHocCommand {
 
		boost::shared_ptr<Swift::Command> handleResponse(boost::shared_ptr<Swift::Command> payload);
 
		State m_state;
 
};
 

	
 
class SettingsAdHocCommandFactory : public AdHocCommandFactory {
 
	public:
 
		SettingsAdHocCommandFactory() {}
 
		SettingsAdHocCommandFactory() {
 
			m_userSettings["send_headlines"] = "0";
 
		}
 

	
 
		virtual ~SettingsAdHocCommandFactory() {}
 

	
 
		AdHocCommand *createAdHocCommand(Component *component, UserManager *userManager, StorageBackend *storageBackend, const Swift::JID &initiator, const Swift::JID &to) {
 
			return new SettingsAdHocCommand(component, userManager, storageBackend, initiator, to);
 
		}
 

	
include/transport/user.h
Show inline comments
 
@@ -113,12 +113,20 @@ class User : public Swift::EntityCapsProvider {
 
		}
 

	
 
		int getResourceCount() {
 
			return m_resources;
 
		}
 

	
 
		void addUserSetting(const std::string &key, const std::string &value) {
 
			m_settings[key] = value;
 
		}
 

	
 
		const std::string &getUserSetting(const std::string &key) {
 
			return m_settings[key];
 
		}
 

	
 
		boost::signal<void ()> onReadyToConnect;
 
		boost::signal<void (Swift::Presence::ref presence)> onPresenceChanged;
 
		boost::signal<void (const Swift::JID &who, const std::string &room, const std::string &nickname, const std::string &password)> onRoomJoined;
 
		boost::signal<void (const std::string &room)> onRoomLeft;
 
		boost::signal<void ()> onDisconnected;
 

	
 
@@ -142,9 +150,10 @@ class User : public Swift::EntityCapsProvider {
 
		time_t m_lastActivity;
 
		std::map<Swift::JID, Swift::DiscoInfo::ref> m_legacyCaps;
 
		std::vector<boost::shared_ptr<Swift::OutgoingFileTransfer> > m_filetransfers;
 
		int m_resources;
 
		int m_reconnectCounter;
 
		std::list<Swift::Presence::ref> m_joinedRooms;
 
		std::map<std::string, std::string> m_settings;
 
};
 

	
 
}
src/adhocmanager.cpp
Show inline comments
 
@@ -39,12 +39,14 @@ AdHocManager::AdHocManager(Component *component, DiscoItemsResponder *discoItems
 
	m_userManager = userManager;
 
	m_storageBackend = storageBackend;
 

	
 
	m_collectTimer = m_component->getNetworkFactories()->getTimerFactory()->createTimer(20);
 
	m_collectTimer->onTick.connect(boost::bind(&AdHocManager::removeOldSessions, this));
 
	m_collectTimer->start();
 

	
 
	m_userManager->onUserCreated.connect(boost::bind(&AdHocManager::handleUserCreated, this, _1));
 
}
 

	
 
AdHocManager::~AdHocManager() {
 
	m_collectTimer->stop();
 
	stop();
 
}
 
@@ -63,12 +65,27 @@ void AdHocManager::stop() {
 
		}
 
	}
 

	
 
	m_sessions.clear();
 
}
 

	
 
void AdHocManager::handleUserCreated(User *user) {
 
	if (!m_storageBackend) {
 
		return;
 
	}
 

	
 
	for (std::map<std::string, AdHocCommandFactory *>::const_iterator it = m_factories.begin(); it != m_factories.end(); it++) {
 
		for (std::map<std::string, std::string>::const_iterator it2 = it->second->getUserSettings().begin(); it2 != it->second->getUserSettings().end(); it2++) {
 
			std::string value = CONFIG_STRING_DEFAULTED(m_component->getConfig(), it->second->getNode() + "." + it2->first, it2->second);
 
			int type = (int) TYPE_BOOLEAN;
 
			m_storageBackend->getUserSetting(user->getUserInfo().id, it2->first, type, value);
 
			user->addUserSetting(it2->first, value);
 
		}
 
	}
 
}
 

	
 
void AdHocManager::addAdHocCommand(AdHocCommandFactory *factory) {
 
	if (m_factories.find(factory->getNode()) != m_factories.end()) {
 
		LOG4CXX_ERROR(logger, "Command with node " << factory->getNode() << " is already registered. Ignoring this attempt.");
 
		return;
 
	}
 

	
 
@@ -156,12 +173,18 @@ bool AdHocManager::handleSetRequest(const Swift::JID& from, const Swift::JID& to
 
	sendResponse(from, id, response);
 

	
 
	command->refreshLastActivity();
 

	
 
	// Command completed, so we can remove it now
 
	if (response->getStatus() == Swift::Command::Completed || response->getStatus() == Swift::Command::Canceled) {
 
		// update userSettings map if user is already connected
 
		User *user = m_userManager->getUser(from.toBare().toString());
 
		if (user) {
 
			handleUserCreated(user);
 
		}
 

	
 
		m_sessions[from].erase(command->getId());
 
		if (m_sessions[from].empty()) {
 
			m_sessions.erase(from);
 
		}
 
		delete command;
 
	}
src/conversation.cpp
Show inline comments
 
@@ -79,13 +79,20 @@ void Conversation::setRoom(const std::string &room) {
 

	
 
void Conversation::handleMessage(boost::shared_ptr<Swift::Message> &message, const std::string &nickname) {
 
	if (m_muc) {
 
		message->setType(Swift::Message::Groupchat);
 
	}
 
	else {
 
		message->setType(Swift::Message::Chat);
 
		if (message->getType() != Swift::Message::Headline) {
 
			if (m_conversationManager->getUser()->getUserSetting("send_headlines") == "1") {
 
				message->setType(Swift::Message::Headline);
 
			}
 
			else {
 
				message->setType(Swift::Message::Chat);
 
			}
 
		}
 
	}
 

	
 
	std::string n = nickname;
 
	if (n.empty() && !m_room.empty() && !m_muc) {
 
		n = m_nickname;
 
	}
src/conversationmanager.cpp
Show inline comments
 
@@ -36,18 +36,13 @@ DEFINE_LOGGER(logger, "ConversationManager");
 
ConversationManager::ConversationManager(User *user, Component *component){
 
	m_user = user;
 
	m_component = component;
 
}
 

	
 
ConversationManager::~ConversationManager() {
 
	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());
 
	}
 
	deleteAllConversations();
 
}
 

	
 
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();
src/networkpluginserver.cpp
Show inline comments
 
@@ -646,12 +646,16 @@ void NetworkPluginServer::handleConvMessagePayload(const std::string &data, bool
 
		msg->setSubject(payload.message());
 
	}
 
	else {
 
		msg->setBody(payload.message());
 
	}
 

	
 
	if (payload.headline()) {
 
		msg->setType(Swift::Message::Headline);
 
	}
 

	
 
	// Add xhtml-im payload.
 
	if (CONFIG_BOOL(m_config, "service.enable_xhtml") && !payload.xhtml().empty()) {
 
		msg->addPayload(boost::make_shared<Swift::XHTMLIMPayload>(payload.xhtml()));
 
	}
 

	
 
	if (!payload.timestamp().empty()) {
src/settingsadhoccommand.cpp
Show inline comments
 
@@ -37,12 +37,17 @@ SettingsAdHocCommand::SettingsAdHocCommand(Component *component, UserManager *us
 
	Swift::BooleanFormField::ref field;
 

	
 
	field = Swift::BooleanFormField::create(true);
 
	field->setName("enable_transport");
 
	field->setLabel("Enable transport");
 
	addFormField(field);
 

	
 
	field = Swift::BooleanFormField::create(CONFIG_STRING_DEFAULTED(component->getConfig(), "settings.send_headlines", "0") == "1");
 
	field->setName("send_headlines");
 
	field->setLabel("Send messages as headlines");
 
	addFormField(field);
 
}
 

	
 
SettingsAdHocCommand::~SettingsAdHocCommand() {
 
}
 

	
 
boost::shared_ptr<Swift::Command> SettingsAdHocCommand::getForm() {
src/tests/basictest.h
Show inline comments
 
@@ -190,12 +190,21 @@ class TestingStorageBackend : public StorageBackend {
 
		}
 

	
 
		virtual void updateUserSetting(long userId, const std::string &variable, const std::string &value) {
 
			settings[userId][variable] = value;
 
		}
 

	
 
		void dumpUserSettings() {
 
			std::cout << "\n\nUserSettings dump:\n";
 
			for (std::map<int, std::map<std::string, std::string> >::const_iterator it = settings.begin(); it != settings.end(); it++) {
 
				for (std::map<std::string, std::string>::const_iterator it2 = it->second.begin(); it2 != it->second.end(); it2++) {
 
					std::cout << it->first << ":" << it2->first << "=" << it2->second << "\n";
 
				}
 
			}
 
		}
 

	
 
		virtual void beginTransaction() {}
 
		virtual void commitTransaction() {}
 
};
 

	
 
class BasicTest : public Swift::XMPPParserClient {
 

	
src/tests/conversationmanager.cpp
Show inline comments
 
@@ -21,12 +21,13 @@
 

	
 
using namespace Transport;
 

	
 
class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTest {
 
	CPPUNIT_TEST_SUITE(ConversationManagerTest);
 
	CPPUNIT_TEST(handleNormalMessages);
 
	CPPUNIT_TEST(handleNormalMessagesHeadline);
 
	CPPUNIT_TEST(handleGroupchatMessages);
 
	CPPUNIT_TEST(handleGroupchatMessagesTwoResources);
 
	CPPUNIT_TEST(handleChatstateMessages);
 
	CPPUNIT_TEST(handleSubjectMessages);
 
	CPPUNIT_TEST(handleParticipantChanged);
 
	CPPUNIT_TEST(handleParticipantChangedTwoResources);
 
@@ -201,12 +202,61 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe
 
		CPPUNIT_ASSERT_EQUAL(std::string("user@localhost"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getTo().toString());
 
		CPPUNIT_ASSERT_EQUAL(std::string("buddy1%test@localhost/bot"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getFrom().toString());
 
		
 
		received.clear();
 
	}
 

	
 
	void handleNormalMessagesHeadline() {
 
		User *user = userManager->getUser("user@localhost");
 
		user->addUserSetting("send_headlines", "1");
 

	
 
		TestingConversation *conv = new TestingConversation(user->getConversationManager(), "buddy1@test");
 
		user->getConversationManager()->addConversation(conv);
 
		conv->onMessageToSend.connect(boost::bind(&ConversationManagerTest::handleMessageReceived, this, _1, _2));
 

	
 
		boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
		msg->setBody("hi there<>!");
 

	
 
		// Forward it
 
		conv->handleMessage(msg);
 
		loop->processEvents();
 
		
 
		CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::Message *>(getStanza(received[0])));
 
		CPPUNIT_ASSERT_EQUAL(Swift::Message::Headline, dynamic_cast<Swift::Message *>(getStanza(received[0]))->getType());
 
		CPPUNIT_ASSERT_EQUAL(std::string("hi there<>!"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getBody());
 
		CPPUNIT_ASSERT_EQUAL(std::string("user@localhost"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getTo().toString());
 
		CPPUNIT_ASSERT_EQUAL(std::string("buddy1\\40test@localhost/bot"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getFrom().toString());
 

	
 
		received.clear();
 
		user->addUserSetting("send_headlines", "0");
 
		// Forward it - Conversation should keep the Headline type, because msg->getType() is Headline
 
		conv->handleMessage(msg);
 
		loop->processEvents();
 

	
 
		CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::Message *>(getStanza(received[0])));
 
		CPPUNIT_ASSERT_EQUAL(Swift::Message::Headline, dynamic_cast<Swift::Message *>(getStanza(received[0]))->getType());
 
		CPPUNIT_ASSERT_EQUAL(std::string("hi there<>!"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getBody());
 
		CPPUNIT_ASSERT_EQUAL(std::string("user@localhost"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getTo().toString());
 
		CPPUNIT_ASSERT_EQUAL(std::string("buddy1\\40test@localhost/bot"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getFrom().toString());
 

	
 
		received.clear();
 
		msg->setType(Swift::Message::Chat);
 
		// Forward it
 
		conv->handleMessage(msg);
 
		loop->processEvents();
 

	
 
		CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::Message *>(getStanza(received[0])));
 
		CPPUNIT_ASSERT_EQUAL(Swift::Message::Chat, dynamic_cast<Swift::Message *>(getStanza(received[0]))->getType());
 
		CPPUNIT_ASSERT_EQUAL(std::string("hi there<>!"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getBody());
 
		CPPUNIT_ASSERT_EQUAL(std::string("user@localhost"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getTo().toString());
 
		CPPUNIT_ASSERT_EQUAL(std::string("buddy1\\40test@localhost/bot"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getFrom().toString());
 
	}
 

	
 
	void handleGroupchatMessages() {
 
		User *user = userManager->getUser("user@localhost");
 
		TestingConversation *conv = new TestingConversation(user->getConversationManager(), "#room", true);
 
		user->getConversationManager()->addConversation(conv);
 
		conv->onMessageToSend.connect(boost::bind(&ConversationManagerTest::handleMessageReceived, this, _1, _2));
 
		conv->setNickname("nickname");
src/tests/settingsadhoccommand.cpp
Show inline comments
 
@@ -30,12 +30,14 @@ class SettingsAdHocCommandTest : public CPPUNIT_NS :: TestFixture, public BasicT
 
	CPPUNIT_TEST(getInfoBare);
 
	CPPUNIT_TEST(execute);
 
	CPPUNIT_TEST(executeTwoCommands);
 
	CPPUNIT_TEST(executeBadSessionID);
 
	CPPUNIT_TEST(executeNotRegistered);
 
	CPPUNIT_TEST(cancel);
 
	CPPUNIT_TEST(propagateUserSetting);
 
	CPPUNIT_TEST(defaultAccordingToConfig);
 
	CPPUNIT_TEST_SUITE_END();
 

	
 
	public:
 
		AdHocManager *adhoc;
 
		SettingsAdHocCommandFactory *settings;
 

	
 
@@ -324,9 +326,80 @@ class SettingsAdHocCommandTest : public CPPUNIT_NS :: TestFixture, public BasicT
 

	
 
			CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::Command>());
 
			CPPUNIT_ASSERT_EQUAL(std::string("settings"), getStanza(received[0])->getPayload<Swift::Command>()->getNode());
 
			CPPUNIT_ASSERT_EQUAL(Swift::Command::Canceled, getStanza(received[0])->getPayload<Swift::Command>()->getStatus());
 
		}
 

	
 
		void propagateUserSetting() {
 
			connectUser();
 
			User *user = userManager->getUser("user@localhost");
 
			CPPUNIT_ASSERT_EQUAL(std::string("0"), user->getUserSetting("send_headlines"));
 

	
 
			boost::shared_ptr<Swift::Command> payload(new Swift::Command("settings"));
 
			boost::shared_ptr<Swift::IQ> iq = Swift::IQ::createRequest(Swift::IQ::Set, Swift::JID("localhost"), "id", payload);
 
			iq->setFrom("user@localhost");
 
			injectIQ(iq);
 
			loop->processEvents();
 

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

	
 
			CPPUNIT_ASSERT(dynamic_cast<Swift::IQ *>(getStanza(received[0])));
 
			CPPUNIT_ASSERT_EQUAL(Swift::IQ::Result, dynamic_cast<Swift::IQ *>(getStanza(received[0]))->getType());
 

	
 
			CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::Command>());
 
			CPPUNIT_ASSERT_EQUAL(std::string("settings"), getStanza(received[0])->getPayload<Swift::Command>()->getNode());
 
			CPPUNIT_ASSERT_EQUAL(Swift::Command::Executing, getStanza(received[0])->getPayload<Swift::Command>()->getStatus());
 

	
 
			// form element
 
			CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::Command>()->getForm());
 
			CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::Command>()->getForm()->getField("send_headlines"));
 

	
 
			// set enabled_transport = 0
 
			Swift::FormField::ref f = getStanza(received[0])->getPayload<Swift:: Command>()->getForm()->getField("send_headlines");
 
			boost::dynamic_pointer_cast<Swift::BooleanFormField>(f)->setValue(true);
 

	
 
			std::string sessionId = getStanza(received[0])->getPayload<Swift::Command>()->getSessionID();
 

	
 
			// finish the command
 
			payload = boost::shared_ptr<Swift::Command>(new Swift::Command("settings"));
 
			payload->setSessionID(sessionId);
 
			payload->setForm(getStanza(received[0])->getPayload<Swift::Command>()->getForm());
 
			iq = Swift::IQ::createRequest(Swift::IQ::Set, Swift::JID("localhost"), "id", payload);
 
			iq->setFrom("user@localhost");
 
			received.clear();
 
			injectIQ(iq);
 
			loop->processEvents();
 

	
 
			CPPUNIT_ASSERT_EQUAL(std::string("1"), user->getUserSetting("send_headlines"));
 
		}
 

	
 
		void defaultAccordingToConfig() {
 
			std::istringstream ifs("service.server_mode = 1\nservice.jid_escaping=0\nservice.jid=localhost\nsettings.send_headlines=1\n");
 
			cfg->load(ifs);
 
			connectUser();
 
			User *user = userManager->getUser("user@localhost");
 
			CPPUNIT_ASSERT_EQUAL(std::string("1"), user->getUserSetting("send_headlines"));
 
			boost::shared_ptr<Swift::Command> payload(new Swift::Command("settings"));
 
			boost::shared_ptr<Swift::IQ> iq = Swift::IQ::createRequest(Swift::IQ::Set, Swift::JID("localhost"), "id", payload);
 
			iq->setFrom("user@localhost");
 
			injectIQ(iq);
 
			loop->processEvents();
 

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

	
 
			CPPUNIT_ASSERT(dynamic_cast<Swift::IQ *>(getStanza(received[0])));
 
			CPPUNIT_ASSERT_EQUAL(Swift::IQ::Result, dynamic_cast<Swift::IQ *>(getStanza(received[0]))->getType());
 

	
 
			CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::Command>());
 
			CPPUNIT_ASSERT_EQUAL(std::string("settings"), getStanza(received[0])->getPayload<Swift::Command>()->getNode());
 
			CPPUNIT_ASSERT_EQUAL(Swift::Command::Executing, getStanza(received[0])->getPayload<Swift::Command>()->getStatus());
 

	
 
			// form element
 
			CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::Command>()->getForm());
 
			CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::Command>()->getForm()->getField("send_headlines"));
 
			Swift::FormField::ref f = getStanza(received[0])->getPayload<Swift:: Command>()->getForm()->getField("send_headlines");
 
			CPPUNIT_ASSERT_EQUAL(true, boost::dynamic_pointer_cast<Swift::BooleanFormField>(f)->getValue());
 
		}
 

	
 
};
 

	
 
CPPUNIT_TEST_SUITE_REGISTRATION (SettingsAdHocCommandTest);
0 comments (0 inline, 0 general)