diff options
| -rw-r--r-- | Sluift/ElementConvertors/CommandConvertor.cpp | 195 | ||||
| -rw-r--r-- | Sluift/ElementConvertors/CommandConvertor.h | 28 | ||||
| -rw-r--r-- | Sluift/Examples/AdHocCommands.lua | 48 | ||||
| -rw-r--r-- | Sluift/LuaElementConvertors.cpp | 2 | ||||
| -rw-r--r-- | Sluift/SConscript | 1 | ||||
| -rw-r--r-- | Sluift/boot.lua | 27 | 
6 files changed, 294 insertions, 7 deletions
| diff --git a/Sluift/ElementConvertors/CommandConvertor.cpp b/Sluift/ElementConvertors/CommandConvertor.cpp new file mode 100644 index 0000000..7fb7b22 --- /dev/null +++ b/Sluift/ElementConvertors/CommandConvertor.cpp @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/ElementConvertors/CommandConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <boost/numeric/conversion/cast.hpp> + +#include <Swiften/Base/foreach.h> +#include <Sluift/Lua/Check.h> +#include <Sluift/Lua/Value.h> +#include <Sluift/LuaElementConvertors.h> + +using namespace Swift; + +static Command::Action convertActionFromString(const std::string& action) { +	if (action == "cancel") { return Command::Cancel; } +	else if (action == "execute") { return Command::Execute; } +	else if (action == "complete") { return Command::Complete; } +	else if (action == "prev") { return Command::Prev; } +	else if (action == "next") { return Command::Next; } +	return Command::NoAction; +} + +static std::string convertActionToString(Command::Action action) { +	switch (action) { +		case Command::Cancel: return "cancel"; +		case Command::Execute: return "execute"; +		case Command::Complete: return "complete"; +		case Command::Prev: return "prev"; +		case Command::Next: return "next"; +		case Command::NoAction: assert(false); return ""; +	} +	assert(false); +	return ""; +} + +CommandConvertor::CommandConvertor(LuaElementConvertors* convertors) :  +		GenericLuaElementConvertor<Command>("command"), +		convertors(convertors) { +} + +CommandConvertor::~CommandConvertor() { +} + +boost::shared_ptr<Command> CommandConvertor::doConvertFromLua(lua_State* L) { +	boost::shared_ptr<Command> result = boost::make_shared<Command>(); + +	lua_getfield(L, -1, "node"); +	if (!lua_isnil(L, -1)) { +		result->setNode(std::string(Lua::checkString(L, -1))); +	} +	lua_pop(L, 1); + +	lua_getfield(L, -1, "session_id"); +	if (!lua_isnil(L, -1)) { +		result->setSessionID(std::string(Lua::checkString(L, -1))); +	} +	lua_pop(L, 1); + +	lua_getfield(L, -1, "status"); +	if (!lua_isnil(L, -1)) { +		std::string statusText = Lua::checkString(L, -1); +		Command::Status status = Command::NoStatus; +		if (statusText == "executing") { status = Command::Executing; } +		else if (statusText == "completed") { status = Command::Completed; } +		else if (statusText == "canceled") { status = Command::Canceled; } +		result->setStatus(status); +	} +	lua_pop(L, 1); + +	lua_getfield(L, -1, "action"); +	if (!lua_isnil(L, -1)) { +		result->setAction(convertActionFromString(Lua::checkString(L, -1))); +	} +	lua_pop(L, 1); + +	lua_getfield(L, -1, "execute_action"); +	if (!lua_isnil(L, -1)) { +		result->setExecuteAction(convertActionFromString(Lua::checkString(L, -1))); +	} +	lua_pop(L, 1); + +	lua_getfield(L, -1, "available_actions"); +	if (!lua_isnil(L, -1)) { +		Lua::checkType(L, -1, LUA_TTABLE); +		lua_pushnil(L); +		for (lua_pushnil(L); lua_next(L, -2) != 0; ) { +			result->addAvailableAction(convertActionFromString(Lua::checkString(L, -1))); +			lua_pop(L, 1); +		} +	} +	lua_pop(L, 1); + +	lua_getfield(L, -1, "notes"); +	if (!lua_isnil(L, -1)) { +		Lua::checkType(L, -1, LUA_TTABLE); +		lua_pushnil(L); +		for (lua_pushnil(L); lua_next(L, -2) != 0; ) { +			Lua::checkType(L, -1, LUA_TTABLE); +			std::string note; +			lua_getfield(L, -1, "note"); +			if (!lua_isnil(L, -1)) { +				note = Lua::checkString(L, -1); +			} +			lua_pop(L, 1); + +			Command::Note::Type noteType = Command::Note::Info; +			lua_getfield(L, -1, "type"); +			if (!lua_isnil(L, -1)) { +				std::string type = Lua::checkString(L, -1); +				if (type == "info") { noteType = Command::Note::Info; } +				else if (type == "warn") { noteType = Command::Note::Warn; } +				else if (type == "error") { noteType = Command::Note::Error; } +			} +			lua_pop(L, 1); + +			result->addNote(Command::Note(note, noteType)); +			lua_pop(L, 1); +		} +	} +	lua_pop(L, 1); + +	lua_getfield(L, -1, "form"); +	if (!lua_isnil(L, -1)) { +		if (boost::shared_ptr<Form> form = boost::dynamic_pointer_cast<Form>(convertors->convertFromLuaUntyped(L, -1, "form"))) { +			result->setForm(form); +		} +	} +	lua_pop(L, 1); + +	return result; +} + +void CommandConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<Command> payload) { +	Lua::Table result; +	if (!payload->getNode().empty()) { +		result["node"] = Lua::valueRef(payload->getNode()); +	} +	if (!payload->getSessionID().empty()) { +		result["session_id"] = Lua::valueRef(payload->getSessionID()); +	} +	switch (payload->getStatus()) { +		case Command::Executing: result["status"] = Lua::valueRef("executing"); break; +		case Command::Completed: result["status"] = Lua::valueRef("completed"); break; +		case Command::Canceled: result["status"] = Lua::valueRef("canceled"); break; +		case Command::NoStatus: break; +	} + +	if (!payload->getNotes().empty()) { +		std::vector<Lua::Value> notes; +		foreach (const Command::Note& note, payload->getNotes()) { +			Lua::Table noteTable; +			if (!note.note.empty()) { +				noteTable["note"] = Lua::valueRef(note.note); +			} +			switch (note.type) { +				case Command::Note::Info: noteTable["type"] = Lua::valueRef("info"); break; +				case Command::Note::Warn: noteTable["type"] = Lua::valueRef("warn"); break; +				case Command::Note::Error: noteTable["type"] = Lua::valueRef("error"); break; +			} +			notes.push_back(noteTable); +		} +		result["notes"] = Lua::valueRef(notes); +	} + +	if (payload->getAction() != Command::NoAction) { +		result["action"] = Lua::valueRef(convertActionToString(payload->getAction())); +	} + +	if (payload->getExecuteAction() != Command::NoAction) { +		result["execute_action"] = Lua::valueRef(convertActionToString(payload->getAction())); +	} + +	if (!payload->getAvailableActions().empty()) { +		std::vector<Lua::Value> availableActions; +		foreach (const Command::Action& action, payload->getAvailableActions()) { +			if (action != Command::NoAction) { +				availableActions.push_back(convertActionToString(action)); +			} +		} +		result["available_actions"] = Lua::valueRef(availableActions); +	} + +	Lua::pushValue(L, result); + +	if (payload->getForm()) { +		convertors->convertToLuaUntyped(L, payload->getForm()); +		lua_setfield(L, -2, "form"); +	} +} diff --git a/Sluift/ElementConvertors/CommandConvertor.h b/Sluift/ElementConvertors/CommandConvertor.h new file mode 100644 index 0000000..7008b50 --- /dev/null +++ b/Sluift/ElementConvertors/CommandConvertor.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/Command.h> + +namespace Swift { +	class LuaElementConvertors; + +	class CommandConvertor : public GenericLuaElementConvertor<Command> { +		public: +			CommandConvertor(LuaElementConvertors* convertors); +			virtual ~CommandConvertor(); + +			virtual boost::shared_ptr<Command> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; +			virtual void doConvertToLua(lua_State*, boost::shared_ptr<Command>) SWIFTEN_OVERRIDE; + +		private: +			LuaElementConvertors* convertors; +	}; +} diff --git a/Sluift/Examples/AdHocCommands.lua b/Sluift/Examples/AdHocCommands.lua new file mode 100644 index 0000000..9e83f0c --- /dev/null +++ b/Sluift/Examples/AdHocCommands.lua @@ -0,0 +1,48 @@ +--[[ +	Copyright (c) 2013 Remko Tronçon +	Licensed under the GNU General Public License v3. +	See Documentation/Licenses/GPLv3.txt for more information. +--]] + +--[[ +	 +	Ad-hoc command example. + +	This script logs into an XMPP server, and executes a +	multi-step ad-hoc command. +	 +	The following environment variables are used: +	* SLUIFT_JID, SWIFT_PASS: JID and password to log in with +	* SLUIFT_DEBUG: Sets whether debugging should be turned on + +--]] + +require "sluift" + +sluift.debug = os.getenv("SLUIFT_DEBUG") or false + +c = sluift.new_client(os.getenv("SLUIFT_JID"), os.getenv("SLUIFT_PASS")) +c:connect(function ()  +	to = sluift.jid.domain(os.getenv("SLUIFT_JID")) + +	-- Get the list of commands +	commands = assert(c:get_disco_items{ to = to, disco_items = { +		node = sluift.disco.features.COMMANDS }}) +	print(commands) + +	-- Get the initial form +	result = assert(c:set_command{ to = to, command = { +			action = 'execute', node = 'http://jabber.org/protocol/admin#get-user-roster'}}) + +	-- Fill in the form +	submission = result.form:create_submission() +	submission.accountjid = os.getenv("SLUIFT_JID") + +	-- Submit the form +	result = assert(c:set_command{ to = to, command = { +			action = 'complete', node = 'http://jabber.org/protocol/admin#get-user-roster', +			session_id = result.session_id, form = submission}}) +	for _, v in ipairs(result.form.roster.values) do +		print(v) +	end +end) diff --git a/Sluift/LuaElementConvertors.cpp b/Sluift/LuaElementConvertors.cpp index cadfbc4..71957c1 100644 --- a/Sluift/LuaElementConvertors.cpp +++ b/Sluift/LuaElementConvertors.cpp @@ -21,6 +21,7 @@  #include <Sluift/ElementConvertors/VCardUpdateConvertor.h>  #include <Sluift/ElementConvertors/VCardConvertor.h>  #include <Sluift/ElementConvertors/BodyConvertor.h> +#include <Sluift/ElementConvertors/CommandConvertor.h>  #include <Sluift/Lua/LuaUtils.h>  #include <Sluift/Lua/Exception.h> @@ -28,6 +29,7 @@ using namespace Swift;  LuaElementConvertors::LuaElementConvertors() {  	registerConvertors(); +	convertors.push_back(boost::make_shared<CommandConvertor>(this));  	convertors.push_back(boost::make_shared<PubSubEventConvertor>(this));  	convertors.push_back(boost::make_shared<BodyConvertor>());  	convertors.push_back(boost::make_shared<VCardConvertor>()); diff --git a/Sluift/SConscript b/Sluift/SConscript index 5d2242e..1a29e43 100644 --- a/Sluift/SConscript +++ b/Sluift/SConscript @@ -29,6 +29,7 @@ elif env["SCONS_STAGE"] == "build" :  		"ElementConvertors/FormConvertor.cpp",  		"ElementConvertors/SoftwareVersionConvertor.cpp",  		"ElementConvertors/VCardConvertor.cpp", +		"ElementConvertors/CommandConvertor.cpp",  		"ClientHelpers.cpp",  		"SluiftClient.cpp",  		"boot.c", diff --git a/Sluift/boot.lua b/Sluift/boot.lua index e81257a..2505736 100644 --- a/Sluift/boot.lua +++ b/Sluift/boot.lua @@ -173,13 +173,25 @@ for method, event_type in pairs({messages = 'message', pubsub_events = 'pubsub'}  	end  end --- Register get_* convenience methods for some type of queries -for _, query_type in ipairs({'software_version', 'disco_items', 'xml', 'dom', 'vcard'}) do -	Client['get_' .. query_type] = function (client, options) -		options = options or {} -		if type(options) ~= 'table' then error('Invalid options: ' .. options) end  -		options['query'] = merge_tables({_type = query_type}, options['query'] or {}) -		return client:get(options) +-- +-- Register get_* and set_* convenience methods for some type of queries +-- +-- Example usages: +--	client:get_software_version{to = 'alice@wonderland.lit'} +--	client:set_command{to = 'alice@wonderland.lit', command = { type = 'execute', node = 'uptime' }} +-- +local get_set_shortcuts = { +	get = {'software_version', 'disco_items', 'xml', 'dom', 'vcard'}, +	set = {'command'} +} +for query_action, query_types in pairs(get_set_shortcuts) do +	for _, query_type in ipairs(query_types) do +		Client[query_action .. '_' .. query_type] = function (client, options) +			options = options or {} +			if type(options) ~= 'table' then error('Invalid options: ' .. options) end  +			options['query'] = merge_tables({_type = query_type}, options[query_type] or {}) +			return client[query_action](client, options) +		end  	end  end @@ -403,6 +415,7 @@ end  local disco = {  	features = {  		DISCO_INFO = 'http://jabber.org/protocol/disco#info', +		COMMANDS = 'http://jabber.org/protocol/commands',  		USER_LOCATION = 'http://jabber.org/protocol/geoloc',  		USER_TUNE = 'http://jabber.org/protocol/tune',  		USER_AVATAR_METADATA = 'urn:xmpp:avatar:metadata', | 
 Swift
 Swift