diff options
Diffstat (limited to 'Sluift')
208 files changed, 13728 insertions, 1031 deletions
diff --git a/Sluift/.gitignore b/Sluift/.gitignore index 35a18c0..4e4c2a4 100644 --- a/Sluift/.gitignore +++ b/Sluift/.gitignore @@ -1,4 +1,9 @@ lua.c +Version.h sluift_dll.cpp sluift dll.c +core.c +iTunes.h +dll/ +exe/ diff --git a/Sluift/Completer.cpp b/Sluift/Completer.cpp new file mode 100644 index 0000000..4bd1eaf --- /dev/null +++ b/Sluift/Completer.cpp @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/Completer.h> + +using namespace Swift; + +Completer::~Completer() { +} diff --git a/Sluift/Completer.h b/Sluift/Completer.h new file mode 100644 index 0000000..9651eb1 --- /dev/null +++ b/Sluift/Completer.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#pragma once + +#include <string> +#include <vector> + +namespace Swift { + class Completer { + public: + virtual ~Completer(); + + virtual std::vector<std::string> getCompletions(const std::string& buffer, int start, int end) = 0; + }; +} diff --git a/Sluift/Console.cpp b/Sluift/Console.cpp new file mode 100644 index 0000000..7b5b437 --- /dev/null +++ b/Sluift/Console.cpp @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/Console.h> +#include <lua.hpp> +#include <stdexcept> +#include <iostream> +#include <boost/optional.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/algorithm/string/predicate.hpp> +#include <boost/numeric/conversion/cast.hpp> +#include <Sluift/Terminal.h> +#include <Sluift/tokenize.h> +#include <Sluift/Lua/LuaUtils.h> +#include <cctype> + +using namespace Swift; + +/** + * This function is called by pcall() when an error happens. + * Adds the backtrace to the error message. + */ +static int traceback(lua_State* L) { + if (!lua_isstring(L, 1)) { + return 1; + } + lua_getglobal(L, "debug"); + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + return 1; + } + lua_getfield(L, -1, "traceback"); + if (!lua_isfunction(L, -1)) { + lua_pop(L, 2); + return 1; + } + lua_pushvalue(L, 1); + lua_pushinteger(L, 2); + lua_call(L, 2, 1); + return 1; +} + + +Console::Console(lua_State* L, Terminal* terminal) : L(L), terminal(terminal), previousNumberOfReturnArguments(0) { + terminal->setCompleter(this); +} + +Console::~Console() { +} + +void Console::run() { + while (true) { + lua_settop(L, 0); + try { + if (!readCommand()) { + return; + } + int result = call(L, 0, true); + if (result != 0) { + throw std::runtime_error(getErrorMessage()); + } + + // Clear the previous results + for (int i = 0; i < previousNumberOfReturnArguments; ++i) { + lua_pushnil(L); + lua_setglobal(L, ("_" + boost::lexical_cast<std::string>(i+1)).c_str()); + } + + // Store the results + for (int i = 0; i < lua_gettop(L); ++i) { + lua_pushvalue(L, i+1); + lua_setglobal(L, ("_" + boost::lexical_cast<std::string>(i+1)).c_str()); + } + previousNumberOfReturnArguments = lua_gettop(L); + + // Print results + if (lua_gettop(L) > 0) { + lua_getglobal(L, "print"); + lua_insert(L, 1); + if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != 0) { + throw std::runtime_error("Error calling 'print': " + getErrorMessage()); + } + } + } + catch (const std::exception& e) { + terminal->printError(e.what()); + } + } + +} + +int Console::tryLoadCommand(const std::string& originalCommand) { + std::string command = originalCommand; + + // Replace '=' by 'return' (for compatibility with Lua console) + if (boost::algorithm::starts_with(command, "=")) { + command = "return " + command.substr(1); + } + + std::string commandAsExpression = "return " + command; + + // Try to load the command as an expression + if (luaL_loadbuffer(L, commandAsExpression.c_str(), commandAsExpression.size(), "=stdin") == 0) { + return 0; + } + lua_pop(L, 1); + + // Try to load the command as a regular command + return luaL_loadbuffer(L, command.c_str(), command.size(), "=stdin"); +} + +bool Console::readCommand() { + boost::optional<std::string> line = terminal->readLine(getPrompt(true)); + if (!line) { + return false; + } + std::string command = *line; + while (true) { + int result = tryLoadCommand(command); + + // Check if we need to read more + if (result == LUA_ERRSYNTAX) { + std::string errorMessage(lua_tostring(L, -1)); + if (boost::algorithm::ends_with(errorMessage, "'<eof>'")) { + lua_pop(L, 1); + + // Read another line + boost::optional<std::string> line = terminal->readLine(getPrompt(false)); + if (!line) { + return false; + } + command = command + "\n" + *line; + continue; + } + } + if (!command.empty()) { + terminal->addToHistory(command); + } + if (result != 0) { + throw std::runtime_error(getErrorMessage()); + } + return true; + } +} + +std::string Console::getErrorMessage() const { + if (lua_isnil(L, -1)) { + return "<null error>"; + } + const char* errorMessage = lua_tostring(L, -1); + return errorMessage ? errorMessage : "<error is not a string>"; +} + +int Console::call(lua_State* L, int numberOfArguments, bool keepResult) { + // Put traceback function on stack below call + int tracebackIndex = lua_gettop(L) - numberOfArguments; + lua_pushcfunction(L, traceback); + lua_insert(L, tracebackIndex); + + int result = lua_pcall(L, numberOfArguments, keepResult ? LUA_MULTRET : 0, tracebackIndex); + + // Remove traceback + lua_remove(L, tracebackIndex); + + return result; +} + +std::string Console::getPrompt(bool firstLine) const { + lua_getglobal(L,firstLine ? "_PROMPT" : "_PROMPT2"); + const char* rawPrompt = lua_tostring(L, -1); + std::string prompt; + if (rawPrompt) { + prompt = std::string(rawPrompt); + } + else { + prompt = firstLine ? "> " : ">> "; + } + lua_pop(L, 1); + return prompt; +} + +static void addMatchingTableKeys(lua_State* L, const std::string& match, std::vector<std::string>& result) { + for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { + const char* rawKey = lua_tostring(L, -2); + if (rawKey) { + std::string key(rawKey); + if (boost::starts_with(key, match) && !(match == "" && boost::starts_with(key, "_"))) { + result.push_back(key); + } + } + } +} + +static void addMatchingTableValues(lua_State* L, const std::string& match, std::vector<std::string>& result) { + for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { + const char* rawValue = lua_tostring(L, -1); + if (rawValue) { + std::string key(rawValue); + if (boost::starts_with(key, match) && !(match == "" && boost::starts_with(key, "_"))) { + result.push_back(key); + } + } + } +} + +std::vector<std::string> Console::getCompletions(const std::string& input, int start, int end) { + std::string prefix = input.substr(boost::numeric_cast<size_t>(start), boost::numeric_cast<size_t>(end - start)); + + std::vector<std::string> tokens; + if (end) { + tokens = Lua::tokenize(input.substr(0, boost::numeric_cast<size_t>(end))); + } + + // Don't autocomplete strings + if (!tokens.empty() && ((*tokens.rbegin())[0] == '\'' || (*tokens.rbegin())[0] == '"')) { + return std::vector<std::string>(); + } + + std::vector<std::string> context; + for (std::vector<std::string>::reverse_iterator i = tokens.rbegin(); i != tokens.rend(); ++i) { + if (std::isalpha((*i)[0]) || (*i)[0] == '_') { + if (i != tokens.rbegin()) { + context.push_back(*i); + } + } + else if (*i != "." && *i != ":") { + break; + } + } + + // Drill into context + int top = lua_gettop(L); + lua_pushglobaltable(L); + for (std::vector<std::string>::reverse_iterator i = context.rbegin(); i != context.rend(); ++i) { + if (lua_istable(L, -1) || lua_isuserdata(L, -1)) { + lua_getfield(L, -1, i->c_str()); + if (!lua_isnil(L, 1)) { + continue; + } + } + lua_settop(L, top); + return std::vector<std::string>(); + } + + // Collect all keys from the table + std::vector<std::string> result; + if (lua_istable(L, -1)) { + addMatchingTableKeys(L, prefix, result); + } + + // Collect all keys from the metatable + if (lua_getmetatable(L, -1)) { + lua_getfield(L, -1, "__index"); + if (lua_istable(L, -1)) { + addMatchingTableKeys(L, prefix, result); + } + lua_pop(L, 1); + + lua_getfield(L, -1, "_completions"); + if (lua_isfunction(L, -1)) { + lua_pushvalue(L, -3); + if (lua_pcall(L, 1, 1, 0) != 0) { + throw std::runtime_error("Error calling '_completions': " + getErrorMessage()); + } + } + if (lua_istable(L, -1)) { + addMatchingTableValues(L, prefix, result); + } + lua_pop(L, 2); + } + + lua_settop(L, top); + + return result; +} + diff --git a/Sluift/Console.h b/Sluift/Console.h new file mode 100644 index 0000000..2d10b38 --- /dev/null +++ b/Sluift/Console.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#pragma once + +#include <string> +#include <boost/optional/optional_fwd.hpp> +#include <Sluift/Completer.h> +#include <Swiften/Base/Override.h> + +struct lua_State; + +namespace Swift { + class Terminal; + + class Console : public Completer { + public: + Console(lua_State* L, Terminal* terminal); + virtual ~Console(); + + void run(); + + static int call(lua_State* L, int numberOfArguments, bool keepResult); + + private: + std::string getPrompt(bool firstLine) const; + std::string getErrorMessage() const; + bool readCommand(); + int tryLoadCommand(const std::string& command); + + virtual std::vector<std::string> getCompletions(const std::string&, int start, int end) SWIFTEN_OVERRIDE; + + private: + lua_State* L; + Terminal* terminal; + int previousNumberOfReturnArguments; + }; +} diff --git a/Sluift/EditlineTerminal.cpp b/Sluift/EditlineTerminal.cpp new file mode 100644 index 0000000..ec2c7a2 --- /dev/null +++ b/Sluift/EditlineTerminal.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/EditlineTerminal.h> + +#include <boost/optional.hpp> +#include <iostream> +#include <editline/readline.h> +#include <boost/numeric/conversion/cast.hpp> +#include <cassert> +#include <vector> +#include <cstring> + +#include <Swiften/Base/Platform.h> +#include <Sluift/Completer.h> + +using namespace Swift; + +static EditlineTerminal* globalInstance = NULL; + +static int completionStart = -1; +static int completionEnd = -1; + +#if defined(SWIFTEN_PLATFORM_WINDOWS) +static char* getEmptyCompletions(const char*, int) { +#else +static int getEmptyCompletions(const char*, int) { +#endif + return 0; +} + +static char* getCompletions(const char*, int state) { + rl_completion_append_character = 0; +#if RL_READLINE_VERSION >= 0x0600 + rl_completion_suppress_append = 1; +#endif + + static std::vector<std::string> completions; + if (state == 0) { + assert(globalInstance); + completions.clear(); + if (globalInstance->getCompleter()) { + completions = globalInstance->getCompleter()->getCompletions(rl_line_buffer, completionStart, completionEnd); + } + } + if (boost::numeric_cast<size_t>(state) >= completions.size()) { + return 0; + } + return strdup(completions[boost::numeric_cast<size_t>(state)].c_str()); +} + +static char** getAttemptedCompletions(const char* text, int start, int end) { + completionStart = start; + completionEnd = end; + return rl_completion_matches(text, getCompletions); +} + +EditlineTerminal& EditlineTerminal::getInstance() { + static EditlineTerminal instance; + globalInstance = &instance; + return instance; +} + +EditlineTerminal::EditlineTerminal() { + rl_attempted_completion_function = getAttemptedCompletions; + rl_completion_entry_function = getEmptyCompletions; // Fallback. Do nothing. +#if defined(SWIFTEN_PLATFORM_WINDOWS) + // rl_basic_word_break is a cons char[] in MinGWEditLine. + // This one seems to work, although it doesn't on OS X for some reason. + rl_completer_word_break_characters = strdup(" \t\n.:+-*/><=;|&()[]{}"); +#else + rl_basic_word_break_characters = strdup(" \t\n.:+-*/><=;|&()[]{}"); +#endif +} + +EditlineTerminal::~EditlineTerminal() { +} + +void EditlineTerminal::printError(const std::string& message) { + std::cout << message << std::endl; +} + +boost::optional<std::string> EditlineTerminal::readLine(const std::string& prompt) { + const char* line = readline(prompt.c_str()); + return line ? std::string(line) : boost::optional<std::string>(); +} + +void EditlineTerminal::addToHistory(const std::string& line) { +#if defined(SWIFTEN_PLATFORM_WINDOWS) + // MinGWEditLine copies the string, so this is safe + add_history(const_cast<char*>(line.c_str())); +#else + add_history(line.c_str()); +#endif +} diff --git a/Sluift/EditlineTerminal.h b/Sluift/EditlineTerminal.h new file mode 100644 index 0000000..2f671b7 --- /dev/null +++ b/Sluift/EditlineTerminal.h @@ -0,0 +1,25 @@ +/* + * 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/Terminal.h> + +namespace Swift { + class EditlineTerminal : public Terminal { + public: + static EditlineTerminal& getInstance(); + + private: + EditlineTerminal(); + virtual ~EditlineTerminal(); + + virtual boost::optional<std::string> readLine(const std::string& prompt) SWIFTEN_OVERRIDE; + virtual void printError(const std::string& message) SWIFTEN_OVERRIDE; + virtual void addToHistory(const std::string& command); + }; +} diff --git a/Sluift/ElementConvertors/BodyConvertor.cpp b/Sluift/ElementConvertors/BodyConvertor.cpp new file mode 100644 index 0000000..a7abc1e --- /dev/null +++ b/Sluift/ElementConvertors/BodyConvertor.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/ElementConvertors/BodyConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <Sluift/Lua/LuaUtils.h> + +using namespace Swift; + +BodyConvertor::BodyConvertor() : GenericLuaElementConvertor<Body>("body") { +} + +BodyConvertor::~BodyConvertor() { +} + +boost::shared_ptr<Body> BodyConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<Body> result = boost::make_shared<Body>(); + if (boost::optional<std::string> value = Lua::getStringField(L, -1, "text")) { + result->setText(*value); + } + return result; +} + +void BodyConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<Body> payload) { + lua_createtable(L, 0, 0); + if (!payload->getText().empty()) { + lua_pushstring(L, payload->getText().c_str()); + lua_setfield(L, -2, "text"); + } +} diff --git a/Sluift/ElementConvertors/BodyConvertor.h b/Sluift/ElementConvertors/BodyConvertor.h new file mode 100644 index 0000000..b1cd494 --- /dev/null +++ b/Sluift/ElementConvertors/BodyConvertor.h @@ -0,0 +1,25 @@ +/* + * 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/Body.h> + +namespace Swift { + class LuaElementConvertors; + + class BodyConvertor : public GenericLuaElementConvertor<Body> { + public: + BodyConvertor(); + virtual ~BodyConvertor(); + + virtual boost::shared_ptr<Body> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<Body>) SWIFTEN_OVERRIDE; + }; +} diff --git a/Sluift/ElementConvertors/CommandConvertor.cpp b/Sluift/ElementConvertors/CommandConvertor.cpp new file mode 100644 index 0000000..9a2aa5f --- /dev/null +++ b/Sluift/ElementConvertors/CommandConvertor.cpp @@ -0,0 +1,196 @@ +/* + * 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()) { + bool result = convertors->convertToLuaUntyped(L, payload->getForm()); + assert(result); + 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/ElementConvertors/DOMElementConvertor.cpp b/Sluift/ElementConvertors/DOMElementConvertor.cpp new file mode 100644 index 0000000..784fcfd --- /dev/null +++ b/Sluift/ElementConvertors/DOMElementConvertor.cpp @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/ElementConvertors/DOMElementConvertor.h> + +#include <iostream> +#include <boost/smart_ptr/make_shared.hpp> +#include <lua.hpp> + +#include <Swiften/Base/foreach.h> +#include <Swiften/Elements/RawXMLPayload.h> +#include <Swiften/Serializer/PayloadSerializer.h> +#include <Sluift/Lua/Check.h> +#include <Sluift/Lua/LuaUtils.h> +#include <Swiften/Parser/XMLParserClient.h> +#include <Swiften/Parser/XMLParser.h> +#include <Swiften/Parser/AttributeMap.h> +#include <Swiften/Parser/Attribute.h> +#include <Swiften/Serializer/XML/XMLElement.h> +#include <Swiften/Serializer/XML/XMLTextNode.h> +#include <Swiften/Serializer/XML/XMLRawTextNode.h> +#include <Sluift/Lua/Debug.h> + +using namespace Swift; + +namespace { + class ParserClient : public XMLParserClient { + public: + ParserClient(lua_State* L) : L(L), currentIndex(1) { + } + + virtual void handleStartElement( + const std::string& element, const std::string& ns, + const AttributeMap& attributes) SWIFTEN_OVERRIDE { + lua_checkstack(L, 6); + lua_pushnumber(L, currentIndex); + lua_newtable(L); + lua_pushstring(L, element.c_str()); + lua_setfield(L, -2, "tag"); + if (!ns.empty()) { + lua_pushstring(L, ns.c_str()); + lua_setfield(L, -2, "ns"); + } + if (!attributes.getEntries().empty()) { + lua_newtable(L); + int i = 1; + foreach(const AttributeMap::Entry& entry, attributes.getEntries()) { + lua_pushnumber(L, i); + lua_newtable(L); + lua_pushstring(L, entry.getAttribute().getName().c_str()); + lua_setfield(L, -2, "name"); + if (!entry.getAttribute().getNamespace().empty()) { + lua_pushstring(L, entry.getAttribute().getNamespace().c_str()); + lua_setfield(L, -2, "ns"); + } + lua_pushstring(L, entry.getValue().c_str()); + lua_setfield(L, -2, "value"); + lua_settable(L, -3); + ++i; + } + lua_setfield(L, -2, "attributes"); + } + + indexStack.push_back(currentIndex); + currentIndex = 1; + lua_newtable(L); + } + + virtual void handleEndElement( + const std::string&, const std::string&) SWIFTEN_OVERRIDE { + lua_setfield(L, -2, "children"); + lua_settable(L, -3); + currentIndex = indexStack.back(); + indexStack.pop_back(); + currentIndex++; + } + + virtual void handleCharacterData(const std::string& data) SWIFTEN_OVERRIDE { + lua_checkstack(L, 2); + lua_pushnumber(L, currentIndex); + lua_pushstring(L, data.c_str()); + lua_settable(L, -3); + currentIndex++; + } + + private: + lua_State* L; + std::vector<int> indexStack; + int currentIndex; + }; + + std::string serializeElement(lua_State* L) { + std::string tag; + lua_getfield(L, -1, "tag"); + if (lua_isstring(L, -1)) { + tag = lua_tostring(L, -1); + } + lua_pop(L, 1); + + std::string ns; + lua_getfield(L, -1, "ns"); + if (lua_isstring(L, -1)) { + ns = lua_tostring(L, -1); + } + lua_pop(L, 1); + + XMLElement element(tag, ns); + + lua_getfield(L, -1, "attributes"); + if (lua_istable(L, -1)) { + int index = Lua::absoluteOffset(L, -1); + for (lua_pushnil(L); lua_next(L, index) != 0; ) { + if (lua_istable(L, -1)) { + std::string attributeName; + lua_getfield(L, -1, "name"); + if (lua_isstring(L, -1)) { + attributeName = lua_tostring(L, -1); + } + lua_pop(L, 1); + + std::string attributeValue; + lua_getfield(L, -1, "value"); + if (lua_isstring(L, -1)) { + attributeValue = lua_tostring(L, -1); + } + lua_pop(L, 1); + + if (!attributeName.empty()) { + element.setAttribute(attributeName, attributeValue); + } + } + lua_pop(L, 1); // value + } + } + lua_pop(L, 1); // children + + lua_getfield(L, -1, "children"); + if (lua_istable(L, -1)) { + int index = Lua::absoluteOffset(L, -1); + for (lua_pushnil(L); lua_next(L, index) != 0; ) { + if (lua_isstring(L, -1)) { + element.addNode(boost::make_shared<XMLTextNode>(lua_tostring(L, -1))); + } + else if (lua_istable(L, -1)) { + element.addNode(boost::make_shared<XMLRawTextNode>(serializeElement(L))); + } + lua_pop(L, 1); // value + } + } + lua_pop(L, 1); // children + + return element.serialize(); + } +} + +DOMElementConvertor::DOMElementConvertor() { +} + +DOMElementConvertor::~DOMElementConvertor() { +} + +boost::shared_ptr<Element> DOMElementConvertor::convertFromLua(lua_State* L, int index, const std::string& type) { + if (!lua_istable(L, index) || type != "dom") { + return boost::shared_ptr<Payload>(); + } + return boost::make_shared<RawXMLPayload>(serializeElement(L).c_str()); +} + +boost::optional<std::string> DOMElementConvertor::convertToLua( + lua_State* L, boost::shared_ptr<Element> element) { + // Serialize payload to XML + boost::shared_ptr<Payload> payload = boost::dynamic_pointer_cast<Payload>(element); + if (!payload) { + return boost::optional<std::string>(); + } + + PayloadSerializer* serializer = serializers.getPayloadSerializer(payload); + assert(serializer); + std::string serializedPayload = serializer->serialize(payload); + + lua_newtable(L); + + // Parse the payload again + ParserClient parserClient(L); + boost::shared_ptr<XMLParser> parser(parsers.createXMLParser(&parserClient)); + bool result = parser->parse(serializedPayload); + assert(result); + + // There can only be one element, so stripping the list + lua_pushnil(L); + lua_next(L, -2); + Lua::registerTableToString(L, -1); + + lua_replace(L, -3); + lua_settop(L, -2); + + return std::string("dom"); +} diff --git a/Sluift/ElementConvertors/DOMElementConvertor.h b/Sluift/ElementConvertors/DOMElementConvertor.h new file mode 100644 index 0000000..fdd7304 --- /dev/null +++ b/Sluift/ElementConvertors/DOMElementConvertor.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/LuaElementConvertor.h> +#include <Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h> +#include <Swiften/Parser/PlatformXMLParserFactory.h> + +namespace Swift { + class DOMElementConvertor : public LuaElementConvertor { + public: + DOMElementConvertor(); + virtual ~DOMElementConvertor(); + + virtual boost::shared_ptr<Element> convertFromLua(lua_State*, int index, const std::string& type) SWIFTEN_OVERRIDE; + virtual boost::optional<std::string> convertToLua(lua_State*, boost::shared_ptr<Element>) SWIFTEN_OVERRIDE; + + private: + PlatformXMLParserFactory parsers; + FullPayloadSerializerCollection serializers; + }; +} diff --git a/Sluift/ElementConvertors/DefaultElementConvertor.cpp b/Sluift/ElementConvertors/DefaultElementConvertor.cpp new file mode 100644 index 0000000..cc326df --- /dev/null +++ b/Sluift/ElementConvertors/DefaultElementConvertor.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/ElementConvertors/DefaultElementConvertor.h> + +#include <iostream> +#include <typeinfo> +#include <string> + +using namespace Swift; + +DefaultElementConvertor::DefaultElementConvertor() { +} + +DefaultElementConvertor::~DefaultElementConvertor() { +} + +boost::shared_ptr<Element> DefaultElementConvertor::convertFromLua(lua_State*, int, const std::string& type) { + std::cerr << "Warning: Unable to convert type '" << type << "'" << std::endl; + return boost::shared_ptr<Element>(); +} + +boost::optional<std::string> DefaultElementConvertor::convertToLua(lua_State*, boost::shared_ptr<Element>) { + // Should have been handled by the raw XML convertor + assert(false); + return NO_RESULT; +} diff --git a/Sluift/ElementConvertors/DefaultElementConvertor.h b/Sluift/ElementConvertors/DefaultElementConvertor.h new file mode 100644 index 0000000..5a2975b --- /dev/null +++ b/Sluift/ElementConvertors/DefaultElementConvertor.h @@ -0,0 +1,22 @@ +/* + * 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/LuaElementConvertor.h> + +namespace Swift { + class DefaultElementConvertor : public LuaElementConvertor { + public: + DefaultElementConvertor(); + virtual ~DefaultElementConvertor(); + + virtual boost::shared_ptr<Element> convertFromLua(lua_State*, int index, const std::string& type) SWIFTEN_OVERRIDE; + virtual boost::optional<std::string> convertToLua(lua_State*, boost::shared_ptr<Element>) SWIFTEN_OVERRIDE; + }; +} diff --git a/Sluift/ElementConvertors/DelayConvertor.cpp b/Sluift/ElementConvertors/DelayConvertor.cpp new file mode 100644 index 0000000..09789e9 --- /dev/null +++ b/Sluift/ElementConvertors/DelayConvertor.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2014 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/ElementConvertors/DelayConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <boost/numeric/conversion/cast.hpp> +#include <Sluift/Lua/Check.h> +#include <Swiften/Base/DateTime.h> + +using namespace Swift; + +DelayConvertor::DelayConvertor() : GenericLuaElementConvertor<Delay>("delay") { +} + +DelayConvertor::~DelayConvertor() { +} + +boost::shared_ptr<Delay> DelayConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<Delay> result = boost::make_shared<Delay>(); + lua_getfield(L, -1, "stamp"); + if (lua_isstring(L, -1)) { + result->setStamp(stringToDateTime(lua_tostring(L, -1))); + } + lua_pop(L, 1); + + lua_getfield(L, -1, "from"); + if (lua_isstring(L, -1)) { + result->setFrom(lua_tostring(L, -1)); + } + lua_pop(L, 1); + return result; +} + +void DelayConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<Delay> payload) { + lua_createtable(L, 0, 0); + if (payload->getFrom()) { + lua_pushstring(L, (*payload->getFrom()).toString().c_str()); + lua_setfield(L, -2, "from"); + } + lua_pushstring(L, dateTimeToString(payload->getStamp()).c_str()); + lua_setfield(L, -2, "stamp"); +} diff --git a/Sluift/ElementConvertors/DelayConvertor.h b/Sluift/ElementConvertors/DelayConvertor.h new file mode 100644 index 0000000..0e6640c --- /dev/null +++ b/Sluift/ElementConvertors/DelayConvertor.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2014 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/Delay.h> + +namespace Swift { + class DelayConvertor : public GenericLuaElementConvertor<Delay> { + public: + DelayConvertor(); + virtual ~DelayConvertor(); + + virtual boost::shared_ptr<Delay> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<Delay>) SWIFTEN_OVERRIDE; + }; +} diff --git a/Sluift/ElementConvertors/DiscoInfoConvertor.cpp b/Sluift/ElementConvertors/DiscoInfoConvertor.cpp new file mode 100644 index 0000000..b4bd2e1 --- /dev/null +++ b/Sluift/ElementConvertors/DiscoInfoConvertor.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/ElementConvertors/DiscoInfoConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <boost/numeric/conversion/cast.hpp> +#include <Sluift/Lua/LuaUtils.h> + +using namespace Swift; + +DiscoInfoConvertor::DiscoInfoConvertor() : GenericLuaElementConvertor<DiscoInfo>("disco_info") { +} + +DiscoInfoConvertor::~DiscoInfoConvertor() { +} + +boost::shared_ptr<DiscoInfo> DiscoInfoConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<DiscoInfo> result = boost::make_shared<DiscoInfo>(); + if (boost::optional<std::string> value = Lua::getStringField(L, -1, "node")) { + result->setNode(*value); + } + + lua_getfield(L, -1, "identities"); + if (lua_istable(L, -1)) { + for (lua_pushnil(L); lua_next(L, -2); ) { + result->addIdentity(DiscoInfo::Identity( + Lua::getStringField(L, -1, "name").get_value_or(""), + Lua::getStringField(L, -1, "category").get_value_or("client"), + Lua::getStringField(L, -1, "type").get_value_or("pc"), + Lua::getStringField(L, -1, "language").get_value_or(""))); + lua_pop(L, 1); + } + } + lua_pop(L, 1); + + lua_getfield(L, -1, "features"); + if (lua_istable(L, -1)) { + for (lua_pushnil(L); lua_next(L, -2); ) { + if (lua_isstring(L, -1)) { + result->addFeature(lua_tostring(L, -1)); + } + lua_pop(L, 1); + } + } + lua_pop(L, 1); + + // TODO: Extension + + return result; +} + +void DiscoInfoConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<DiscoInfo> payload) { + lua_newtable(L); + if (!payload->getNode().empty()) { + lua_pushstring(L, payload->getNode().c_str()); + lua_setfield(L, -2, "node"); + } + + const std::vector<DiscoInfo::Identity>& identities = payload->getIdentities(); + if (!identities.empty()) { + lua_createtable(L, boost::numeric_cast<int>(identities.size()), 0); + for (size_t i = 0; i < identities.size(); ++i) { + lua_createtable(L, 0, 0); + if (!identities[i].getName().empty()) { + lua_pushstring(L, identities[i].getName().c_str()); + lua_setfield(L, -2, "name"); + } + if (!identities[i].getCategory().empty()) { + lua_pushstring(L, identities[i].getCategory().c_str()); + lua_setfield(L, -2, "category"); + } + if (!identities[i].getType().empty()) { + lua_pushstring(L, identities[i].getType().c_str()); + lua_setfield(L, -2, "type"); + } + if (!identities[i].getLanguage().empty()) { + lua_pushstring(L, identities[i].getLanguage().c_str()); + lua_setfield(L, -2, "language"); + } + lua_rawseti(L, -2, boost::numeric_cast<int>(i+1)); + } + lua_setfield(L, -2, "identities"); + } + + const std::vector<std::string>& features = payload->getFeatures(); + if (!features.empty()) { + lua_createtable(L, boost::numeric_cast<int>(features.size()), 0); + for (size_t i = 0; i < features.size(); ++i) { + lua_pushstring(L, features[i].c_str()); + lua_rawseti(L, -2, boost::numeric_cast<int>(i+1)); + } + lua_setfield(L, -2, "features"); + } + + // TODO: Extension +} + +boost::optional<LuaElementConvertor::Documentation> DiscoInfoConvertor::getDocumentation() const { + return Documentation( + "DiscoInfo", + "Represents `disco#info` service discovery data.\n\n" + "This table has the following structure:\n\n" + "- `node`: string\n" + "- `identities`: array(table)\n" + " - `name`: string\n" + " - `category`: string\n" + " - `type`: string\n" + " - `language`: string\n" + "- `features`: array(string)\n" + ); +} diff --git a/Sluift/ElementConvertors/DiscoInfoConvertor.h b/Sluift/ElementConvertors/DiscoInfoConvertor.h new file mode 100644 index 0000000..4cf9c71 --- /dev/null +++ b/Sluift/ElementConvertors/DiscoInfoConvertor.h @@ -0,0 +1,24 @@ +/* + * 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/DiscoInfo.h> + +namespace Swift { + class DiscoInfoConvertor : public GenericLuaElementConvertor<DiscoInfo> { + public: + DiscoInfoConvertor(); + virtual ~DiscoInfoConvertor(); + + virtual boost::shared_ptr<DiscoInfo> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<DiscoInfo>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + }; +} diff --git a/Sluift/ElementConvertors/DiscoItemsConvertor.cpp b/Sluift/ElementConvertors/DiscoItemsConvertor.cpp new file mode 100644 index 0000000..9049912 --- /dev/null +++ b/Sluift/ElementConvertors/DiscoItemsConvertor.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/ElementConvertors/DiscoItemsConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <boost/numeric/conversion/cast.hpp> +#include <Sluift/Lua/LuaUtils.h> + +using namespace Swift; + +DiscoItemsConvertor::DiscoItemsConvertor() : GenericLuaElementConvertor<DiscoItems>("disco_items") { +} + +DiscoItemsConvertor::~DiscoItemsConvertor() { +} + +boost::shared_ptr<DiscoItems> DiscoItemsConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<DiscoItems> result = boost::make_shared<DiscoItems>(); + if (boost::optional<std::string> value = Lua::getStringField(L, -1, "node")) { + result->setNode(*value); + } + lua_getfield(L, -1, "items"); + if (lua_istable(L, -1)) { + for (lua_pushnil(L); lua_next(L, -2); ) { + result->addItem(DiscoItems::Item( + Lua::getStringField(L, -1, "name").get_value_or(""), + JID(Lua::getStringField(L, -1, "jid").get_value_or("")), + Lua::getStringField(L, -1, "node").get_value_or(""))); + lua_pop(L, 1); + } + } + lua_pop(L, 1); + return result; +} + +void DiscoItemsConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<DiscoItems> payload) { + lua_newtable(L); + if (!payload->getNode().empty()) { + lua_pushstring(L, payload->getNode().c_str()); + lua_setfield(L, -2, "node"); + } + const std::vector<DiscoItems::Item>& items = payload->getItems(); + if (!items.empty()) { + lua_createtable(L, boost::numeric_cast<int>(items.size()), 0); + for (size_t i = 0; i < items.size(); ++i) { + lua_createtable(L, 0, 0); + if (!items[i].getName().empty()) { + lua_pushstring(L, items[i].getName().c_str()); + lua_setfield(L, -2, "name"); + } + if (!items[i].getNode().empty()) { + lua_pushstring(L, items[i].getNode().c_str()); + lua_setfield(L, -2, "node"); + } + if (items[i].getJID().isValid()) { + lua_pushstring(L, items[i].getJID().toString().c_str()); + lua_setfield(L, -2, "jid"); + } + lua_rawseti(L, -2, boost::numeric_cast<int>(i+1)); + } + lua_setfield(L, -2, "items"); + } +} diff --git a/Sluift/ElementConvertors/DiscoItemsConvertor.h b/Sluift/ElementConvertors/DiscoItemsConvertor.h new file mode 100644 index 0000000..8972a84 --- /dev/null +++ b/Sluift/ElementConvertors/DiscoItemsConvertor.h @@ -0,0 +1,23 @@ +/* + * 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/DiscoItems.h> + +namespace Swift { + class DiscoItemsConvertor : public GenericLuaElementConvertor<DiscoItems> { + public: + DiscoItemsConvertor(); + virtual ~DiscoItemsConvertor(); + + virtual boost::shared_ptr<DiscoItems> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<DiscoItems>) SWIFTEN_OVERRIDE; + }; +} diff --git a/Sluift/ElementConvertors/ElementConvertors.ipp b/Sluift/ElementConvertors/ElementConvertors.ipp new file mode 100644 index 0000000..ef2416c --- /dev/null +++ b/Sluift/ElementConvertors/ElementConvertors.ipp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2013-2014 Remko Tronçon and Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubRetractConvertor.h> +#include <Sluift/ElementConvertors/PubSubAffiliationsConvertor.h> +#include <Sluift/ElementConvertors/PubSubPublishConvertor.h> +#include <Sluift/ElementConvertors/PubSubItemsConvertor.h> +#include <Sluift/ElementConvertors/PubSubOwnerRedirectConvertor.h> +#include <Sluift/ElementConvertors/PubSubEventRedirectConvertor.h> +#include <Sluift/ElementConvertors/UserTuneConvertor.h> +#include <Sluift/ElementConvertors/PubSubConfigureConvertor.h> +#include <Sluift/ElementConvertors/PubSubEventDisassociateConvertor.h> +#include <Sluift/ElementConvertors/PubSubOwnerAffiliationsConvertor.h> +#include <Sluift/ElementConvertors/PubSubOwnerConfigureConvertor.h> +#include <Sluift/ElementConvertors/UserLocationConvertor.h> +#include <Sluift/ElementConvertors/PubSubSubscribeOptionsConvertor.h> +#include <Sluift/ElementConvertors/PubSubOwnerSubscriptionsConvertor.h> +#include <Sluift/ElementConvertors/PubSubDefaultConvertor.h> +#include <Sluift/ElementConvertors/PubSubEventCollectionConvertor.h> +#include <Sluift/ElementConvertors/PubSubEventSubscriptionConvertor.h> +#include <Sluift/ElementConvertors/PubSubEventRetractConvertor.h> +#include <Sluift/ElementConvertors/PubSubItemConvertor.h> +#include <Sluift/ElementConvertors/PubSubUnsubscribeConvertor.h> +#include <Sluift/ElementConvertors/PubSubEventDeleteConvertor.h> +#include <Sluift/ElementConvertors/PubSubCreateConvertor.h> +#include <Sluift/ElementConvertors/PubSubOwnerPurgeConvertor.h> +#include <Sluift/ElementConvertors/PubSubEventItemsConvertor.h> +#include <Sluift/ElementConvertors/PubSubOptionsConvertor.h> +#include <Sluift/ElementConvertors/PubSubEventItemConvertor.h> +#include <Sluift/ElementConvertors/PubSubOwnerSubscriptionConvertor.h> +#include <Sluift/ElementConvertors/IsodeIQDelegationConvertor.h> +#include <Sluift/ElementConvertors/PubSubOwnerAffiliationConvertor.h> +#include <Sluift/ElementConvertors/PubSubEventPurgeConvertor.h> +#include <Sluift/ElementConvertors/PubSubAffiliationConvertor.h> +#include <Sluift/ElementConvertors/PubSubSubscribeConvertor.h> +#include <Sluift/ElementConvertors/PubSubOwnerDeleteConvertor.h> +#include <Sluift/ElementConvertors/PubSubOwnerDefaultConvertor.h> +#include <Sluift/ElementConvertors/PubSubSubscriptionsConvertor.h> +#include <Sluift/ElementConvertors/PubSubEventAssociateConvertor.h> +#include <Sluift/ElementConvertors/PubSubSubscriptionConvertor.h> +#include <Sluift/ElementConvertors/SecurityLabelConvertor.h> +#include <Sluift/ElementConvertors/PubSubEventConfigurationConvertor.h> + +void LuaElementConvertors::registerConvertors() { + convertors.push_back(boost::make_shared<PubSubRetractConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubAffiliationsConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubPublishConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubItemsConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubOwnerRedirectConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubEventRedirectConvertor>(this)); + convertors.push_back(boost::make_shared<UserTuneConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubConfigureConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubEventDisassociateConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubOwnerAffiliationsConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubOwnerConfigureConvertor>(this)); + convertors.push_back(boost::make_shared<UserLocationConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubSubscribeOptionsConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubOwnerSubscriptionsConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubDefaultConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubEventCollectionConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubEventSubscriptionConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubEventRetractConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubItemConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubUnsubscribeConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubEventDeleteConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubCreateConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubOwnerPurgeConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubEventItemsConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubOptionsConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubEventItemConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubOwnerSubscriptionConvertor>(this)); + convertors.push_back(boost::make_shared<IsodeIQDelegationConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubOwnerAffiliationConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubEventPurgeConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubAffiliationConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubSubscribeConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubOwnerDeleteConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubOwnerDefaultConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubSubscriptionsConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubEventAssociateConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubSubscriptionConvertor>(this)); + convertors.push_back(boost::make_shared<SecurityLabelConvertor>(this)); + convertors.push_back(boost::make_shared<PubSubEventConfigurationConvertor>(this)); +} diff --git a/Sluift/ElementConvertors/FormConvertor.cpp b/Sluift/ElementConvertors/FormConvertor.cpp new file mode 100644 index 0000000..26a7d29 --- /dev/null +++ b/Sluift/ElementConvertors/FormConvertor.cpp @@ -0,0 +1,359 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/ElementConvertors/FormConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <boost/numeric/conversion/cast.hpp> +#include <Sluift/Lua/Check.h> +#include <Sluift/Lua/Value.h> +#include <Swiften/Base/foreach.h> +#include <boost/assign/list_of.hpp> +#include <sstream> + +using namespace Swift; + +namespace { + int formIndex(lua_State* L) { + lua_getfield(L, 1, "fields"); + if (lua_type(L, -1) != LUA_TTABLE) { + return 0; + } + int index = Lua::absoluteOffset(L, -1); + lua_pushnil(L); + for (lua_pushnil(L); lua_next(L, index) != 0; ) { + lua_getfield(L, -1, "name"); + if (lua_equal(L, -1, 2)) { + lua_pop(L, 1); + return 1; + } + lua_pop(L, 2); + } + return 0; + } + + int formNewIndex(lua_State* L) { + lua_getfield(L, 1, "fields"); + bool foundField = false; + if (lua_type(L, -1) == LUA_TTABLE) { + for (lua_pushnil(L); lua_next(L, -2) != 0; ) { + lua_getfield(L, -1, "name"); + if (lua_equal(L, -1, 2)) { + lua_pushvalue(L, 3); + lua_setfield(L, -3, "value"); + foundField = true; + lua_pop(L, 3); + break; + } + lua_pop(L, 2); + } + } + lua_pop(L, 1); + + if (!foundField) { + lua_pushvalue(L, 2); + lua_pushvalue(L, 3); + lua_rawset(L, 1); + } + return 0; + } + + Lua::Table convertFieldToLua(boost::shared_ptr<FormField> field) { + Lua::Table luaField = boost::assign::map_list_of("name", Lua::valueRef(field->getName())); + std::string type; + switch (field->getType()) { + case FormField::UnknownType: type = ""; break; + case FormField::BooleanType: type = "boolean"; break; + case FormField::FixedType: type = "fixed"; break; + case FormField::HiddenType: type = "hidden"; break; + case FormField::ListSingleType: type = "list-single"; break; + case FormField::TextMultiType: type = "text-multi"; break; + case FormField::TextPrivateType: type = "text-private"; break; + case FormField::TextSingleType: type = "text-single"; break; + case FormField::JIDSingleType: type = "jid-single"; break; + case FormField::JIDMultiType: type = "jid-multi"; break; + case FormField::ListMultiType: type = "list-multi"; break; + } + if (!type.empty()) { + luaField["type"] = Lua::valueRef(type); + } + if (!field->getLabel().empty()) { + luaField["label"] = Lua::valueRef(field->getLabel()); + } + if (field->getRequired()) { + luaField["required"] = Lua::boolRef(field->getRequired()); + } + if (!field->getDescription().empty()) { + luaField["description"] = Lua::valueRef(field->getDescription()); + } + if (field->getType() == FormField::BooleanType) { + luaField["value"] = Lua::boolRef(field->getBoolValue()); + } + else if (field->getValues().size() > 1) { + luaField["value"] = Lua::valueRef(Lua::Array(field->getValues().begin(), field->getValues().end())); + } + else if (field->getValues().size() == 1) { + luaField["value"] = Lua::valueRef(field->getValues()[0]); + } + if (!field->getOptions().empty()) { + Lua::Array options; + foreach(const FormField::Option& option, field->getOptions()) { + Lua::Table luaOption = boost::assign::map_list_of + ("label", Lua::valueRef(option.label)) + ("value", Lua::valueRef(option.value)); + options.push_back(luaOption); + } + luaField["options"] = valueRef(options); + } + return luaField; + } + + Lua::Array convertFieldListToLua(const std::vector< boost::shared_ptr<FormField> >& fieldList) { + Lua::Array fields; + foreach(boost::shared_ptr<FormField> field, fieldList) { + fields.push_back(convertFieldToLua(field)); + } + return fields; + } + + + boost::shared_ptr<FormField> convertFieldFromLua(lua_State* L) { + boost::shared_ptr<FormField> result = boost::make_shared<FormField>(); + FormField::Type fieldType = FormField::UnknownType; + boost::optional<std::string> type = Lua::getStringField(L, -1, "type"); + if (type) { + if (*type == "boolean") { + fieldType = FormField::BooleanType; + } + if (*type == "fixed") { + fieldType = FormField::FixedType; + } + if (*type == "hidden") { + fieldType = FormField::HiddenType; + } + if (*type == "list-single") { + fieldType = FormField::ListSingleType; + } + if (*type == "text-multi") { + fieldType = FormField::TextMultiType; + } + if (*type == "text-private") { + fieldType = FormField::TextPrivateType; + } + if (*type == "text-single") { + fieldType = FormField::TextSingleType; + } + if (*type == "jid-single") { + fieldType = FormField::JIDSingleType; + } + if (*type == "jid-multi") { + fieldType = FormField::JIDMultiType; + } + if (*type == "list-multi") { + fieldType = FormField::ListMultiType; + } + } + result->setType(fieldType); + if (boost::optional<std::string> name = Lua::getStringField(L, -1, "name")) { + result->setName(*name); + } + if (boost::optional<std::string> description = Lua::getStringField(L, -1, "description")) { + result->setDescription(*description); + } + if (boost::optional<std::string> label = Lua::getStringField(L, -1, "label")) { + result->setLabel(*label); + } + if (boost::optional<bool> required = Lua::getBooleanField(L, -1, "required")) { + result->setRequired(*required); + } + if (boost::optional<std::string> value = Lua::getStringField(L, -1, "value")) { + result->addValue(*value); + } + else if (boost::optional<bool> value = Lua::getBooleanField(L, -1, "value")) { + result->setBoolValue(*value); + } + else { + lua_getfield(L, -1, "value"); + if (lua_istable(L, -1)) { + for (lua_pushnil(L); lua_next(L, -2); ) { + if (lua_isstring(L, -1)) { + result->addValue(lua_tostring(L, -1)); + } + lua_pop(L, 1); + } + } + lua_pop(L, 1); + } + lua_getfield(L, -1, "options"); + if (lua_istable(L, -1)) { + for (lua_pushnil(L); lua_next(L, -2); ) { + if (lua_istable(L, -1)) { + FormField::Option option("", ""); + if (boost::optional<std::string> value = Lua::getStringField(L, -1, "value")) { + option.value = *value; + } + if (boost::optional<std::string> label = Lua::getStringField(L, -1, "label")) { + option.label = *label; + } + result->addOption(option); + } + lua_pop(L, 1); + } + } + lua_pop(L, 1); + return result; + } + + std::vector< boost::shared_ptr<FormField> > convertFieldListFromLua(lua_State* L) { + std::vector< boost::shared_ptr<FormField> > result; + for (lua_pushnil(L); lua_next(L, -2);) { + result.push_back(convertFieldFromLua(L)); + lua_pop(L, 1); + } + return result; + } + + boost::shared_ptr<Form> convertFormFromLua(lua_State* L) { + boost::shared_ptr<Form> result = boost::make_shared<Form>(); + if (boost::optional<std::string> title = Lua::getStringField(L, -1, "title")) { + result->setTitle(*title); + } + if (boost::optional<std::string> instructions = Lua::getStringField(L, -1, "instructions")) { + result->setInstructions(*instructions); + } + if (boost::optional<std::string> type = Lua::getStringField(L, -1, "type")) { + Form::Type formType = Form::FormType; + if (*type == "submit") { + formType = Form::SubmitType; + } + else if (*type == "cancel") { + formType = Form::CancelType; + } + else if (*type == "result") { + formType = Form::ResultType; + } + result->setType(formType); + } + + lua_getfield(L, -1, "fields"); + if (lua_istable(L, -1)) { + foreach (boost::shared_ptr<FormField> formField, convertFieldListFromLua(L)) { + result->addField(formField); + } + } + lua_pop(L, 1); + + lua_getfield(L, -1, "reported_fields"); + if (lua_istable(L, -1)) { + foreach (boost::shared_ptr<FormField> formField, convertFieldListFromLua(L)) { + result->addReportedField(formField); + } + } + lua_pop(L, 1); + + lua_getfield(L, -1, "items"); + if (lua_istable(L, -1)) { + for (lua_pushnil(L); lua_next(L, -2);) { + result->addItem(convertFieldListFromLua(L)); + lua_pop(L, 1); + } + } + lua_pop(L, 1); + + return result; + } + + void convertFormToLua(lua_State* L, boost::shared_ptr<Form> payload) { + std::string type; + switch (payload->getType()) { + case Form::FormType: type = "form"; break; + case Form::SubmitType: type = "submit"; break; + case Form::CancelType: type = "cancel"; break; + case Form::ResultType: type = "result"; break; + } + + Lua::Table result = boost::assign::map_list_of("type", Lua::valueRef(type)); + if (!payload->getTitle().empty()) { + result["title"] = Lua::valueRef(payload->getTitle()); + } + if (!payload->getInstructions().empty()) { + result["instructions"] = Lua::valueRef(payload->getInstructions()); + } + if (!payload->getFields().empty()) { + result["fields"] = valueRef(convertFieldListToLua(payload->getFields())); + } + if (!payload->getReportedFields().empty()) { + result["reported_fields"] = valueRef(convertFieldListToLua(payload->getReportedFields())); + } + + if (!payload->getItems().empty()) { + Lua::Array luaItems; + foreach(const Form::FormItem& item, payload->getItems()) { + if (!item.empty()) { + luaItems.push_back(convertFieldListToLua(item)); + } + } + result["items"] = valueRef(luaItems); + } + + Lua::pushValue(L, result); + + lua_newtable(L); + lua_pushcfunction(L, formIndex); + lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, formNewIndex); + lua_setfield(L, -2, "__newindex"); + lua_setmetatable(L, -2); + } + + int createSubmission(lua_State* L) { + boost::shared_ptr<Form> form = convertFormFromLua(L); + + // Remove all redundant elements + form->setInstructions(""); + form->setTitle(""); + form->clearItems(); + form->clearReportedFields(); + std::vector< boost::shared_ptr<FormField> > fields(form->getFields()); + form->clearFields(); + foreach (boost::shared_ptr<FormField> field, fields) { + if (field->getType() == FormField::FixedType) { + continue; + } + field->clearOptions(); + field->setLabel(""); + field->setType(FormField::UnknownType); + field->setDescription(""); + form->addField(field); + } + form->setType(Form::SubmitType); + + // Convert back + convertFormToLua(L, form); + Lua::registerTableToString(L, -1); + + return 1; + } +} + +FormConvertor::FormConvertor() : GenericLuaElementConvertor<Form>("form") { +} + +FormConvertor::~FormConvertor() { +} + +boost::shared_ptr<Form> FormConvertor::doConvertFromLua(lua_State* L) { + return convertFormFromLua(L); +} + +void FormConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<Form> payload) { + convertFormToLua(L, payload); + + lua_pushstring(L, "create_submission"); + lua_pushcfunction(L, createSubmission); + lua_rawset(L, -3); +} diff --git a/Sluift/ElementConvertors/FormConvertor.h b/Sluift/ElementConvertors/FormConvertor.h new file mode 100644 index 0000000..eaccf74 --- /dev/null +++ b/Sluift/ElementConvertors/FormConvertor.h @@ -0,0 +1,23 @@ +/* + * 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/Form.h> + +namespace Swift { + class FormConvertor : public GenericLuaElementConvertor<Form> { + public: + FormConvertor(); + virtual ~FormConvertor(); + + virtual boost::shared_ptr<Form> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<Form>) SWIFTEN_OVERRIDE; + }; +} diff --git a/Sluift/ElementConvertors/ForwardedConvertor.cpp b/Sluift/ElementConvertors/ForwardedConvertor.cpp new file mode 100644 index 0000000..a6f78d0 --- /dev/null +++ b/Sluift/ElementConvertors/ForwardedConvertor.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2014 Kevin Smith and Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <boost/numeric/conversion/cast.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <lua.hpp> +#include <Swiften/Base/foreach.h> +#include <Sluift/ElementConvertors/ForwardedConvertor.h> +#include <Sluift/LuaElementConvertors.h> +#include <Swiften/Elements/Delay.h> +#include <Swiften/Elements/IQ.h> +#include <Swiften/Elements/Presence.h> +#include <Swiften/Elements/Message.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +ForwardedConvertor::ForwardedConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<Forwarded>("forwarded"), + convertors(convertors) { +} + +ForwardedConvertor::~ForwardedConvertor() { +} + +boost::shared_ptr<Forwarded> ForwardedConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<Forwarded> result = boost::make_shared<Forwarded>(); + lua_getfield(L, -1, "delay"); + if (!lua_isnil(L, -1)) { + boost::shared_ptr<Delay> delay = boost::dynamic_pointer_cast<Delay>(convertors->convertFromLuaUntyped(L, -1, "delay")); + if (!!delay) { + result->setDelay(delay); + } + } + lua_pop(L, 1); + lua_getfield(L, -1, "stanza"); + if (!lua_isnil(L, -1)) { + boost::shared_ptr<Stanza> stanza = boost::dynamic_pointer_cast<Stanza>(convertors->convertFromLua(L, -1)); + if (!!stanza) { + result->setStanza(stanza); + } + lua_pop(L, 1); + return result; + } + return result; +} + +void ForwardedConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<Forwarded> payload) { + lua_createtable(L, 0, 0); + if (convertors->convertToLuaUntyped(L, payload->getDelay()) > 0) { + lua_setfield(L, -2, "delay"); + } + boost::shared_ptr<Stanza> stanza = payload->getStanza(); + if (!!stanza) { + if (convertors->convertToLua(L, stanza) > 0) { + lua_setfield(L, -2, "stanza"); + } + } +} + +boost::optional<LuaElementConvertor::Documentation> ForwardedConvertor::getDocumentation() const { + return Documentation( + "Forwarded", + "This table has the following fields:\n\n" + "- `delay`: @{Delay} (Optional)\n" + "- `stanza`: @{Stanza} (Optional)\n" + ); +} diff --git a/Sluift/ElementConvertors/ForwardedConvertor.h b/Sluift/ElementConvertors/ForwardedConvertor.h new file mode 100644 index 0000000..3ee4498 --- /dev/null +++ b/Sluift/ElementConvertors/ForwardedConvertor.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2014 Kevin Smith and Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Base/Override.h> +#include <Swiften/Elements/Forwarded.h> + +namespace Swift { + class LuaElementConvertors; + + class ForwardedConvertor : public GenericLuaElementConvertor<Forwarded> { + public: + ForwardedConvertor(LuaElementConvertors* convertors); + virtual ~ForwardedConvertor(); + + virtual boost::shared_ptr<Forwarded> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<Forwarded>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + private: + LuaElementConvertors* convertors; + }; +} + diff --git a/Sluift/ElementConvertors/IQConvertor.cpp b/Sluift/ElementConvertors/IQConvertor.cpp new file mode 100644 index 0000000..e535e60 --- /dev/null +++ b/Sluift/ElementConvertors/IQConvertor.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2014 Kevin Smith and Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <boost/smart_ptr/make_shared.hpp> +#include <lua.hpp> +#include <Sluift/ElementConvertors/IQConvertor.h> +#include <Sluift/LuaElementConvertors.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +IQConvertor::IQConvertor(LuaElementConvertors* convertors) : + StanzaConvertor<IQ>("iq"), + convertors(convertors) { +} + +IQConvertor::~IQConvertor() { +} + +boost::shared_ptr<IQ> IQConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<IQ> result = getStanza(L, convertors); + lua_getfield(L, -1, "type"); + if (lua_isstring(L, -1)) { + result->setType(IQConvertor::convertIQTypeFromString(lua_tostring(L, -1))); + } + lua_pop(L, 1); + return result; +} + +void IQConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<IQ> stanza) { + pushStanza(L, stanza, convertors); + const std::string type = IQConvertor::convertIQTypeToString(stanza->getType()); + lua_pushstring(L, type.c_str()); + lua_setfield(L, -2, "type"); +} + +boost::optional<LuaElementConvertor::Documentation> IQConvertor::getDocumentation() const { + return Documentation( + "IQ", + "This table has the following fields:\n\n" + "- `type`: string\n" + "- `id`: string\n" + "- `from`: string\n" + "- `to`: string\n" + "- `payloads`: array<@{Payload}>\n" + ); +} + +std::string IQConvertor::convertIQTypeToString(IQ::Type type) { + switch (type) { + case IQ::Get: return "get"; + case IQ::Set: return "set"; + case IQ::Result: return "result"; + case IQ::Error: return "error"; + } + assert(false); + return ""; +} + +IQ::Type IQConvertor::convertIQTypeFromString(const std::string& type) { + if (type == "get") { + return IQ::Get; + } + else if (type == "set") { + return IQ::Set; + } + else { + throw Lua::Exception("Illegal query type: '" + type + "'"); + } +} diff --git a/Sluift/ElementConvertors/IQConvertor.h b/Sluift/ElementConvertors/IQConvertor.h new file mode 100644 index 0000000..d22df08 --- /dev/null +++ b/Sluift/ElementConvertors/IQConvertor.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2014 Kevin Smith and Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Sluift/ElementConvertors/StanzaConvertor.h> +#include <Swiften/Base/Override.h> +#include <Swiften/Elements/IQ.h> + +namespace Swift { + class LuaElementConvertors; + + class IQConvertor : public StanzaConvertor<IQ> { + public: + IQConvertor(LuaElementConvertors* convertors); + virtual ~IQConvertor(); + + virtual boost::shared_ptr<IQ> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<IQ>) SWIFTEN_OVERRIDE; + + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + static std::string convertIQTypeToString(IQ::Type type); + static IQ::Type convertIQTypeFromString(const std::string& type); + + private: + LuaElementConvertors* convertors; + }; +} + diff --git a/Sluift/ElementConvertors/IsodeIQDelegationConvertor.cpp b/Sluift/ElementConvertors/IsodeIQDelegationConvertor.cpp new file mode 100644 index 0000000..889c32b --- /dev/null +++ b/Sluift/ElementConvertors/IsodeIQDelegationConvertor.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2014 Remko Tronçon and Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/IsodeIQDelegationConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> + + +#include <Sluift/LuaElementConvertors.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +IsodeIQDelegationConvertor::IsodeIQDelegationConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<IsodeIQDelegation>("isode_iq_delegation"), + convertors(convertors) { +} + +IsodeIQDelegationConvertor::~IsodeIQDelegationConvertor() { +} + +boost::shared_ptr<IsodeIQDelegation> IsodeIQDelegationConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<IsodeIQDelegation> result = boost::make_shared<IsodeIQDelegation>(); + lua_getfield(L, -1, "forward"); + if (!lua_isnil(L, -1)) { + if (boost::shared_ptr<Forwarded> payload = boost::dynamic_pointer_cast<Forwarded>(convertors->convertFromLuaUntyped(L, -1, "forwarded"))) { + result->setForward(payload); + } + } + lua_pop(L, 1); + return result; +} + +void IsodeIQDelegationConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<IsodeIQDelegation> payload) { + lua_createtable(L, 0, 0); + if (convertors->convertToLuaUntyped(L, payload->getForward()) > 0) { + lua_setfield(L, -2, "forward"); + } +} + +boost::optional<LuaElementConvertor::Documentation> IsodeIQDelegationConvertor::getDocumentation() const { + return Documentation( + "IsodeIQDelegation", + "This table has the following fields:\n\n" + "- `forward`: @{Forwarded}\n" + ); +} diff --git a/Sluift/ElementConvertors/IsodeIQDelegationConvertor.h b/Sluift/ElementConvertors/IsodeIQDelegationConvertor.h new file mode 100644 index 0000000..079423d --- /dev/null +++ b/Sluift/ElementConvertors/IsodeIQDelegationConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2014 Remko Tronçon and Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/IsodeIQDelegation.h> + +namespace Swift { + class LuaElementConvertors; + + class IsodeIQDelegationConvertor : public GenericLuaElementConvertor<IsodeIQDelegation> { + public: + IsodeIQDelegationConvertor(LuaElementConvertors* convertors); + virtual ~IsodeIQDelegationConvertor(); + + virtual boost::shared_ptr<IsodeIQDelegation> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<IsodeIQDelegation>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/MAMArchivedConvertor.cpp b/Sluift/ElementConvertors/MAMArchivedConvertor.cpp new file mode 100644 index 0000000..39ac7df --- /dev/null +++ b/Sluift/ElementConvertors/MAMArchivedConvertor.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2014 Kevin Smith and Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <boost/numeric/conversion/cast.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <lua.hpp> +#include <Sluift/ElementConvertors/MAMArchivedConvertor.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +MAMArchivedConvertor::MAMArchivedConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<MAMArchived>("mam_archived"), + convertors(convertors) { +} + +MAMArchivedConvertor::~MAMArchivedConvertor() { +} + +boost::shared_ptr<MAMArchived> MAMArchivedConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<MAMArchived> result = boost::make_shared<MAMArchived>(); + lua_getfield(L, -1, "by"); + if (lua_isstring(L, -1)) { + result->setBy(JID(std::string(lua_tostring(L, -1)))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "id"); + if (lua_isstring(L, -1)) { + result->setID(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + return result; +} + +void MAMArchivedConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<MAMArchived> payload) { + lua_createtable(L, 0, 0); + lua_pushstring(L, payload->getBy().toString().c_str()); + lua_setfield(L, -2, "by"); + lua_pushstring(L, payload->getID().c_str()); + lua_setfield(L, -2, "id"); +} + +boost::optional<LuaElementConvertor::Documentation> MAMArchivedConvertor::getDocumentation() const { + return Documentation( + "MAMArchived", + "This table has the following fields:\n\n" + "- `by`: string\n" + "- `id`: string\n" + ); +} diff --git a/Sluift/ElementConvertors/MAMArchivedConvertor.h b/Sluift/ElementConvertors/MAMArchivedConvertor.h new file mode 100644 index 0000000..9d95e4a --- /dev/null +++ b/Sluift/ElementConvertors/MAMArchivedConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2014 Kevin Smith and Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Base/Override.h> +#include <Swiften/Elements/MAMArchived.h> + +namespace Swift { + class LuaElementConvertors; + + class MAMArchivedConvertor : public GenericLuaElementConvertor<MAMArchived> { + public: + MAMArchivedConvertor(LuaElementConvertors* convertors); + virtual ~MAMArchivedConvertor(); + + virtual boost::shared_ptr<MAMArchived> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<MAMArchived>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} + diff --git a/Sluift/ElementConvertors/MAMQueryConvertor.cpp b/Sluift/ElementConvertors/MAMQueryConvertor.cpp new file mode 100644 index 0000000..cf4f787 --- /dev/null +++ b/Sluift/ElementConvertors/MAMQueryConvertor.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2014 Kevin Smith and Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <boost/numeric/conversion/cast.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <lua.hpp> +#include <Sluift/ElementConvertors/MAMQueryConvertor.h> +#include <Sluift/LuaElementConvertors.h> +#include <Swiften/Elements/Form.h> +#include <Swiften/Elements/ResultSet.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +MAMQueryConvertor::MAMQueryConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<MAMQuery>("mam"), + convertors(convertors) { +} + +MAMQueryConvertor::~MAMQueryConvertor() { +} + +boost::shared_ptr<MAMQuery> MAMQueryConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<MAMQuery> result = boost::make_shared<MAMQuery>(); + lua_getfield(L, -1, "query_id"); + if (lua_isstring(L, -1)) { + result->setQueryID(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "form"); + if (!lua_isnil(L, -1)) { + boost::shared_ptr<Form> form = boost::dynamic_pointer_cast<Form>(convertors->convertFromLuaUntyped(L, -1, "form")); + if (!!form) { + result->setForm(form); + } + } + lua_pop(L, 1); + lua_getfield(L, -1, "result_set"); + if (!lua_isnil(L, -1)) { + boost::shared_ptr<ResultSet> resultSet = boost::dynamic_pointer_cast<ResultSet>(convertors->convertFromLuaUntyped(L, -1, "result_set")); + if (!!resultSet) { + result->setResultSet(resultSet); + } + } + lua_pop(L, 1); + return result; +} + +void MAMQueryConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<MAMQuery> payload) { + lua_createtable(L, 0, 0); + if (payload->getQueryID()) { + lua_pushstring(L, (*payload->getQueryID()).c_str()); + lua_setfield(L, -2, "query_id"); + } + if (convertors->convertToLuaUntyped(L, payload->getForm()) > 0) { + lua_setfield(L, -2, "form"); + } + if (convertors->convertToLuaUntyped(L, payload->getResultSet()) > 0) { + lua_setfield(L, -2, "result_set"); + } +} + +boost::optional<LuaElementConvertor::Documentation> MAMQueryConvertor::getDocumentation() const { + return Documentation( + "MAMQuery", + "This table has the following fields:\n\n" + "- `query_id`: string (Optional)\n" + "- `form`: string @{Form} (Optional)\n" + "- `result_set`: @{ResultSet} (Optional)\n" + ); +} diff --git a/Sluift/ElementConvertors/MAMQueryConvertor.h b/Sluift/ElementConvertors/MAMQueryConvertor.h new file mode 100644 index 0000000..92392e5 --- /dev/null +++ b/Sluift/ElementConvertors/MAMQueryConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2014 Kevin Smith and Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Base/Override.h> +#include <Swiften/Elements/MAMQuery.h> + +namespace Swift { + class LuaElementConvertors; + + class MAMQueryConvertor : public GenericLuaElementConvertor<MAMQuery> { + public: + MAMQueryConvertor(LuaElementConvertors* convertors); + virtual ~MAMQueryConvertor(); + + virtual boost::shared_ptr<MAMQuery> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<MAMQuery>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} + diff --git a/Sluift/ElementConvertors/MAMResultConvertor.cpp b/Sluift/ElementConvertors/MAMResultConvertor.cpp new file mode 100644 index 0000000..8ba4de7 --- /dev/null +++ b/Sluift/ElementConvertors/MAMResultConvertor.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014 Kevin Smith and Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <boost/numeric/conversion/cast.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <lua.hpp> +#include <Sluift/ElementConvertors/MAMResultConvertor.h> +#include <Sluift/LuaElementConvertors.h> +#include <Swiften/Elements/Forwarded.h> + + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +MAMResultConvertor::MAMResultConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<MAMResult>("mam_result"), + convertors(convertors) { +} + +MAMResultConvertor::~MAMResultConvertor() { +} + +boost::shared_ptr<MAMResult> MAMResultConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<MAMResult> result = boost::make_shared<MAMResult>(); + lua_getfield(L, -1, "payload"); + if (!lua_isnil(L, -1)) { + boost::shared_ptr<Forwarded> payload = boost::dynamic_pointer_cast<Forwarded>(convertors->convertFromLuaUntyped(L, -1, "payload")); + if (!!payload) { + result->setPayload(payload); + } + } + lua_pop(L, 1); + lua_getfield(L, -1, "id"); + if (lua_isstring(L, -1)) { + result->setID(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "query_id"); + if (lua_isstring(L, -1)) { + result->setQueryID(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + return result; +} + +void MAMResultConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<MAMResult> payload) { + lua_createtable(L, 0, 0); + if (convertors->convertToLuaUntyped(L, payload->getPayload()) > 0) { + lua_setfield(L, -2, "payload"); + } + lua_pushstring(L, payload->getID().c_str()); + lua_setfield(L, -2, "id"); + if (payload->getQueryID()) { + lua_pushstring(L, (*payload->getQueryID()).c_str()); + lua_setfield(L, -2, "query_id"); + } +} + +boost::optional<LuaElementConvertor::Documentation> MAMResultConvertor::getDocumentation() const { + return Documentation( + "MAMResult", + "This table has the following fields:\n\n" + "- `payload`: @{Forwarded}\n" + "- `id`: string\n" + "- `query_id`: string (Optional)\n" + ); +} diff --git a/Sluift/ElementConvertors/MAMResultConvertor.h b/Sluift/ElementConvertors/MAMResultConvertor.h new file mode 100644 index 0000000..153ffd8 --- /dev/null +++ b/Sluift/ElementConvertors/MAMResultConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2014 Kevin Smith and Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Base/Override.h> +#include <Swiften/Elements/MAMResult.h> + +namespace Swift { + class LuaElementConvertors; + + class MAMResultConvertor : public GenericLuaElementConvertor<MAMResult> { + public: + MAMResultConvertor(LuaElementConvertors* convertors); + virtual ~MAMResultConvertor(); + + virtual boost::shared_ptr<MAMResult> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<MAMResult>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} + diff --git a/Sluift/ElementConvertors/MessageConvertor.cpp b/Sluift/ElementConvertors/MessageConvertor.cpp new file mode 100644 index 0000000..690ef88 --- /dev/null +++ b/Sluift/ElementConvertors/MessageConvertor.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2014 Kevin Smith and Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <boost/smart_ptr/make_shared.hpp> +#include <lua.hpp> +#include <Sluift/ElementConvertors/MessageConvertor.h> +#include <Sluift/LuaElementConvertors.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +MessageConvertor::MessageConvertor(LuaElementConvertors* convertors) : + StanzaConvertor<Message>("message"), + convertors(convertors) { +} + +MessageConvertor::~MessageConvertor() { +} + +boost::shared_ptr<Message> MessageConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<Message> result = getStanza(L, convertors); + lua_getfield(L, -1, "type"); + if (lua_isstring(L, -1)) { + result->setType(convertMessageTypeFromString(lua_tostring(L, -1))); + } + lua_pop(L, 1); + return result; +} + +void MessageConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<Message> stanza) { + pushStanza(L, stanza, convertors); + const std::string type = convertMessageTypeToString(stanza->getType()); + lua_pushstring(L, type.c_str()); + lua_setfield(L, -2, "type"); +} + +boost::optional<LuaElementConvertor::Documentation> MessageConvertor::getDocumentation() const { + return Documentation( + "Message", + "This table has the following fields:\n\n" + "- `type`: string\n" + "- `id`: string\n" + "- `from`: string\n" + "- `to`: string\n" + "- `payloads`: array<@{Payload}>\n" + ); +} + +std::string MessageConvertor::convertMessageTypeToString(Message::Type type) { + switch (type) { + case Message::Normal: return "normal"; + case Message::Chat: return "chat"; + case Message::Error: return "error"; + case Message::Groupchat: return "groupchat"; + case Message::Headline: return "headline"; + } + assert(false); + return ""; +} + +Message::Type MessageConvertor::convertMessageTypeFromString(const std::string& type) { + if (type == "normal") { + return Message::Normal; + } + else if (type == "chat") { + return Message::Chat; + } + else if (type == "error") { + return Message::Error; + } + else if (type == "groupchat") { + return Message::Groupchat; + } + else if (type == "headline") { + return Message::Headline; + } + else { + throw Lua::Exception("Illegal message type: '" + type + "'"); + } +} diff --git a/Sluift/ElementConvertors/MessageConvertor.h b/Sluift/ElementConvertors/MessageConvertor.h new file mode 100644 index 0000000..b2167be --- /dev/null +++ b/Sluift/ElementConvertors/MessageConvertor.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2014 Kevin Smith and Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Sluift/ElementConvertors/StanzaConvertor.h> +#include <Swiften/Base/Override.h> +#include <Swiften/Elements/Message.h> + +namespace Swift { + class LuaElementConvertors; + + class MessageConvertor : public StanzaConvertor<Message> { + public: + MessageConvertor(LuaElementConvertors* convertors); + virtual ~MessageConvertor(); + + virtual boost::shared_ptr<Message> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<Message>) SWIFTEN_OVERRIDE; + + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + static std::string convertMessageTypeToString(Message::Type type); + static Message::Type convertMessageTypeFromString(const std::string& type); + + private: + LuaElementConvertors* convertors; + }; +} + diff --git a/Sluift/ElementConvertors/PresenceConvertor.cpp b/Sluift/ElementConvertors/PresenceConvertor.cpp new file mode 100644 index 0000000..823993b --- /dev/null +++ b/Sluift/ElementConvertors/PresenceConvertor.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2014 Kevin Smith and Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <boost/smart_ptr/make_shared.hpp> +#include <lua.hpp> +#include <Sluift/ElementConvertors/PresenceConvertor.h> +#include <Sluift/LuaElementConvertors.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PresenceConvertor::PresenceConvertor(LuaElementConvertors* convertors) : + StanzaConvertor<Presence>("presence"), + convertors(convertors) { +} + +PresenceConvertor::~PresenceConvertor() { +} + +boost::shared_ptr<Presence> PresenceConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<Presence> result = getStanza(L, convertors); + lua_getfield(L, -1, "type"); + if (lua_isstring(L, -1)) { + result->setType(convertPresenceTypeFromString(lua_tostring(L, -1))); + } + lua_pop(L, 1); + return result; +} + +void PresenceConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<Presence> stanza) { + pushStanza(L, stanza, convertors); + const std::string type = convertPresenceTypeToString(stanza->getType()); + lua_pushstring(L, type.c_str()); + lua_setfield(L, -2, "type"); +} + +boost::optional<LuaElementConvertor::Documentation> PresenceConvertor::getDocumentation() const { + return Documentation( + "Presence", + "This table has the following fields:\n\n" + "- `type`: string\n" + "- `id`: string\n" + "- `from`: string\n" + "- `to`: string\n" + "- `payloads`: array<@{Payload}>\n" + ); +} + +std::string PresenceConvertor::convertPresenceTypeToString(Presence::Type type) { + switch (type) { + case Presence::Available: return "available"; + case Presence::Error: return "error"; + case Presence::Probe: return "probe"; + case Presence::Subscribe: return "subscribe"; + case Presence::Subscribed: return "subscribed"; + case Presence::Unavailable: return "unavailable"; + case Presence::Unsubscribe: return "unsubscribe"; + case Presence::Unsubscribed: return "unsubscribed"; + } + assert(false); + return ""; +} + +Presence::Type PresenceConvertor::convertPresenceTypeFromString(const std::string& type) { + if (type == "available") { + return Presence::Available; + } + else if (type == "error") { + return Presence::Error; + } + else if (type == "probe") { + return Presence::Probe; + } + else if (type == "subscribe") { + return Presence::Subscribe; + } + else if (type == "subscribed") { + return Presence::Subscribed; + } + else if (type == "unavailable") { + return Presence::Unavailable; + } + else if (type == "unsubscribe") { + return Presence::Unsubscribe; + } + else if (type == "unsubscribed") { + return Presence::Unsubscribed; + } + else { + throw Lua::Exception("Illegal presence type: '" + type + "'"); + } +} diff --git a/Sluift/ElementConvertors/PresenceConvertor.h b/Sluift/ElementConvertors/PresenceConvertor.h new file mode 100644 index 0000000..d25d3a6 --- /dev/null +++ b/Sluift/ElementConvertors/PresenceConvertor.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2014 Kevin Smith and Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Sluift/ElementConvertors/StanzaConvertor.h> +#include <Swiften/Base/Override.h> +#include <Swiften/Elements/Presence.h> + +namespace Swift { + class LuaElementConvertors; + + class PresenceConvertor : public StanzaConvertor<Presence> { + public: + PresenceConvertor(LuaElementConvertors* convertors); + virtual ~PresenceConvertor(); + + virtual boost::shared_ptr<Presence> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<Presence>) SWIFTEN_OVERRIDE; + + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + static std::string convertPresenceTypeToString(Presence::Type type); + static Presence::Type convertPresenceTypeFromString(const std::string& type); + + private: + LuaElementConvertors* convertors; + }; +} + diff --git a/Sluift/ElementConvertors/PubSubAffiliationConvertor.cpp b/Sluift/ElementConvertors/PubSubAffiliationConvertor.cpp new file mode 100644 index 0000000..de1b342 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubAffiliationConvertor.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubAffiliationConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> + + + + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubAffiliationConvertor::PubSubAffiliationConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubAffiliation>("pubsub_affiliation"), + convertors(convertors) { +} + +PubSubAffiliationConvertor::~PubSubAffiliationConvertor() { +} + +boost::shared_ptr<PubSubAffiliation> PubSubAffiliationConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubAffiliation> result = boost::make_shared<PubSubAffiliation>(); + lua_getfield(L, -1, "node"); + if (lua_isstring(L, -1)) { + result->setNode(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "type"); + if (lua_isstring(L, -1)) { + if (std::string(lua_tostring(L, -1)) == "none") { + result->setType(PubSubAffiliation::None); + } + if (std::string(lua_tostring(L, -1)) == "member") { + result->setType(PubSubAffiliation::Member); + } + if (std::string(lua_tostring(L, -1)) == "outcast") { + result->setType(PubSubAffiliation::Outcast); + } + if (std::string(lua_tostring(L, -1)) == "owner") { + result->setType(PubSubAffiliation::Owner); + } + if (std::string(lua_tostring(L, -1)) == "publisher") { + result->setType(PubSubAffiliation::Publisher); + } + if (std::string(lua_tostring(L, -1)) == "publish_only") { + result->setType(PubSubAffiliation::PublishOnly); + } + } + lua_pop(L, 1); + return result; +} + +void PubSubAffiliationConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubAffiliation> payload) { + lua_createtable(L, 0, 0); + lua_pushstring(L, payload->getNode().c_str()); + lua_setfield(L, -2, "node"); + switch (payload->getType()) { + case PubSubAffiliation::None: + lua_pushstring(L, "none"); + break; + case PubSubAffiliation::Member: + lua_pushstring(L, "member"); + break; + case PubSubAffiliation::Outcast: + lua_pushstring(L, "outcast"); + break; + case PubSubAffiliation::Owner: + lua_pushstring(L, "owner"); + break; + case PubSubAffiliation::Publisher: + lua_pushstring(L, "publisher"); + break; + case PubSubAffiliation::PublishOnly: + lua_pushstring(L, "publish_only"); + break; + } + lua_setfield(L, -2, "type"); +} + +boost::optional<LuaElementConvertor::Documentation> PubSubAffiliationConvertor::getDocumentation() const { + return Documentation( + "PubSubAffiliation", + "This table has the following fields:\n\n" + "- `node`: string\n" + "- `type`: `\"none\"`, `\"member\"`, `\"outcast\"`, `\"owner\"`, `\"publisher\"`, or `\"publish_only\"`\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubAffiliationConvertor.h b/Sluift/ElementConvertors/PubSubAffiliationConvertor.h new file mode 100644 index 0000000..f72a913 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubAffiliationConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubAffiliation.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubAffiliationConvertor : public GenericLuaElementConvertor<PubSubAffiliation> { + public: + PubSubAffiliationConvertor(LuaElementConvertors* convertors); + virtual ~PubSubAffiliationConvertor(); + + virtual boost::shared_ptr<PubSubAffiliation> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubAffiliation>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubAffiliationsConvertor.cpp b/Sluift/ElementConvertors/PubSubAffiliationsConvertor.cpp new file mode 100644 index 0000000..a71ffb8 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubAffiliationsConvertor.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubAffiliationsConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <boost/numeric/conversion/cast.hpp> + +#include <Sluift/LuaElementConvertors.h> +#include <Swiften/Base/foreach.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubAffiliationsConvertor::PubSubAffiliationsConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubAffiliations>("pubsub_affiliations"), + convertors(convertors) { +} + +PubSubAffiliationsConvertor::~PubSubAffiliationsConvertor() { +} + +boost::shared_ptr<PubSubAffiliations> PubSubAffiliationsConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubAffiliations> result = boost::make_shared<PubSubAffiliations>(); + lua_getfield(L, -1, "node"); + if (lua_isstring(L, -1)) { + result->setNode(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + if (lua_type(L, -1) == LUA_TTABLE) { + std::vector< boost::shared_ptr<PubSubAffiliation> > items; + for(size_t i = 0; i < lua_objlen(L, -1); ++i) { + lua_pushnumber(L, i + 1); + lua_gettable(L, -2); + if (!lua_isnil(L, -1)) { + if (boost::shared_ptr<PubSubAffiliation> payload = boost::dynamic_pointer_cast<PubSubAffiliation>(convertors->convertFromLuaUntyped(L, -1, "pubsub_affiliation"))) { + items.push_back(payload); + } + } + lua_pop(L, 1); + } + + result->setAffiliations(items); + } + return result; +} + +void PubSubAffiliationsConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubAffiliations> payload) { + lua_createtable(L, 0, 0); + if (payload->getNode()) { + lua_pushstring(L, (*payload->getNode()).c_str()); + lua_setfield(L, -2, "node"); + } + if (!payload->getAffiliations().empty()) { + { + int i = 0; + foreach(boost::shared_ptr<PubSubAffiliation> item, payload->getAffiliations()) { + if (convertors->convertToLuaUntyped(L, item) > 0) { + lua_rawseti(L, -2, boost::numeric_cast<int>(i+1)); + ++i; + } + } + } + } +} + +boost::optional<LuaElementConvertor::Documentation> PubSubAffiliationsConvertor::getDocumentation() const { + return Documentation( + "PubSubAffiliations", + "This table has the following fields:\n\n" + "- `node`: string (Optional)\n" + "- `affiliations`: array<@{PubSubAffiliation}>\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubAffiliationsConvertor.h b/Sluift/ElementConvertors/PubSubAffiliationsConvertor.h new file mode 100644 index 0000000..9c529bf --- /dev/null +++ b/Sluift/ElementConvertors/PubSubAffiliationsConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubAffiliations.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubAffiliationsConvertor : public GenericLuaElementConvertor<PubSubAffiliations> { + public: + PubSubAffiliationsConvertor(LuaElementConvertors* convertors); + virtual ~PubSubAffiliationsConvertor(); + + virtual boost::shared_ptr<PubSubAffiliations> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubAffiliations>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubConfigureConvertor.cpp b/Sluift/ElementConvertors/PubSubConfigureConvertor.cpp new file mode 100644 index 0000000..1f49ac4 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubConfigureConvertor.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubConfigureConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> + + +#include <Sluift/LuaElementConvertors.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubConfigureConvertor::PubSubConfigureConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubConfigure>("pubsub_configure"), + convertors(convertors) { +} + +PubSubConfigureConvertor::~PubSubConfigureConvertor() { +} + +boost::shared_ptr<PubSubConfigure> PubSubConfigureConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubConfigure> result = boost::make_shared<PubSubConfigure>(); + lua_getfield(L, -1, "data"); + if (!lua_isnil(L, -1)) { + if (boost::shared_ptr<Form> payload = boost::dynamic_pointer_cast<Form>(convertors->convertFromLuaUntyped(L, -1, "form"))) { + result->setData(payload); + } + } + lua_pop(L, 1); + return result; +} + +void PubSubConfigureConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubConfigure> payload) { + lua_createtable(L, 0, 0); + if (convertors->convertToLuaUntyped(L, payload->getData()) > 0) { + lua_setfield(L, -2, "data"); + } +} + +boost::optional<LuaElementConvertor::Documentation> PubSubConfigureConvertor::getDocumentation() const { + return Documentation( + "PubSubConfigure", + "This table has the following fields:\n\n" + "- `data`: @{Form}\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubConfigureConvertor.h b/Sluift/ElementConvertors/PubSubConfigureConvertor.h new file mode 100644 index 0000000..88e4b33 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubConfigureConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubConfigure.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubConfigureConvertor : public GenericLuaElementConvertor<PubSubConfigure> { + public: + PubSubConfigureConvertor(LuaElementConvertors* convertors); + virtual ~PubSubConfigureConvertor(); + + virtual boost::shared_ptr<PubSubConfigure> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubConfigure>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubCreateConvertor.cpp b/Sluift/ElementConvertors/PubSubCreateConvertor.cpp new file mode 100644 index 0000000..d78490e --- /dev/null +++ b/Sluift/ElementConvertors/PubSubCreateConvertor.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubCreateConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> + + +#include <Sluift/LuaElementConvertors.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubCreateConvertor::PubSubCreateConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubCreate>("pubsub_create"), + convertors(convertors) { +} + +PubSubCreateConvertor::~PubSubCreateConvertor() { +} + +boost::shared_ptr<PubSubCreate> PubSubCreateConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubCreate> result = boost::make_shared<PubSubCreate>(); + lua_getfield(L, -1, "node"); + if (lua_isstring(L, -1)) { + result->setNode(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "configure"); + if (!lua_isnil(L, -1)) { + if (boost::shared_ptr<PubSubConfigure> payload = boost::dynamic_pointer_cast<PubSubConfigure>(convertors->convertFromLuaUntyped(L, -1, "pubsub_configure"))) { + result->setConfigure(payload); + } + } + lua_pop(L, 1); + return result; +} + +void PubSubCreateConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubCreate> payload) { + lua_createtable(L, 0, 0); + lua_pushstring(L, payload->getNode().c_str()); + lua_setfield(L, -2, "node"); + if (convertors->convertToLuaUntyped(L, payload->getConfigure()) > 0) { + lua_setfield(L, -2, "configure"); + } +} + +boost::optional<LuaElementConvertor::Documentation> PubSubCreateConvertor::getDocumentation() const { + return Documentation( + "PubSubCreate", + "This table has the following fields:\n\n" + "- `node`: string\n" + "- `configure`: @{PubSubConfigure}\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubCreateConvertor.h b/Sluift/ElementConvertors/PubSubCreateConvertor.h new file mode 100644 index 0000000..87b48b2 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubCreateConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubCreate.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubCreateConvertor : public GenericLuaElementConvertor<PubSubCreate> { + public: + PubSubCreateConvertor(LuaElementConvertors* convertors); + virtual ~PubSubCreateConvertor(); + + virtual boost::shared_ptr<PubSubCreate> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubCreate>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubDefaultConvertor.cpp b/Sluift/ElementConvertors/PubSubDefaultConvertor.cpp new file mode 100644 index 0000000..3192ac9 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubDefaultConvertor.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubDefaultConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> + + + + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubDefaultConvertor::PubSubDefaultConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubDefault>("pubsub_default"), + convertors(convertors) { +} + +PubSubDefaultConvertor::~PubSubDefaultConvertor() { +} + +boost::shared_ptr<PubSubDefault> PubSubDefaultConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubDefault> result = boost::make_shared<PubSubDefault>(); + lua_getfield(L, -1, "node"); + if (lua_isstring(L, -1)) { + result->setNode(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "type"); + if (lua_isstring(L, -1)) { + if (std::string(lua_tostring(L, -1)) == "none") { + result->setType(PubSubDefault::None); + } + if (std::string(lua_tostring(L, -1)) == "collection") { + result->setType(PubSubDefault::Collection); + } + if (std::string(lua_tostring(L, -1)) == "leaf") { + result->setType(PubSubDefault::Leaf); + } + } + lua_pop(L, 1); + return result; +} + +void PubSubDefaultConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubDefault> payload) { + lua_createtable(L, 0, 0); + if (payload->getNode()) { + lua_pushstring(L, (*payload->getNode()).c_str()); + lua_setfield(L, -2, "node"); + } + switch (payload->getType()) { + case PubSubDefault::None: + lua_pushstring(L, "none"); + break; + case PubSubDefault::Collection: + lua_pushstring(L, "collection"); + break; + case PubSubDefault::Leaf: + lua_pushstring(L, "leaf"); + break; + } + lua_setfield(L, -2, "type"); +} + +boost::optional<LuaElementConvertor::Documentation> PubSubDefaultConvertor::getDocumentation() const { + return Documentation( + "PubSubDefault", + "This table has the following fields:\n\n" + "- `node`: string (Optional)\n" + "- `type`: `\"none\"`, `\"collection\"`, or `\"leaf\"`\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubDefaultConvertor.h b/Sluift/ElementConvertors/PubSubDefaultConvertor.h new file mode 100644 index 0000000..ba1c6e9 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubDefaultConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubDefault.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubDefaultConvertor : public GenericLuaElementConvertor<PubSubDefault> { + public: + PubSubDefaultConvertor(LuaElementConvertors* convertors); + virtual ~PubSubDefaultConvertor(); + + virtual boost::shared_ptr<PubSubDefault> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubDefault>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubEventAssociateConvertor.cpp b/Sluift/ElementConvertors/PubSubEventAssociateConvertor.cpp new file mode 100644 index 0000000..8a29033 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubEventAssociateConvertor.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubEventAssociateConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> + + + + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubEventAssociateConvertor::PubSubEventAssociateConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubEventAssociate>("pubsub_event_associate"), + convertors(convertors) { +} + +PubSubEventAssociateConvertor::~PubSubEventAssociateConvertor() { +} + +boost::shared_ptr<PubSubEventAssociate> PubSubEventAssociateConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubEventAssociate> result = boost::make_shared<PubSubEventAssociate>(); + lua_getfield(L, -1, "node"); + if (lua_isstring(L, -1)) { + result->setNode(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + return result; +} + +void PubSubEventAssociateConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubEventAssociate> payload) { + lua_createtable(L, 0, 0); + lua_pushstring(L, payload->getNode().c_str()); + lua_setfield(L, -2, "node"); +} + +boost::optional<LuaElementConvertor::Documentation> PubSubEventAssociateConvertor::getDocumentation() const { + return Documentation( + "PubSubEventAssociate", + "This table has the following fields:\n\n" + "- `node`: string\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubEventAssociateConvertor.h b/Sluift/ElementConvertors/PubSubEventAssociateConvertor.h new file mode 100644 index 0000000..6e440e7 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubEventAssociateConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubEventAssociate.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubEventAssociateConvertor : public GenericLuaElementConvertor<PubSubEventAssociate> { + public: + PubSubEventAssociateConvertor(LuaElementConvertors* convertors); + virtual ~PubSubEventAssociateConvertor(); + + virtual boost::shared_ptr<PubSubEventAssociate> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubEventAssociate>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubEventCollectionConvertor.cpp b/Sluift/ElementConvertors/PubSubEventCollectionConvertor.cpp new file mode 100644 index 0000000..6144f49 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubEventCollectionConvertor.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubEventCollectionConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> + + +#include <Sluift/LuaElementConvertors.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubEventCollectionConvertor::PubSubEventCollectionConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubEventCollection>("pubsub_event_collection"), + convertors(convertors) { +} + +PubSubEventCollectionConvertor::~PubSubEventCollectionConvertor() { +} + +boost::shared_ptr<PubSubEventCollection> PubSubEventCollectionConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubEventCollection> result = boost::make_shared<PubSubEventCollection>(); + lua_getfield(L, -1, "node"); + if (lua_isstring(L, -1)) { + result->setNode(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "disassociate"); + if (!lua_isnil(L, -1)) { + if (boost::shared_ptr<PubSubEventDisassociate> payload = boost::dynamic_pointer_cast<PubSubEventDisassociate>(convertors->convertFromLuaUntyped(L, -1, "pubsub_event_disassociate"))) { + result->setDisassociate(payload); + } + } + lua_pop(L, 1); + lua_getfield(L, -1, "associate"); + if (!lua_isnil(L, -1)) { + if (boost::shared_ptr<PubSubEventAssociate> payload = boost::dynamic_pointer_cast<PubSubEventAssociate>(convertors->convertFromLuaUntyped(L, -1, "pubsub_event_associate"))) { + result->setAssociate(payload); + } + } + lua_pop(L, 1); + return result; +} + +void PubSubEventCollectionConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubEventCollection> payload) { + lua_createtable(L, 0, 0); + if (payload->getNode()) { + lua_pushstring(L, (*payload->getNode()).c_str()); + lua_setfield(L, -2, "node"); + } + if (convertors->convertToLuaUntyped(L, payload->getDisassociate()) > 0) { + lua_setfield(L, -2, "disassociate"); + } + if (convertors->convertToLuaUntyped(L, payload->getAssociate()) > 0) { + lua_setfield(L, -2, "associate"); + } +} + +boost::optional<LuaElementConvertor::Documentation> PubSubEventCollectionConvertor::getDocumentation() const { + return Documentation( + "PubSubEventCollection", + "This table has the following fields:\n\n" + "- `node`: string (Optional)\n" + "- `disassociate`: @{PubSubEventDisassociate}\n" + "- `associate`: @{PubSubEventAssociate}\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubEventCollectionConvertor.h b/Sluift/ElementConvertors/PubSubEventCollectionConvertor.h new file mode 100644 index 0000000..c5bf942 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubEventCollectionConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubEventCollection.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubEventCollectionConvertor : public GenericLuaElementConvertor<PubSubEventCollection> { + public: + PubSubEventCollectionConvertor(LuaElementConvertors* convertors); + virtual ~PubSubEventCollectionConvertor(); + + virtual boost::shared_ptr<PubSubEventCollection> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubEventCollection>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubEventConfigurationConvertor.cpp b/Sluift/ElementConvertors/PubSubEventConfigurationConvertor.cpp new file mode 100644 index 0000000..8f2b01d --- /dev/null +++ b/Sluift/ElementConvertors/PubSubEventConfigurationConvertor.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubEventConfigurationConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> + + +#include <Sluift/LuaElementConvertors.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubEventConfigurationConvertor::PubSubEventConfigurationConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubEventConfiguration>("pubsub_event_configuration"), + convertors(convertors) { +} + +PubSubEventConfigurationConvertor::~PubSubEventConfigurationConvertor() { +} + +boost::shared_ptr<PubSubEventConfiguration> PubSubEventConfigurationConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubEventConfiguration> result = boost::make_shared<PubSubEventConfiguration>(); + lua_getfield(L, -1, "node"); + if (lua_isstring(L, -1)) { + result->setNode(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "data"); + if (!lua_isnil(L, -1)) { + if (boost::shared_ptr<Form> payload = boost::dynamic_pointer_cast<Form>(convertors->convertFromLuaUntyped(L, -1, "form"))) { + result->setData(payload); + } + } + lua_pop(L, 1); + return result; +} + +void PubSubEventConfigurationConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubEventConfiguration> payload) { + lua_createtable(L, 0, 0); + lua_pushstring(L, payload->getNode().c_str()); + lua_setfield(L, -2, "node"); + if (convertors->convertToLuaUntyped(L, payload->getData()) > 0) { + lua_setfield(L, -2, "data"); + } +} + +boost::optional<LuaElementConvertor::Documentation> PubSubEventConfigurationConvertor::getDocumentation() const { + return Documentation( + "PubSubEventConfiguration", + "This table has the following fields:\n\n" + "- `node`: string\n" + "- `data`: @{Form}\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubEventConfigurationConvertor.h b/Sluift/ElementConvertors/PubSubEventConfigurationConvertor.h new file mode 100644 index 0000000..1f5ea96 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubEventConfigurationConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubEventConfiguration.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubEventConfigurationConvertor : public GenericLuaElementConvertor<PubSubEventConfiguration> { + public: + PubSubEventConfigurationConvertor(LuaElementConvertors* convertors); + virtual ~PubSubEventConfigurationConvertor(); + + virtual boost::shared_ptr<PubSubEventConfiguration> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubEventConfiguration>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubEventConvertor.cpp b/Sluift/ElementConvertors/PubSubEventConvertor.cpp new file mode 100644 index 0000000..46d870a --- /dev/null +++ b/Sluift/ElementConvertors/PubSubEventConvertor.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/ElementConvertors/PubSubEventConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> + + +#include <Sluift/LuaElementConvertors.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubEventConvertor::PubSubEventConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubEvent>("pubsub_event"), + convertors(convertors) { +} + +PubSubEventConvertor::~PubSubEventConvertor() { +} + +boost::shared_ptr<PubSubEvent> PubSubEventConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubEvent> result = boost::make_shared<PubSubEvent>(); + if (boost::shared_ptr<PubSubEventPayload> payload = boost::dynamic_pointer_cast<PubSubEventPayload>(convertors->convertFromLua(L, -1))) { + result->setPayload(payload); + } + return result; +} + +void PubSubEventConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubEvent> event) { + convertors->convertToLua(L, event->getPayload()); +} diff --git a/Sluift/ElementConvertors/PubSubEventConvertor.h b/Sluift/ElementConvertors/PubSubEventConvertor.h new file mode 100644 index 0000000..8250fc4 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubEventConvertor.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/PubSubEvent.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubEventConvertor : public GenericLuaElementConvertor<PubSubEvent> { + public: + PubSubEventConvertor(LuaElementConvertors* convertors); + virtual ~PubSubEventConvertor(); + + virtual boost::shared_ptr<PubSubEvent> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubEvent>) SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubEventDeleteConvertor.cpp b/Sluift/ElementConvertors/PubSubEventDeleteConvertor.cpp new file mode 100644 index 0000000..e011f0c --- /dev/null +++ b/Sluift/ElementConvertors/PubSubEventDeleteConvertor.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubEventDeleteConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> + + +#include <Sluift/LuaElementConvertors.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubEventDeleteConvertor::PubSubEventDeleteConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubEventDelete>("pubsub_event_delete"), + convertors(convertors) { +} + +PubSubEventDeleteConvertor::~PubSubEventDeleteConvertor() { +} + +boost::shared_ptr<PubSubEventDelete> PubSubEventDeleteConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubEventDelete> result = boost::make_shared<PubSubEventDelete>(); + lua_getfield(L, -1, "node"); + if (lua_isstring(L, -1)) { + result->setNode(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "redirects"); + if (!lua_isnil(L, -1)) { + if (boost::shared_ptr<PubSubEventRedirect> payload = boost::dynamic_pointer_cast<PubSubEventRedirect>(convertors->convertFromLuaUntyped(L, -1, "pubsub_event_redirect"))) { + result->setRedirects(payload); + } + } + lua_pop(L, 1); + return result; +} + +void PubSubEventDeleteConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubEventDelete> payload) { + lua_createtable(L, 0, 0); + lua_pushstring(L, payload->getNode().c_str()); + lua_setfield(L, -2, "node"); + if (convertors->convertToLuaUntyped(L, payload->getRedirects()) > 0) { + lua_setfield(L, -2, "redirects"); + } +} + +boost::optional<LuaElementConvertor::Documentation> PubSubEventDeleteConvertor::getDocumentation() const { + return Documentation( + "PubSubEventDelete", + "This table has the following fields:\n\n" + "- `node`: string\n" + "- `redirects`: @{PubSubEventRedirect}\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubEventDeleteConvertor.h b/Sluift/ElementConvertors/PubSubEventDeleteConvertor.h new file mode 100644 index 0000000..bff4d30 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubEventDeleteConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubEventDelete.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubEventDeleteConvertor : public GenericLuaElementConvertor<PubSubEventDelete> { + public: + PubSubEventDeleteConvertor(LuaElementConvertors* convertors); + virtual ~PubSubEventDeleteConvertor(); + + virtual boost::shared_ptr<PubSubEventDelete> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubEventDelete>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubEventDisassociateConvertor.cpp b/Sluift/ElementConvertors/PubSubEventDisassociateConvertor.cpp new file mode 100644 index 0000000..ef71a91 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubEventDisassociateConvertor.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubEventDisassociateConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> + + + + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubEventDisassociateConvertor::PubSubEventDisassociateConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubEventDisassociate>("pubsub_event_disassociate"), + convertors(convertors) { +} + +PubSubEventDisassociateConvertor::~PubSubEventDisassociateConvertor() { +} + +boost::shared_ptr<PubSubEventDisassociate> PubSubEventDisassociateConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubEventDisassociate> result = boost::make_shared<PubSubEventDisassociate>(); + lua_getfield(L, -1, "node"); + if (lua_isstring(L, -1)) { + result->setNode(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + return result; +} + +void PubSubEventDisassociateConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubEventDisassociate> payload) { + lua_createtable(L, 0, 0); + lua_pushstring(L, payload->getNode().c_str()); + lua_setfield(L, -2, "node"); +} + +boost::optional<LuaElementConvertor::Documentation> PubSubEventDisassociateConvertor::getDocumentation() const { + return Documentation( + "PubSubEventDisassociate", + "This table has the following fields:\n\n" + "- `node`: string\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubEventDisassociateConvertor.h b/Sluift/ElementConvertors/PubSubEventDisassociateConvertor.h new file mode 100644 index 0000000..0087a4e --- /dev/null +++ b/Sluift/ElementConvertors/PubSubEventDisassociateConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubEventDisassociate.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubEventDisassociateConvertor : public GenericLuaElementConvertor<PubSubEventDisassociate> { + public: + PubSubEventDisassociateConvertor(LuaElementConvertors* convertors); + virtual ~PubSubEventDisassociateConvertor(); + + virtual boost::shared_ptr<PubSubEventDisassociate> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubEventDisassociate>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubEventItemConvertor.cpp b/Sluift/ElementConvertors/PubSubEventItemConvertor.cpp new file mode 100644 index 0000000..9905df3 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubEventItemConvertor.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubEventItemConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <boost/numeric/conversion/cast.hpp> + +#include <Sluift/LuaElementConvertors.h> +#include <Swiften/Base/foreach.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubEventItemConvertor::PubSubEventItemConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubEventItem>("pubsub_event_item"), + convertors(convertors) { +} + +PubSubEventItemConvertor::~PubSubEventItemConvertor() { +} + +boost::shared_ptr<PubSubEventItem> PubSubEventItemConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubEventItem> result = boost::make_shared<PubSubEventItem>(); + lua_getfield(L, -1, "node"); + if (lua_isstring(L, -1)) { + result->setNode(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "publisher"); + if (lua_isstring(L, -1)) { + result->setPublisher(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "data"); + if (lua_type(L, -1) == LUA_TTABLE) { + std::vector< boost::shared_ptr<Payload> > items; + for(size_t i = 0; i < lua_objlen(L, -1); ++i) { + lua_pushnumber(L, i + 1); + lua_gettable(L, -2); + if (!lua_isnil(L, -1)) { + if (boost::shared_ptr<Payload> payload = boost::dynamic_pointer_cast<Payload>(convertors->convertFromLua(L, -1))) { + items.push_back(payload); + } + } + lua_pop(L, 1); + } + + result->setData(items); + } + lua_pop(L, 1); + lua_getfield(L, -1, "id"); + if (lua_isstring(L, -1)) { + result->setID(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + return result; +} + +void PubSubEventItemConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubEventItem> payload) { + lua_createtable(L, 0, 0); + if (payload->getNode()) { + lua_pushstring(L, (*payload->getNode()).c_str()); + lua_setfield(L, -2, "node"); + } + if (payload->getPublisher()) { + lua_pushstring(L, (*payload->getPublisher()).c_str()); + lua_setfield(L, -2, "publisher"); + } + if (!payload->getData().empty()) { + lua_createtable(L, boost::numeric_cast<int>(payload->getData().size()), 0); + { + int i = 0; + foreach(boost::shared_ptr<Payload> item, payload->getData()) { + if (convertors->convertToLua(L, item) > 0) { + lua_rawseti(L, -2, boost::numeric_cast<int>(i+1)); + ++i; + } + } + } + lua_setfield(L, -2, "data"); + } + if (payload->getID()) { + lua_pushstring(L, (*payload->getID()).c_str()); + lua_setfield(L, -2, "id"); + } +} + +boost::optional<LuaElementConvertor::Documentation> PubSubEventItemConvertor::getDocumentation() const { + return Documentation( + "PubSubEventItem", + "This table has the following fields:\n\n" + "- `node`: string (Optional)\n" + "- `publisher`: string (Optional)\n" + "- `data`: array<element (table)>\n" + "- `id`: string (Optional)\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubEventItemConvertor.h b/Sluift/ElementConvertors/PubSubEventItemConvertor.h new file mode 100644 index 0000000..6bc93eb --- /dev/null +++ b/Sluift/ElementConvertors/PubSubEventItemConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubEventItem.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubEventItemConvertor : public GenericLuaElementConvertor<PubSubEventItem> { + public: + PubSubEventItemConvertor(LuaElementConvertors* convertors); + virtual ~PubSubEventItemConvertor(); + + virtual boost::shared_ptr<PubSubEventItem> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubEventItem>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubEventItemsConvertor.cpp b/Sluift/ElementConvertors/PubSubEventItemsConvertor.cpp new file mode 100644 index 0000000..4e82f2e --- /dev/null +++ b/Sluift/ElementConvertors/PubSubEventItemsConvertor.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubEventItemsConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <boost/numeric/conversion/cast.hpp> + +#include <Sluift/LuaElementConvertors.h> +#include <Swiften/Base/foreach.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubEventItemsConvertor::PubSubEventItemsConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubEventItems>("pubsub_event_items"), + convertors(convertors) { +} + +PubSubEventItemsConvertor::~PubSubEventItemsConvertor() { +} + +boost::shared_ptr<PubSubEventItems> PubSubEventItemsConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubEventItems> result = boost::make_shared<PubSubEventItems>(); + lua_getfield(L, -1, "node"); + if (lua_isstring(L, -1)) { + result->setNode(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "items"); + if (lua_type(L, -1) == LUA_TTABLE) { + std::vector< boost::shared_ptr<PubSubEventItem> > items; + for(size_t i = 0; i < lua_objlen(L, -1); ++i) { + lua_pushnumber(L, i + 1); + lua_gettable(L, -2); + if (!lua_isnil(L, -1)) { + if (boost::shared_ptr<PubSubEventItem> payload = boost::dynamic_pointer_cast<PubSubEventItem>(convertors->convertFromLuaUntyped(L, -1, "pubsub_event_item"))) { + items.push_back(payload); + } + } + lua_pop(L, 1); + } + + result->setItems(items); + } + lua_pop(L, 1); + lua_getfield(L, -1, "retracts"); + if (lua_type(L, -1) == LUA_TTABLE) { + std::vector< boost::shared_ptr<PubSubEventRetract> > items; + for(size_t i = 0; i < lua_objlen(L, -1); ++i) { + lua_pushnumber(L, i + 1); + lua_gettable(L, -2); + if (!lua_isnil(L, -1)) { + if (boost::shared_ptr<PubSubEventRetract> payload = boost::dynamic_pointer_cast<PubSubEventRetract>(convertors->convertFromLuaUntyped(L, -1, "pubsub_event_retract"))) { + items.push_back(payload); + } + } + lua_pop(L, 1); + } + + result->setRetracts(items); + } + lua_pop(L, 1); + return result; +} + +void PubSubEventItemsConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubEventItems> payload) { + lua_createtable(L, 0, 0); + lua_pushstring(L, payload->getNode().c_str()); + lua_setfield(L, -2, "node"); + if (!payload->getItems().empty()) { + lua_createtable(L, boost::numeric_cast<int>(payload->getItems().size()), 0); + { + int i = 0; + foreach(boost::shared_ptr<PubSubEventItem> item, payload->getItems()) { + if (convertors->convertToLuaUntyped(L, item) > 0) { + lua_rawseti(L, -2, boost::numeric_cast<int>(i+1)); + ++i; + } + } + } + lua_setfield(L, -2, "items"); + } + if (!payload->getRetracts().empty()) { + lua_createtable(L, boost::numeric_cast<int>(payload->getRetracts().size()), 0); + { + int i = 0; + foreach(boost::shared_ptr<PubSubEventRetract> item, payload->getRetracts()) { + if (convertors->convertToLuaUntyped(L, item) > 0) { + lua_rawseti(L, -2, boost::numeric_cast<int>(i+1)); + ++i; + } + } + } + lua_setfield(L, -2, "retracts"); + } +} + +boost::optional<LuaElementConvertor::Documentation> PubSubEventItemsConvertor::getDocumentation() const { + return Documentation( + "PubSubEventItems", + "This table has the following fields:\n\n" + "- `node`: string\n" + "- `items`: array<@{PubSubEventItem}>\n" + "- `retracts`: array<@{PubSubEventRetract}>\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubEventItemsConvertor.h b/Sluift/ElementConvertors/PubSubEventItemsConvertor.h new file mode 100644 index 0000000..096643c --- /dev/null +++ b/Sluift/ElementConvertors/PubSubEventItemsConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubEventItems.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubEventItemsConvertor : public GenericLuaElementConvertor<PubSubEventItems> { + public: + PubSubEventItemsConvertor(LuaElementConvertors* convertors); + virtual ~PubSubEventItemsConvertor(); + + virtual boost::shared_ptr<PubSubEventItems> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubEventItems>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubEventPurgeConvertor.cpp b/Sluift/ElementConvertors/PubSubEventPurgeConvertor.cpp new file mode 100644 index 0000000..2728c0d --- /dev/null +++ b/Sluift/ElementConvertors/PubSubEventPurgeConvertor.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubEventPurgeConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> + + + + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubEventPurgeConvertor::PubSubEventPurgeConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubEventPurge>("pubsub_event_purge"), + convertors(convertors) { +} + +PubSubEventPurgeConvertor::~PubSubEventPurgeConvertor() { +} + +boost::shared_ptr<PubSubEventPurge> PubSubEventPurgeConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubEventPurge> result = boost::make_shared<PubSubEventPurge>(); + lua_getfield(L, -1, "node"); + if (lua_isstring(L, -1)) { + result->setNode(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + return result; +} + +void PubSubEventPurgeConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubEventPurge> payload) { + lua_createtable(L, 0, 0); + lua_pushstring(L, payload->getNode().c_str()); + lua_setfield(L, -2, "node"); +} + +boost::optional<LuaElementConvertor::Documentation> PubSubEventPurgeConvertor::getDocumentation() const { + return Documentation( + "PubSubEventPurge", + "This table has the following fields:\n\n" + "- `node`: string\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubEventPurgeConvertor.h b/Sluift/ElementConvertors/PubSubEventPurgeConvertor.h new file mode 100644 index 0000000..a264f5e --- /dev/null +++ b/Sluift/ElementConvertors/PubSubEventPurgeConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubEventPurge.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubEventPurgeConvertor : public GenericLuaElementConvertor<PubSubEventPurge> { + public: + PubSubEventPurgeConvertor(LuaElementConvertors* convertors); + virtual ~PubSubEventPurgeConvertor(); + + virtual boost::shared_ptr<PubSubEventPurge> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubEventPurge>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubEventRedirectConvertor.cpp b/Sluift/ElementConvertors/PubSubEventRedirectConvertor.cpp new file mode 100644 index 0000000..e9ebe02 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubEventRedirectConvertor.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubEventRedirectConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> + + + + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubEventRedirectConvertor::PubSubEventRedirectConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubEventRedirect>("pubsub_event_redirect"), + convertors(convertors) { +} + +PubSubEventRedirectConvertor::~PubSubEventRedirectConvertor() { +} + +boost::shared_ptr<PubSubEventRedirect> PubSubEventRedirectConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubEventRedirect> result = boost::make_shared<PubSubEventRedirect>(); + lua_getfield(L, -1, "uri"); + if (lua_isstring(L, -1)) { + result->setURI(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + return result; +} + +void PubSubEventRedirectConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubEventRedirect> payload) { + lua_createtable(L, 0, 0); + lua_pushstring(L, payload->getURI().c_str()); + lua_setfield(L, -2, "uri"); +} + +boost::optional<LuaElementConvertor::Documentation> PubSubEventRedirectConvertor::getDocumentation() const { + return Documentation( + "PubSubEventRedirect", + "This table has the following fields:\n\n" + "- `uri`: string\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubEventRedirectConvertor.h b/Sluift/ElementConvertors/PubSubEventRedirectConvertor.h new file mode 100644 index 0000000..c319573 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubEventRedirectConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubEventRedirect.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubEventRedirectConvertor : public GenericLuaElementConvertor<PubSubEventRedirect> { + public: + PubSubEventRedirectConvertor(LuaElementConvertors* convertors); + virtual ~PubSubEventRedirectConvertor(); + + virtual boost::shared_ptr<PubSubEventRedirect> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubEventRedirect>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubEventRetractConvertor.cpp b/Sluift/ElementConvertors/PubSubEventRetractConvertor.cpp new file mode 100644 index 0000000..5647f7d --- /dev/null +++ b/Sluift/ElementConvertors/PubSubEventRetractConvertor.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubEventRetractConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> + + + + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubEventRetractConvertor::PubSubEventRetractConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubEventRetract>("pubsub_event_retract"), + convertors(convertors) { +} + +PubSubEventRetractConvertor::~PubSubEventRetractConvertor() { +} + +boost::shared_ptr<PubSubEventRetract> PubSubEventRetractConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubEventRetract> result = boost::make_shared<PubSubEventRetract>(); + lua_getfield(L, -1, "id"); + if (lua_isstring(L, -1)) { + result->setID(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + return result; +} + +void PubSubEventRetractConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubEventRetract> payload) { + lua_createtable(L, 0, 0); + lua_pushstring(L, payload->getID().c_str()); + lua_setfield(L, -2, "id"); +} + +boost::optional<LuaElementConvertor::Documentation> PubSubEventRetractConvertor::getDocumentation() const { + return Documentation( + "PubSubEventRetract", + "This table has the following fields:\n\n" + "- `id`: string\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubEventRetractConvertor.h b/Sluift/ElementConvertors/PubSubEventRetractConvertor.h new file mode 100644 index 0000000..500ee7e --- /dev/null +++ b/Sluift/ElementConvertors/PubSubEventRetractConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubEventRetract.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubEventRetractConvertor : public GenericLuaElementConvertor<PubSubEventRetract> { + public: + PubSubEventRetractConvertor(LuaElementConvertors* convertors); + virtual ~PubSubEventRetractConvertor(); + + virtual boost::shared_ptr<PubSubEventRetract> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubEventRetract>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubEventSubscriptionConvertor.cpp b/Sluift/ElementConvertors/PubSubEventSubscriptionConvertor.cpp new file mode 100644 index 0000000..62d2834 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubEventSubscriptionConvertor.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubEventSubscriptionConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> + + +#include <Swiften/Base/DateTime.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubEventSubscriptionConvertor::PubSubEventSubscriptionConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubEventSubscription>("pubsub_event_subscription"), + convertors(convertors) { +} + +PubSubEventSubscriptionConvertor::~PubSubEventSubscriptionConvertor() { +} + +boost::shared_ptr<PubSubEventSubscription> PubSubEventSubscriptionConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubEventSubscription> result = boost::make_shared<PubSubEventSubscription>(); + lua_getfield(L, -1, "node"); + if (lua_isstring(L, -1)) { + result->setNode(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "jid"); + if (lua_isstring(L, -1)) { + result->setJID(JID(std::string(lua_tostring(L, -1)))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "subscription"); + if (lua_isstring(L, -1)) { + if (std::string(lua_tostring(L, -1)) == "none") { + result->setSubscription(PubSubEventSubscription::None); + } + if (std::string(lua_tostring(L, -1)) == "pending") { + result->setSubscription(PubSubEventSubscription::Pending); + } + if (std::string(lua_tostring(L, -1)) == "subscribed") { + result->setSubscription(PubSubEventSubscription::Subscribed); + } + if (std::string(lua_tostring(L, -1)) == "unconfigured") { + result->setSubscription(PubSubEventSubscription::Unconfigured); + } + } + lua_pop(L, 1); + lua_getfield(L, -1, "subscription_id"); + if (lua_isstring(L, -1)) { + result->setSubscriptionID(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "expiry"); + if (lua_isstring(L, -1)) { + result->setExpiry(stringToDateTime(std::string(lua_tostring(L, -1)))); + } + lua_pop(L, 1); + return result; +} + +void PubSubEventSubscriptionConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubEventSubscription> payload) { + lua_createtable(L, 0, 0); + lua_pushstring(L, payload->getNode().c_str()); + lua_setfield(L, -2, "node"); + lua_pushstring(L, payload->getJID().toString().c_str()); + lua_setfield(L, -2, "jid"); + switch (payload->getSubscription()) { + case PubSubEventSubscription::None: + lua_pushstring(L, "none"); + break; + case PubSubEventSubscription::Pending: + lua_pushstring(L, "pending"); + break; + case PubSubEventSubscription::Subscribed: + lua_pushstring(L, "subscribed"); + break; + case PubSubEventSubscription::Unconfigured: + lua_pushstring(L, "unconfigured"); + break; + } + lua_setfield(L, -2, "subscription"); + if (payload->getSubscriptionID()) { + lua_pushstring(L, (*payload->getSubscriptionID()).c_str()); + lua_setfield(L, -2, "subscription_id"); + } + lua_pushstring(L, dateTimeToString(payload->getExpiry()).c_str()); + lua_setfield(L, -2, "expiry"); +} + +boost::optional<LuaElementConvertor::Documentation> PubSubEventSubscriptionConvertor::getDocumentation() const { + return Documentation( + "PubSubEventSubscription", + "This table has the following fields:\n\n" + "- `node`: string\n" + "- `jid`: jid (string)\n" + "- `subscription`: `\"none\"`, `\"pending\"`, `\"subscribed\"`, or `\"unconfigured\"`\n" + "- `subscription_id`: string (Optional)\n" + "- `expiry`: datetime (string)\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubEventSubscriptionConvertor.h b/Sluift/ElementConvertors/PubSubEventSubscriptionConvertor.h new file mode 100644 index 0000000..9291a81 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubEventSubscriptionConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubEventSubscription.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubEventSubscriptionConvertor : public GenericLuaElementConvertor<PubSubEventSubscription> { + public: + PubSubEventSubscriptionConvertor(LuaElementConvertors* convertors); + virtual ~PubSubEventSubscriptionConvertor(); + + virtual boost::shared_ptr<PubSubEventSubscription> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubEventSubscription>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubItemConvertor.cpp b/Sluift/ElementConvertors/PubSubItemConvertor.cpp new file mode 100644 index 0000000..dcaa600 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubItemConvertor.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubItemConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <boost/numeric/conversion/cast.hpp> + +#include <Sluift/LuaElementConvertors.h> +#include <Swiften/Base/foreach.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubItemConvertor::PubSubItemConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubItem>("pubsub_item"), + convertors(convertors) { +} + +PubSubItemConvertor::~PubSubItemConvertor() { +} + +boost::shared_ptr<PubSubItem> PubSubItemConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubItem> result = boost::make_shared<PubSubItem>(); + lua_getfield(L, -1, "data"); + if (lua_type(L, -1) == LUA_TTABLE) { + std::vector< boost::shared_ptr<Payload> > items; + for(size_t i = 0; i < lua_objlen(L, -1); ++i) { + lua_pushnumber(L, i + 1); + lua_gettable(L, -2); + if (!lua_isnil(L, -1)) { + if (boost::shared_ptr<Payload> payload = boost::dynamic_pointer_cast<Payload>(convertors->convertFromLua(L, -1))) { + items.push_back(payload); + } + } + lua_pop(L, 1); + } + + result->setData(items); + } + lua_pop(L, 1); + lua_getfield(L, -1, "id"); + if (lua_isstring(L, -1)) { + result->setID(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + return result; +} + +void PubSubItemConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubItem> payload) { + lua_createtable(L, 0, 0); + if (!payload->getData().empty()) { + lua_createtable(L, boost::numeric_cast<int>(payload->getData().size()), 0); + { + int i = 0; + foreach(boost::shared_ptr<Payload> item, payload->getData()) { + if (convertors->convertToLua(L, item) > 0) { + lua_rawseti(L, -2, boost::numeric_cast<int>(i+1)); + ++i; + } + } + } + lua_setfield(L, -2, "data"); + } + lua_pushstring(L, payload->getID().c_str()); + lua_setfield(L, -2, "id"); +} + +boost::optional<LuaElementConvertor::Documentation> PubSubItemConvertor::getDocumentation() const { + return Documentation( + "PubSubItem", + "This table has the following fields:\n\n" + "- `data`: array<element (table)>\n" + "- `id`: string\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubItemConvertor.h b/Sluift/ElementConvertors/PubSubItemConvertor.h new file mode 100644 index 0000000..dd5f485 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubItemConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubItem.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubItemConvertor : public GenericLuaElementConvertor<PubSubItem> { + public: + PubSubItemConvertor(LuaElementConvertors* convertors); + virtual ~PubSubItemConvertor(); + + virtual boost::shared_ptr<PubSubItem> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubItem>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubItemsConvertor.cpp b/Sluift/ElementConvertors/PubSubItemsConvertor.cpp new file mode 100644 index 0000000..91a080f --- /dev/null +++ b/Sluift/ElementConvertors/PubSubItemsConvertor.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubItemsConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <boost/numeric/conversion/cast.hpp> + +#include <Sluift/LuaElementConvertors.h> +#include <Swiften/Base/foreach.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubItemsConvertor::PubSubItemsConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubItems>("pubsub_items"), + convertors(convertors) { +} + +PubSubItemsConvertor::~PubSubItemsConvertor() { +} + +boost::shared_ptr<PubSubItems> PubSubItemsConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubItems> result = boost::make_shared<PubSubItems>(); + lua_getfield(L, -1, "node"); + if (lua_isstring(L, -1)) { + result->setNode(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + if (lua_type(L, -1) == LUA_TTABLE) { + std::vector< boost::shared_ptr<PubSubItem> > items; + for(size_t i = 0; i < lua_objlen(L, -1); ++i) { + lua_pushnumber(L, i + 1); + lua_gettable(L, -2); + if (!lua_isnil(L, -1)) { + if (boost::shared_ptr<PubSubItem> payload = boost::dynamic_pointer_cast<PubSubItem>(convertors->convertFromLuaUntyped(L, -1, "pubsub_item"))) { + items.push_back(payload); + } + } + lua_pop(L, 1); + } + + result->setItems(items); + } + lua_getfield(L, -1, "maximum_items"); + if (lua_isnumber(L, -1)) { + result->setMaximumItems(boost::numeric_cast<unsigned int>(lua_tonumber(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "subscription_id"); + if (lua_isstring(L, -1)) { + result->setSubscriptionID(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + return result; +} + +void PubSubItemsConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubItems> payload) { + lua_createtable(L, 0, 0); + lua_pushstring(L, payload->getNode().c_str()); + lua_setfield(L, -2, "node"); + if (!payload->getItems().empty()) { + { + int i = 0; + foreach(boost::shared_ptr<PubSubItem> item, payload->getItems()) { + if (convertors->convertToLuaUntyped(L, item) > 0) { + lua_rawseti(L, -2, boost::numeric_cast<int>(i+1)); + ++i; + } + } + } + } + if (payload->getMaximumItems()) { + lua_pushnumber(L, (*payload->getMaximumItems())); + lua_setfield(L, -2, "maximum_items"); + } + if (payload->getSubscriptionID()) { + lua_pushstring(L, (*payload->getSubscriptionID()).c_str()); + lua_setfield(L, -2, "subscription_id"); + } +} + +boost::optional<LuaElementConvertor::Documentation> PubSubItemsConvertor::getDocumentation() const { + return Documentation( + "PubSubItems", + "This table has the following fields:\n\n" + "- `node`: string\n" + "- `items`: array<@{PubSubItem}>\n" + "- `maximum_items`: number (Optional)\n" + "- `subscription_id`: string (Optional)\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubItemsConvertor.h b/Sluift/ElementConvertors/PubSubItemsConvertor.h new file mode 100644 index 0000000..88a4287 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubItemsConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubItems.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubItemsConvertor : public GenericLuaElementConvertor<PubSubItems> { + public: + PubSubItemsConvertor(LuaElementConvertors* convertors); + virtual ~PubSubItemsConvertor(); + + virtual boost::shared_ptr<PubSubItems> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubItems>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubOptionsConvertor.cpp b/Sluift/ElementConvertors/PubSubOptionsConvertor.cpp new file mode 100644 index 0000000..aa3cdd5 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubOptionsConvertor.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubOptionsConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> + + +#include <Sluift/LuaElementConvertors.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubOptionsConvertor::PubSubOptionsConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubOptions>("pubsub_options"), + convertors(convertors) { +} + +PubSubOptionsConvertor::~PubSubOptionsConvertor() { +} + +boost::shared_ptr<PubSubOptions> PubSubOptionsConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubOptions> result = boost::make_shared<PubSubOptions>(); + lua_getfield(L, -1, "node"); + if (lua_isstring(L, -1)) { + result->setNode(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "jid"); + if (lua_isstring(L, -1)) { + result->setJID(JID(std::string(lua_tostring(L, -1)))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "data"); + if (!lua_isnil(L, -1)) { + if (boost::shared_ptr<Form> payload = boost::dynamic_pointer_cast<Form>(convertors->convertFromLuaUntyped(L, -1, "form"))) { + result->setData(payload); + } + } + lua_pop(L, 1); + lua_getfield(L, -1, "subscription_id"); + if (lua_isstring(L, -1)) { + result->setSubscriptionID(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + return result; +} + +void PubSubOptionsConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubOptions> payload) { + lua_createtable(L, 0, 0); + lua_pushstring(L, payload->getNode().c_str()); + lua_setfield(L, -2, "node"); + lua_pushstring(L, payload->getJID().toString().c_str()); + lua_setfield(L, -2, "jid"); + if (convertors->convertToLuaUntyped(L, payload->getData()) > 0) { + lua_setfield(L, -2, "data"); + } + if (payload->getSubscriptionID()) { + lua_pushstring(L, (*payload->getSubscriptionID()).c_str()); + lua_setfield(L, -2, "subscription_id"); + } +} + +boost::optional<LuaElementConvertor::Documentation> PubSubOptionsConvertor::getDocumentation() const { + return Documentation( + "PubSubOptions", + "This table has the following fields:\n\n" + "- `node`: string\n" + "- `jid`: jid (string)\n" + "- `data`: @{Form}\n" + "- `subscription_id`: string (Optional)\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubOptionsConvertor.h b/Sluift/ElementConvertors/PubSubOptionsConvertor.h new file mode 100644 index 0000000..ef67f6d --- /dev/null +++ b/Sluift/ElementConvertors/PubSubOptionsConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubOptions.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubOptionsConvertor : public GenericLuaElementConvertor<PubSubOptions> { + public: + PubSubOptionsConvertor(LuaElementConvertors* convertors); + virtual ~PubSubOptionsConvertor(); + + virtual boost::shared_ptr<PubSubOptions> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubOptions>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubOwnerAffiliationConvertor.cpp b/Sluift/ElementConvertors/PubSubOwnerAffiliationConvertor.cpp new file mode 100644 index 0000000..7d16209 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubOwnerAffiliationConvertor.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubOwnerAffiliationConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> + + + + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubOwnerAffiliationConvertor::PubSubOwnerAffiliationConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubOwnerAffiliation>("pubsub_owner_affiliation"), + convertors(convertors) { +} + +PubSubOwnerAffiliationConvertor::~PubSubOwnerAffiliationConvertor() { +} + +boost::shared_ptr<PubSubOwnerAffiliation> PubSubOwnerAffiliationConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubOwnerAffiliation> result = boost::make_shared<PubSubOwnerAffiliation>(); + lua_getfield(L, -1, "jid"); + if (lua_isstring(L, -1)) { + result->setJID(JID(std::string(lua_tostring(L, -1)))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "type"); + if (lua_isstring(L, -1)) { + if (std::string(lua_tostring(L, -1)) == "none") { + result->setType(PubSubOwnerAffiliation::None); + } + if (std::string(lua_tostring(L, -1)) == "member") { + result->setType(PubSubOwnerAffiliation::Member); + } + if (std::string(lua_tostring(L, -1)) == "outcast") { + result->setType(PubSubOwnerAffiliation::Outcast); + } + if (std::string(lua_tostring(L, -1)) == "owner") { + result->setType(PubSubOwnerAffiliation::Owner); + } + if (std::string(lua_tostring(L, -1)) == "publisher") { + result->setType(PubSubOwnerAffiliation::Publisher); + } + if (std::string(lua_tostring(L, -1)) == "publish_only") { + result->setType(PubSubOwnerAffiliation::PublishOnly); + } + } + lua_pop(L, 1); + return result; +} + +void PubSubOwnerAffiliationConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubOwnerAffiliation> payload) { + lua_createtable(L, 0, 0); + lua_pushstring(L, payload->getJID().toString().c_str()); + lua_setfield(L, -2, "jid"); + switch (payload->getType()) { + case PubSubOwnerAffiliation::None: + lua_pushstring(L, "none"); + break; + case PubSubOwnerAffiliation::Member: + lua_pushstring(L, "member"); + break; + case PubSubOwnerAffiliation::Outcast: + lua_pushstring(L, "outcast"); + break; + case PubSubOwnerAffiliation::Owner: + lua_pushstring(L, "owner"); + break; + case PubSubOwnerAffiliation::Publisher: + lua_pushstring(L, "publisher"); + break; + case PubSubOwnerAffiliation::PublishOnly: + lua_pushstring(L, "publish_only"); + break; + } + lua_setfield(L, -2, "type"); +} + +boost::optional<LuaElementConvertor::Documentation> PubSubOwnerAffiliationConvertor::getDocumentation() const { + return Documentation( + "PubSubOwnerAffiliation", + "This table has the following fields:\n\n" + "- `jid`: jid (string)\n" + "- `type`: `\"none\"`, `\"member\"`, `\"outcast\"`, `\"owner\"`, `\"publisher\"`, or `\"publish_only\"`\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubOwnerAffiliationConvertor.h b/Sluift/ElementConvertors/PubSubOwnerAffiliationConvertor.h new file mode 100644 index 0000000..035b393 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubOwnerAffiliationConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubOwnerAffiliation.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubOwnerAffiliationConvertor : public GenericLuaElementConvertor<PubSubOwnerAffiliation> { + public: + PubSubOwnerAffiliationConvertor(LuaElementConvertors* convertors); + virtual ~PubSubOwnerAffiliationConvertor(); + + virtual boost::shared_ptr<PubSubOwnerAffiliation> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubOwnerAffiliation>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubOwnerAffiliationsConvertor.cpp b/Sluift/ElementConvertors/PubSubOwnerAffiliationsConvertor.cpp new file mode 100644 index 0000000..3daaefa --- /dev/null +++ b/Sluift/ElementConvertors/PubSubOwnerAffiliationsConvertor.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubOwnerAffiliationsConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <boost/numeric/conversion/cast.hpp> + +#include <Sluift/LuaElementConvertors.h> +#include <Swiften/Base/foreach.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubOwnerAffiliationsConvertor::PubSubOwnerAffiliationsConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubOwnerAffiliations>("pubsub_owner_affiliations"), + convertors(convertors) { +} + +PubSubOwnerAffiliationsConvertor::~PubSubOwnerAffiliationsConvertor() { +} + +boost::shared_ptr<PubSubOwnerAffiliations> PubSubOwnerAffiliationsConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubOwnerAffiliations> result = boost::make_shared<PubSubOwnerAffiliations>(); + lua_getfield(L, -1, "node"); + if (lua_isstring(L, -1)) { + result->setNode(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + if (lua_type(L, -1) == LUA_TTABLE) { + std::vector< boost::shared_ptr<PubSubOwnerAffiliation> > items; + for(size_t i = 0; i < lua_objlen(L, -1); ++i) { + lua_pushnumber(L, i + 1); + lua_gettable(L, -2); + if (!lua_isnil(L, -1)) { + if (boost::shared_ptr<PubSubOwnerAffiliation> payload = boost::dynamic_pointer_cast<PubSubOwnerAffiliation>(convertors->convertFromLuaUntyped(L, -1, "pubsub_owner_affiliation"))) { + items.push_back(payload); + } + } + lua_pop(L, 1); + } + + result->setAffiliations(items); + } + return result; +} + +void PubSubOwnerAffiliationsConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubOwnerAffiliations> payload) { + lua_createtable(L, 0, 0); + lua_pushstring(L, payload->getNode().c_str()); + lua_setfield(L, -2, "node"); + if (!payload->getAffiliations().empty()) { + { + int i = 0; + foreach(boost::shared_ptr<PubSubOwnerAffiliation> item, payload->getAffiliations()) { + if (convertors->convertToLuaUntyped(L, item) > 0) { + lua_rawseti(L, -2, boost::numeric_cast<int>(i+1)); + ++i; + } + } + } + } +} + +boost::optional<LuaElementConvertor::Documentation> PubSubOwnerAffiliationsConvertor::getDocumentation() const { + return Documentation( + "PubSubOwnerAffiliations", + "This table has the following fields:\n\n" + "- `node`: string\n" + "- `affiliations`: array<@{PubSubOwnerAffiliation}>\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubOwnerAffiliationsConvertor.h b/Sluift/ElementConvertors/PubSubOwnerAffiliationsConvertor.h new file mode 100644 index 0000000..2a00539 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubOwnerAffiliationsConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubOwnerAffiliations.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubOwnerAffiliationsConvertor : public GenericLuaElementConvertor<PubSubOwnerAffiliations> { + public: + PubSubOwnerAffiliationsConvertor(LuaElementConvertors* convertors); + virtual ~PubSubOwnerAffiliationsConvertor(); + + virtual boost::shared_ptr<PubSubOwnerAffiliations> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubOwnerAffiliations>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubOwnerConfigureConvertor.cpp b/Sluift/ElementConvertors/PubSubOwnerConfigureConvertor.cpp new file mode 100644 index 0000000..c18b0bc --- /dev/null +++ b/Sluift/ElementConvertors/PubSubOwnerConfigureConvertor.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubOwnerConfigureConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> + + +#include <Sluift/LuaElementConvertors.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubOwnerConfigureConvertor::PubSubOwnerConfigureConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubOwnerConfigure>("pubsub_owner_configure"), + convertors(convertors) { +} + +PubSubOwnerConfigureConvertor::~PubSubOwnerConfigureConvertor() { +} + +boost::shared_ptr<PubSubOwnerConfigure> PubSubOwnerConfigureConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubOwnerConfigure> result = boost::make_shared<PubSubOwnerConfigure>(); + lua_getfield(L, -1, "node"); + if (lua_isstring(L, -1)) { + result->setNode(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "data"); + if (!lua_isnil(L, -1)) { + if (boost::shared_ptr<Form> payload = boost::dynamic_pointer_cast<Form>(convertors->convertFromLuaUntyped(L, -1, "form"))) { + result->setData(payload); + } + } + lua_pop(L, 1); + return result; +} + +void PubSubOwnerConfigureConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubOwnerConfigure> payload) { + lua_createtable(L, 0, 0); + if (payload->getNode()) { + lua_pushstring(L, (*payload->getNode()).c_str()); + lua_setfield(L, -2, "node"); + } + if (convertors->convertToLuaUntyped(L, payload->getData()) > 0) { + lua_setfield(L, -2, "data"); + } +} + +boost::optional<LuaElementConvertor::Documentation> PubSubOwnerConfigureConvertor::getDocumentation() const { + return Documentation( + "PubSubOwnerConfigure", + "This table has the following fields:\n\n" + "- `node`: string (Optional)\n" + "- `data`: @{Form}\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubOwnerConfigureConvertor.h b/Sluift/ElementConvertors/PubSubOwnerConfigureConvertor.h new file mode 100644 index 0000000..ec6a34c --- /dev/null +++ b/Sluift/ElementConvertors/PubSubOwnerConfigureConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubOwnerConfigure.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubOwnerConfigureConvertor : public GenericLuaElementConvertor<PubSubOwnerConfigure> { + public: + PubSubOwnerConfigureConvertor(LuaElementConvertors* convertors); + virtual ~PubSubOwnerConfigureConvertor(); + + virtual boost::shared_ptr<PubSubOwnerConfigure> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubOwnerConfigure>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubOwnerDefaultConvertor.cpp b/Sluift/ElementConvertors/PubSubOwnerDefaultConvertor.cpp new file mode 100644 index 0000000..e99edbb --- /dev/null +++ b/Sluift/ElementConvertors/PubSubOwnerDefaultConvertor.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubOwnerDefaultConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> + + +#include <Sluift/LuaElementConvertors.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubOwnerDefaultConvertor::PubSubOwnerDefaultConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubOwnerDefault>("pubsub_owner_default"), + convertors(convertors) { +} + +PubSubOwnerDefaultConvertor::~PubSubOwnerDefaultConvertor() { +} + +boost::shared_ptr<PubSubOwnerDefault> PubSubOwnerDefaultConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubOwnerDefault> result = boost::make_shared<PubSubOwnerDefault>(); + lua_getfield(L, -1, "data"); + if (!lua_isnil(L, -1)) { + if (boost::shared_ptr<Form> payload = boost::dynamic_pointer_cast<Form>(convertors->convertFromLuaUntyped(L, -1, "form"))) { + result->setData(payload); + } + } + lua_pop(L, 1); + return result; +} + +void PubSubOwnerDefaultConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubOwnerDefault> payload) { + lua_createtable(L, 0, 0); + if (convertors->convertToLuaUntyped(L, payload->getData()) > 0) { + lua_setfield(L, -2, "data"); + } +} + +boost::optional<LuaElementConvertor::Documentation> PubSubOwnerDefaultConvertor::getDocumentation() const { + return Documentation( + "PubSubOwnerDefault", + "This table has the following fields:\n\n" + "- `data`: @{Form}\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubOwnerDefaultConvertor.h b/Sluift/ElementConvertors/PubSubOwnerDefaultConvertor.h new file mode 100644 index 0000000..824db8d --- /dev/null +++ b/Sluift/ElementConvertors/PubSubOwnerDefaultConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubOwnerDefault.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubOwnerDefaultConvertor : public GenericLuaElementConvertor<PubSubOwnerDefault> { + public: + PubSubOwnerDefaultConvertor(LuaElementConvertors* convertors); + virtual ~PubSubOwnerDefaultConvertor(); + + virtual boost::shared_ptr<PubSubOwnerDefault> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubOwnerDefault>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubOwnerDeleteConvertor.cpp b/Sluift/ElementConvertors/PubSubOwnerDeleteConvertor.cpp new file mode 100644 index 0000000..815e910 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubOwnerDeleteConvertor.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubOwnerDeleteConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> + + +#include <Sluift/LuaElementConvertors.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubOwnerDeleteConvertor::PubSubOwnerDeleteConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubOwnerDelete>("pubsub_owner_delete"), + convertors(convertors) { +} + +PubSubOwnerDeleteConvertor::~PubSubOwnerDeleteConvertor() { +} + +boost::shared_ptr<PubSubOwnerDelete> PubSubOwnerDeleteConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubOwnerDelete> result = boost::make_shared<PubSubOwnerDelete>(); + lua_getfield(L, -1, "node"); + if (lua_isstring(L, -1)) { + result->setNode(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "redirect"); + if (!lua_isnil(L, -1)) { + if (boost::shared_ptr<PubSubOwnerRedirect> payload = boost::dynamic_pointer_cast<PubSubOwnerRedirect>(convertors->convertFromLuaUntyped(L, -1, "pubsub_owner_redirect"))) { + result->setRedirect(payload); + } + } + lua_pop(L, 1); + return result; +} + +void PubSubOwnerDeleteConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubOwnerDelete> payload) { + lua_createtable(L, 0, 0); + lua_pushstring(L, payload->getNode().c_str()); + lua_setfield(L, -2, "node"); + if (convertors->convertToLuaUntyped(L, payload->getRedirect()) > 0) { + lua_setfield(L, -2, "redirect"); + } +} + +boost::optional<LuaElementConvertor::Documentation> PubSubOwnerDeleteConvertor::getDocumentation() const { + return Documentation( + "PubSubOwnerDelete", + "This table has the following fields:\n\n" + "- `node`: string\n" + "- `redirect`: @{PubSubOwnerRedirect}\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubOwnerDeleteConvertor.h b/Sluift/ElementConvertors/PubSubOwnerDeleteConvertor.h new file mode 100644 index 0000000..583c8e4 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubOwnerDeleteConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubOwnerDelete.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubOwnerDeleteConvertor : public GenericLuaElementConvertor<PubSubOwnerDelete> { + public: + PubSubOwnerDeleteConvertor(LuaElementConvertors* convertors); + virtual ~PubSubOwnerDeleteConvertor(); + + virtual boost::shared_ptr<PubSubOwnerDelete> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubOwnerDelete>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubOwnerPurgeConvertor.cpp b/Sluift/ElementConvertors/PubSubOwnerPurgeConvertor.cpp new file mode 100644 index 0000000..d34c298 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubOwnerPurgeConvertor.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubOwnerPurgeConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> + + + + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubOwnerPurgeConvertor::PubSubOwnerPurgeConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubOwnerPurge>("pubsub_owner_purge"), + convertors(convertors) { +} + +PubSubOwnerPurgeConvertor::~PubSubOwnerPurgeConvertor() { +} + +boost::shared_ptr<PubSubOwnerPurge> PubSubOwnerPurgeConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubOwnerPurge> result = boost::make_shared<PubSubOwnerPurge>(); + lua_getfield(L, -1, "node"); + if (lua_isstring(L, -1)) { + result->setNode(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + return result; +} + +void PubSubOwnerPurgeConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubOwnerPurge> payload) { + lua_createtable(L, 0, 0); + lua_pushstring(L, payload->getNode().c_str()); + lua_setfield(L, -2, "node"); +} + +boost::optional<LuaElementConvertor::Documentation> PubSubOwnerPurgeConvertor::getDocumentation() const { + return Documentation( + "PubSubOwnerPurge", + "This table has the following fields:\n\n" + "- `node`: string\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubOwnerPurgeConvertor.h b/Sluift/ElementConvertors/PubSubOwnerPurgeConvertor.h new file mode 100644 index 0000000..d8e134e --- /dev/null +++ b/Sluift/ElementConvertors/PubSubOwnerPurgeConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubOwnerPurge.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubOwnerPurgeConvertor : public GenericLuaElementConvertor<PubSubOwnerPurge> { + public: + PubSubOwnerPurgeConvertor(LuaElementConvertors* convertors); + virtual ~PubSubOwnerPurgeConvertor(); + + virtual boost::shared_ptr<PubSubOwnerPurge> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubOwnerPurge>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubOwnerRedirectConvertor.cpp b/Sluift/ElementConvertors/PubSubOwnerRedirectConvertor.cpp new file mode 100644 index 0000000..71386f9 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubOwnerRedirectConvertor.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubOwnerRedirectConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> + + + + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubOwnerRedirectConvertor::PubSubOwnerRedirectConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubOwnerRedirect>("pubsub_owner_redirect"), + convertors(convertors) { +} + +PubSubOwnerRedirectConvertor::~PubSubOwnerRedirectConvertor() { +} + +boost::shared_ptr<PubSubOwnerRedirect> PubSubOwnerRedirectConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubOwnerRedirect> result = boost::make_shared<PubSubOwnerRedirect>(); + lua_getfield(L, -1, "uri"); + if (lua_isstring(L, -1)) { + result->setURI(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + return result; +} + +void PubSubOwnerRedirectConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubOwnerRedirect> payload) { + lua_createtable(L, 0, 0); + lua_pushstring(L, payload->getURI().c_str()); + lua_setfield(L, -2, "uri"); +} + +boost::optional<LuaElementConvertor::Documentation> PubSubOwnerRedirectConvertor::getDocumentation() const { + return Documentation( + "PubSubOwnerRedirect", + "This table has the following fields:\n\n" + "- `uri`: string\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubOwnerRedirectConvertor.h b/Sluift/ElementConvertors/PubSubOwnerRedirectConvertor.h new file mode 100644 index 0000000..a8c031a --- /dev/null +++ b/Sluift/ElementConvertors/PubSubOwnerRedirectConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubOwnerRedirect.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubOwnerRedirectConvertor : public GenericLuaElementConvertor<PubSubOwnerRedirect> { + public: + PubSubOwnerRedirectConvertor(LuaElementConvertors* convertors); + virtual ~PubSubOwnerRedirectConvertor(); + + virtual boost::shared_ptr<PubSubOwnerRedirect> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubOwnerRedirect>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubOwnerSubscriptionConvertor.cpp b/Sluift/ElementConvertors/PubSubOwnerSubscriptionConvertor.cpp new file mode 100644 index 0000000..3f34aff --- /dev/null +++ b/Sluift/ElementConvertors/PubSubOwnerSubscriptionConvertor.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubOwnerSubscriptionConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> + + + + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubOwnerSubscriptionConvertor::PubSubOwnerSubscriptionConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubOwnerSubscription>("pubsub_owner_subscription"), + convertors(convertors) { +} + +PubSubOwnerSubscriptionConvertor::~PubSubOwnerSubscriptionConvertor() { +} + +boost::shared_ptr<PubSubOwnerSubscription> PubSubOwnerSubscriptionConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubOwnerSubscription> result = boost::make_shared<PubSubOwnerSubscription>(); + lua_getfield(L, -1, "jid"); + if (lua_isstring(L, -1)) { + result->setJID(JID(std::string(lua_tostring(L, -1)))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "subscription"); + if (lua_isstring(L, -1)) { + if (std::string(lua_tostring(L, -1)) == "none") { + result->setSubscription(PubSubOwnerSubscription::None); + } + if (std::string(lua_tostring(L, -1)) == "pending") { + result->setSubscription(PubSubOwnerSubscription::Pending); + } + if (std::string(lua_tostring(L, -1)) == "subscribed") { + result->setSubscription(PubSubOwnerSubscription::Subscribed); + } + if (std::string(lua_tostring(L, -1)) == "unconfigured") { + result->setSubscription(PubSubOwnerSubscription::Unconfigured); + } + } + lua_pop(L, 1); + return result; +} + +void PubSubOwnerSubscriptionConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubOwnerSubscription> payload) { + lua_createtable(L, 0, 0); + lua_pushstring(L, payload->getJID().toString().c_str()); + lua_setfield(L, -2, "jid"); + switch (payload->getSubscription()) { + case PubSubOwnerSubscription::None: + lua_pushstring(L, "none"); + break; + case PubSubOwnerSubscription::Pending: + lua_pushstring(L, "pending"); + break; + case PubSubOwnerSubscription::Subscribed: + lua_pushstring(L, "subscribed"); + break; + case PubSubOwnerSubscription::Unconfigured: + lua_pushstring(L, "unconfigured"); + break; + } + lua_setfield(L, -2, "subscription"); +} + +boost::optional<LuaElementConvertor::Documentation> PubSubOwnerSubscriptionConvertor::getDocumentation() const { + return Documentation( + "PubSubOwnerSubscription", + "This table has the following fields:\n\n" + "- `jid`: jid (string)\n" + "- `subscription`: `\"none\"`, `\"pending\"`, `\"subscribed\"`, or `\"unconfigured\"`\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubOwnerSubscriptionConvertor.h b/Sluift/ElementConvertors/PubSubOwnerSubscriptionConvertor.h new file mode 100644 index 0000000..523f9e0 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubOwnerSubscriptionConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubOwnerSubscription.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubOwnerSubscriptionConvertor : public GenericLuaElementConvertor<PubSubOwnerSubscription> { + public: + PubSubOwnerSubscriptionConvertor(LuaElementConvertors* convertors); + virtual ~PubSubOwnerSubscriptionConvertor(); + + virtual boost::shared_ptr<PubSubOwnerSubscription> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubOwnerSubscription>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubOwnerSubscriptionsConvertor.cpp b/Sluift/ElementConvertors/PubSubOwnerSubscriptionsConvertor.cpp new file mode 100644 index 0000000..fbed95b --- /dev/null +++ b/Sluift/ElementConvertors/PubSubOwnerSubscriptionsConvertor.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubOwnerSubscriptionsConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <boost/numeric/conversion/cast.hpp> + +#include <Sluift/LuaElementConvertors.h> +#include <Swiften/Base/foreach.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubOwnerSubscriptionsConvertor::PubSubOwnerSubscriptionsConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubOwnerSubscriptions>("pubsub_owner_subscriptions"), + convertors(convertors) { +} + +PubSubOwnerSubscriptionsConvertor::~PubSubOwnerSubscriptionsConvertor() { +} + +boost::shared_ptr<PubSubOwnerSubscriptions> PubSubOwnerSubscriptionsConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubOwnerSubscriptions> result = boost::make_shared<PubSubOwnerSubscriptions>(); + lua_getfield(L, -1, "node"); + if (lua_isstring(L, -1)) { + result->setNode(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + if (lua_type(L, -1) == LUA_TTABLE) { + std::vector< boost::shared_ptr<PubSubOwnerSubscription> > items; + for(size_t i = 0; i < lua_objlen(L, -1); ++i) { + lua_pushnumber(L, i + 1); + lua_gettable(L, -2); + if (!lua_isnil(L, -1)) { + if (boost::shared_ptr<PubSubOwnerSubscription> payload = boost::dynamic_pointer_cast<PubSubOwnerSubscription>(convertors->convertFromLuaUntyped(L, -1, "pubsub_owner_subscription"))) { + items.push_back(payload); + } + } + lua_pop(L, 1); + } + + result->setSubscriptions(items); + } + return result; +} + +void PubSubOwnerSubscriptionsConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubOwnerSubscriptions> payload) { + lua_createtable(L, 0, 0); + lua_pushstring(L, payload->getNode().c_str()); + lua_setfield(L, -2, "node"); + if (!payload->getSubscriptions().empty()) { + { + int i = 0; + foreach(boost::shared_ptr<PubSubOwnerSubscription> item, payload->getSubscriptions()) { + if (convertors->convertToLuaUntyped(L, item) > 0) { + lua_rawseti(L, -2, boost::numeric_cast<int>(i+1)); + ++i; + } + } + } + } +} + +boost::optional<LuaElementConvertor::Documentation> PubSubOwnerSubscriptionsConvertor::getDocumentation() const { + return Documentation( + "PubSubOwnerSubscriptions", + "This table has the following fields:\n\n" + "- `node`: string\n" + "- `subscriptions`: array<@{PubSubOwnerSubscription}>\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubOwnerSubscriptionsConvertor.h b/Sluift/ElementConvertors/PubSubOwnerSubscriptionsConvertor.h new file mode 100644 index 0000000..61a7a86 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubOwnerSubscriptionsConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubOwnerSubscriptions.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubOwnerSubscriptionsConvertor : public GenericLuaElementConvertor<PubSubOwnerSubscriptions> { + public: + PubSubOwnerSubscriptionsConvertor(LuaElementConvertors* convertors); + virtual ~PubSubOwnerSubscriptionsConvertor(); + + virtual boost::shared_ptr<PubSubOwnerSubscriptions> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubOwnerSubscriptions>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubPublishConvertor.cpp b/Sluift/ElementConvertors/PubSubPublishConvertor.cpp new file mode 100644 index 0000000..4a5ae35 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubPublishConvertor.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubPublishConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <boost/numeric/conversion/cast.hpp> + +#include <Sluift/LuaElementConvertors.h> +#include <Swiften/Base/foreach.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubPublishConvertor::PubSubPublishConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubPublish>("pubsub_publish"), + convertors(convertors) { +} + +PubSubPublishConvertor::~PubSubPublishConvertor() { +} + +boost::shared_ptr<PubSubPublish> PubSubPublishConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubPublish> result = boost::make_shared<PubSubPublish>(); + lua_getfield(L, -1, "node"); + if (lua_isstring(L, -1)) { + result->setNode(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "items"); + if (lua_type(L, -1) == LUA_TTABLE) { + std::vector< boost::shared_ptr<PubSubItem> > items; + for(size_t i = 0; i < lua_objlen(L, -1); ++i) { + lua_pushnumber(L, i + 1); + lua_gettable(L, -2); + if (!lua_isnil(L, -1)) { + if (boost::shared_ptr<PubSubItem> payload = boost::dynamic_pointer_cast<PubSubItem>(convertors->convertFromLuaUntyped(L, -1, "pubsub_item"))) { + items.push_back(payload); + } + } + lua_pop(L, 1); + } + + result->setItems(items); + } + lua_pop(L, 1); + return result; +} + +void PubSubPublishConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubPublish> payload) { + lua_createtable(L, 0, 0); + lua_pushstring(L, payload->getNode().c_str()); + lua_setfield(L, -2, "node"); + if (!payload->getItems().empty()) { + lua_createtable(L, boost::numeric_cast<int>(payload->getItems().size()), 0); + { + int i = 0; + foreach(boost::shared_ptr<PubSubItem> item, payload->getItems()) { + if (convertors->convertToLuaUntyped(L, item) > 0) { + lua_rawseti(L, -2, boost::numeric_cast<int>(i+1)); + ++i; + } + } + } + lua_setfield(L, -2, "items"); + } +} + +boost::optional<LuaElementConvertor::Documentation> PubSubPublishConvertor::getDocumentation() const { + return Documentation( + "PubSubPublish", + "This table has the following fields:\n\n" + "- `node`: string\n" + "- `items`: array<@{PubSubItem}>\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubPublishConvertor.h b/Sluift/ElementConvertors/PubSubPublishConvertor.h new file mode 100644 index 0000000..94e444b --- /dev/null +++ b/Sluift/ElementConvertors/PubSubPublishConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubPublish.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubPublishConvertor : public GenericLuaElementConvertor<PubSubPublish> { + public: + PubSubPublishConvertor(LuaElementConvertors* convertors); + virtual ~PubSubPublishConvertor(); + + virtual boost::shared_ptr<PubSubPublish> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubPublish>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubRetractConvertor.cpp b/Sluift/ElementConvertors/PubSubRetractConvertor.cpp new file mode 100644 index 0000000..df32615 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubRetractConvertor.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubRetractConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <boost/numeric/conversion/cast.hpp> + +#include <Sluift/LuaElementConvertors.h> +#include <Swiften/Base/foreach.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubRetractConvertor::PubSubRetractConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubRetract>("pubsub_retract"), + convertors(convertors) { +} + +PubSubRetractConvertor::~PubSubRetractConvertor() { +} + +boost::shared_ptr<PubSubRetract> PubSubRetractConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubRetract> result = boost::make_shared<PubSubRetract>(); + lua_getfield(L, -1, "node"); + if (lua_isstring(L, -1)) { + result->setNode(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "items"); + if (lua_type(L, -1) == LUA_TTABLE) { + std::vector< boost::shared_ptr<PubSubItem> > items; + for(size_t i = 0; i < lua_objlen(L, -1); ++i) { + lua_pushnumber(L, i + 1); + lua_gettable(L, -2); + if (!lua_isnil(L, -1)) { + if (boost::shared_ptr<PubSubItem> payload = boost::dynamic_pointer_cast<PubSubItem>(convertors->convertFromLuaUntyped(L, -1, "pubsub_item"))) { + items.push_back(payload); + } + } + lua_pop(L, 1); + } + + result->setItems(items); + } + lua_pop(L, 1); + lua_getfield(L, -1, "notify"); + if (lua_isboolean(L, -1)) { + result->setNotify(lua_toboolean(L, -1)); + } + lua_pop(L, 1); + return result; +} + +void PubSubRetractConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubRetract> payload) { + lua_createtable(L, 0, 0); + lua_pushstring(L, payload->getNode().c_str()); + lua_setfield(L, -2, "node"); + if (!payload->getItems().empty()) { + lua_createtable(L, boost::numeric_cast<int>(payload->getItems().size()), 0); + { + int i = 0; + foreach(boost::shared_ptr<PubSubItem> item, payload->getItems()) { + if (convertors->convertToLuaUntyped(L, item) > 0) { + lua_rawseti(L, -2, boost::numeric_cast<int>(i+1)); + ++i; + } + } + } + lua_setfield(L, -2, "items"); + } + lua_pushboolean(L, payload->isNotify()); + lua_setfield(L, -2, "notify"); +} + +boost::optional<LuaElementConvertor::Documentation> PubSubRetractConvertor::getDocumentation() const { + return Documentation( + "PubSubRetract", + "This table has the following fields:\n\n" + "- `node`: string\n" + "- `items`: array<@{PubSubItem}>\n" + "- `notify`: boolean\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubRetractConvertor.h b/Sluift/ElementConvertors/PubSubRetractConvertor.h new file mode 100644 index 0000000..ea2c07e --- /dev/null +++ b/Sluift/ElementConvertors/PubSubRetractConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubRetract.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubRetractConvertor : public GenericLuaElementConvertor<PubSubRetract> { + public: + PubSubRetractConvertor(LuaElementConvertors* convertors); + virtual ~PubSubRetractConvertor(); + + virtual boost::shared_ptr<PubSubRetract> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubRetract>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubSubscribeConvertor.cpp b/Sluift/ElementConvertors/PubSubSubscribeConvertor.cpp new file mode 100644 index 0000000..c6d6024 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubSubscribeConvertor.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubSubscribeConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> + + +#include <Sluift/LuaElementConvertors.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubSubscribeConvertor::PubSubSubscribeConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubSubscribe>("pubsub_subscribe"), + convertors(convertors) { +} + +PubSubSubscribeConvertor::~PubSubSubscribeConvertor() { +} + +boost::shared_ptr<PubSubSubscribe> PubSubSubscribeConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubSubscribe> result = boost::make_shared<PubSubSubscribe>(); + lua_getfield(L, -1, "node"); + if (lua_isstring(L, -1)) { + result->setNode(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "jid"); + if (lua_isstring(L, -1)) { + result->setJID(JID(std::string(lua_tostring(L, -1)))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "options"); + if (!lua_isnil(L, -1)) { + if (boost::shared_ptr<PubSubOptions> payload = boost::dynamic_pointer_cast<PubSubOptions>(convertors->convertFromLuaUntyped(L, -1, "pubsub_options"))) { + result->setOptions(payload); + } + } + lua_pop(L, 1); + return result; +} + +void PubSubSubscribeConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubSubscribe> payload) { + lua_createtable(L, 0, 0); + if (payload->getNode()) { + lua_pushstring(L, (*payload->getNode()).c_str()); + lua_setfield(L, -2, "node"); + } + lua_pushstring(L, payload->getJID().toString().c_str()); + lua_setfield(L, -2, "jid"); + if (convertors->convertToLuaUntyped(L, payload->getOptions()) > 0) { + lua_setfield(L, -2, "options"); + } +} + +boost::optional<LuaElementConvertor::Documentation> PubSubSubscribeConvertor::getDocumentation() const { + return Documentation( + "PubSubSubscribe", + "This table has the following fields:\n\n" + "- `node`: string (Optional)\n" + "- `jid`: jid (string)\n" + "- `options`: @{PubSubOptions}\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubSubscribeConvertor.h b/Sluift/ElementConvertors/PubSubSubscribeConvertor.h new file mode 100644 index 0000000..ded24a2 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubSubscribeConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubSubscribe.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubSubscribeConvertor : public GenericLuaElementConvertor<PubSubSubscribe> { + public: + PubSubSubscribeConvertor(LuaElementConvertors* convertors); + virtual ~PubSubSubscribeConvertor(); + + virtual boost::shared_ptr<PubSubSubscribe> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubSubscribe>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubSubscribeOptionsConvertor.cpp b/Sluift/ElementConvertors/PubSubSubscribeOptionsConvertor.cpp new file mode 100644 index 0000000..b6ecdf3 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubSubscribeOptionsConvertor.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubSubscribeOptionsConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> + + + + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubSubscribeOptionsConvertor::PubSubSubscribeOptionsConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubSubscribeOptions>("pubsub_subscribe_options"), + convertors(convertors) { +} + +PubSubSubscribeOptionsConvertor::~PubSubSubscribeOptionsConvertor() { +} + +boost::shared_ptr<PubSubSubscribeOptions> PubSubSubscribeOptionsConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubSubscribeOptions> result = boost::make_shared<PubSubSubscribeOptions>(); + lua_getfield(L, -1, "required"); + if (lua_isboolean(L, -1)) { + result->setRequired(lua_toboolean(L, -1)); + } + lua_pop(L, 1); + return result; +} + +void PubSubSubscribeOptionsConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubSubscribeOptions> payload) { + lua_createtable(L, 0, 0); + lua_pushboolean(L, payload->isRequired()); + lua_setfield(L, -2, "required"); +} + +boost::optional<LuaElementConvertor::Documentation> PubSubSubscribeOptionsConvertor::getDocumentation() const { + return Documentation( + "PubSubSubscribeOptions", + "This table has the following fields:\n\n" + "- `required`: boolean\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubSubscribeOptionsConvertor.h b/Sluift/ElementConvertors/PubSubSubscribeOptionsConvertor.h new file mode 100644 index 0000000..ca3aacf --- /dev/null +++ b/Sluift/ElementConvertors/PubSubSubscribeOptionsConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubSubscribeOptions.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubSubscribeOptionsConvertor : public GenericLuaElementConvertor<PubSubSubscribeOptions> { + public: + PubSubSubscribeOptionsConvertor(LuaElementConvertors* convertors); + virtual ~PubSubSubscribeOptionsConvertor(); + + virtual boost::shared_ptr<PubSubSubscribeOptions> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubSubscribeOptions>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubSubscriptionConvertor.cpp b/Sluift/ElementConvertors/PubSubSubscriptionConvertor.cpp new file mode 100644 index 0000000..3e04641 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubSubscriptionConvertor.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubSubscriptionConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> + + +#include <Sluift/LuaElementConvertors.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubSubscriptionConvertor::PubSubSubscriptionConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubSubscription>("pubsub_subscription"), + convertors(convertors) { +} + +PubSubSubscriptionConvertor::~PubSubSubscriptionConvertor() { +} + +boost::shared_ptr<PubSubSubscription> PubSubSubscriptionConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubSubscription> result = boost::make_shared<PubSubSubscription>(); + lua_getfield(L, -1, "node"); + if (lua_isstring(L, -1)) { + result->setNode(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "subscription_id"); + if (lua_isstring(L, -1)) { + result->setSubscriptionID(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "jid"); + if (lua_isstring(L, -1)) { + result->setJID(JID(std::string(lua_tostring(L, -1)))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "options"); + if (!lua_isnil(L, -1)) { + if (boost::shared_ptr<PubSubSubscribeOptions> payload = boost::dynamic_pointer_cast<PubSubSubscribeOptions>(convertors->convertFromLuaUntyped(L, -1, "pubsub_subscribe_options"))) { + result->setOptions(payload); + } + } + lua_pop(L, 1); + lua_getfield(L, -1, "subscription"); + if (lua_isstring(L, -1)) { + if (std::string(lua_tostring(L, -1)) == "none") { + result->setSubscription(PubSubSubscription::None); + } + if (std::string(lua_tostring(L, -1)) == "pending") { + result->setSubscription(PubSubSubscription::Pending); + } + if (std::string(lua_tostring(L, -1)) == "subscribed") { + result->setSubscription(PubSubSubscription::Subscribed); + } + if (std::string(lua_tostring(L, -1)) == "unconfigured") { + result->setSubscription(PubSubSubscription::Unconfigured); + } + } + lua_pop(L, 1); + return result; +} + +void PubSubSubscriptionConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubSubscription> payload) { + lua_createtable(L, 0, 0); + if (payload->getNode()) { + lua_pushstring(L, (*payload->getNode()).c_str()); + lua_setfield(L, -2, "node"); + } + if (payload->getSubscriptionID()) { + lua_pushstring(L, (*payload->getSubscriptionID()).c_str()); + lua_setfield(L, -2, "subscription_id"); + } + lua_pushstring(L, payload->getJID().toString().c_str()); + lua_setfield(L, -2, "jid"); + if (convertors->convertToLuaUntyped(L, payload->getOptions()) > 0) { + lua_setfield(L, -2, "options"); + } + switch (payload->getSubscription()) { + case PubSubSubscription::None: + lua_pushstring(L, "none"); + break; + case PubSubSubscription::Pending: + lua_pushstring(L, "pending"); + break; + case PubSubSubscription::Subscribed: + lua_pushstring(L, "subscribed"); + break; + case PubSubSubscription::Unconfigured: + lua_pushstring(L, "unconfigured"); + break; + } + lua_setfield(L, -2, "subscription"); +} + +boost::optional<LuaElementConvertor::Documentation> PubSubSubscriptionConvertor::getDocumentation() const { + return Documentation( + "PubSubSubscription", + "This table has the following fields:\n\n" + "- `node`: string (Optional)\n" + "- `subscription_id`: string (Optional)\n" + "- `jid`: jid (string)\n" + "- `options`: @{PubSubSubscribeOptions}\n" + "- `subscription`: `\"none\"`, `\"pending\"`, `\"subscribed\"`, or `\"unconfigured\"`\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubSubscriptionConvertor.h b/Sluift/ElementConvertors/PubSubSubscriptionConvertor.h new file mode 100644 index 0000000..af0f22a --- /dev/null +++ b/Sluift/ElementConvertors/PubSubSubscriptionConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubSubscription.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubSubscriptionConvertor : public GenericLuaElementConvertor<PubSubSubscription> { + public: + PubSubSubscriptionConvertor(LuaElementConvertors* convertors); + virtual ~PubSubSubscriptionConvertor(); + + virtual boost::shared_ptr<PubSubSubscription> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubSubscription>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubSubscriptionsConvertor.cpp b/Sluift/ElementConvertors/PubSubSubscriptionsConvertor.cpp new file mode 100644 index 0000000..380f393 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubSubscriptionsConvertor.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubSubscriptionsConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <boost/numeric/conversion/cast.hpp> + +#include <Sluift/LuaElementConvertors.h> +#include <Swiften/Base/foreach.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubSubscriptionsConvertor::PubSubSubscriptionsConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubSubscriptions>("pubsub_subscriptions"), + convertors(convertors) { +} + +PubSubSubscriptionsConvertor::~PubSubSubscriptionsConvertor() { +} + +boost::shared_ptr<PubSubSubscriptions> PubSubSubscriptionsConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubSubscriptions> result = boost::make_shared<PubSubSubscriptions>(); + lua_getfield(L, -1, "node"); + if (lua_isstring(L, -1)) { + result->setNode(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + if (lua_type(L, -1) == LUA_TTABLE) { + std::vector< boost::shared_ptr<PubSubSubscription> > items; + for(size_t i = 0; i < lua_objlen(L, -1); ++i) { + lua_pushnumber(L, i + 1); + lua_gettable(L, -2); + if (!lua_isnil(L, -1)) { + if (boost::shared_ptr<PubSubSubscription> payload = boost::dynamic_pointer_cast<PubSubSubscription>(convertors->convertFromLuaUntyped(L, -1, "pubsub_subscription"))) { + items.push_back(payload); + } + } + lua_pop(L, 1); + } + + result->setSubscriptions(items); + } + return result; +} + +void PubSubSubscriptionsConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubSubscriptions> payload) { + lua_createtable(L, 0, 0); + if (payload->getNode()) { + lua_pushstring(L, (*payload->getNode()).c_str()); + lua_setfield(L, -2, "node"); + } + if (!payload->getSubscriptions().empty()) { + { + int i = 0; + foreach(boost::shared_ptr<PubSubSubscription> item, payload->getSubscriptions()) { + if (convertors->convertToLuaUntyped(L, item) > 0) { + lua_rawseti(L, -2, boost::numeric_cast<int>(i+1)); + ++i; + } + } + } + } +} + +boost::optional<LuaElementConvertor::Documentation> PubSubSubscriptionsConvertor::getDocumentation() const { + return Documentation( + "PubSubSubscriptions", + "This table has the following fields:\n\n" + "- `node`: string (Optional)\n" + "- `subscriptions`: array<@{PubSubSubscription}>\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubSubscriptionsConvertor.h b/Sluift/ElementConvertors/PubSubSubscriptionsConvertor.h new file mode 100644 index 0000000..8313e4c --- /dev/null +++ b/Sluift/ElementConvertors/PubSubSubscriptionsConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubSubscriptions.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubSubscriptionsConvertor : public GenericLuaElementConvertor<PubSubSubscriptions> { + public: + PubSubSubscriptionsConvertor(LuaElementConvertors* convertors); + virtual ~PubSubSubscriptionsConvertor(); + + virtual boost::shared_ptr<PubSubSubscriptions> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubSubscriptions>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/PubSubUnsubscribeConvertor.cpp b/Sluift/ElementConvertors/PubSubUnsubscribeConvertor.cpp new file mode 100644 index 0000000..0c22425 --- /dev/null +++ b/Sluift/ElementConvertors/PubSubUnsubscribeConvertor.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/PubSubUnsubscribeConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> + + + + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +PubSubUnsubscribeConvertor::PubSubUnsubscribeConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<PubSubUnsubscribe>("pubsub_unsubscribe"), + convertors(convertors) { +} + +PubSubUnsubscribeConvertor::~PubSubUnsubscribeConvertor() { +} + +boost::shared_ptr<PubSubUnsubscribe> PubSubUnsubscribeConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<PubSubUnsubscribe> result = boost::make_shared<PubSubUnsubscribe>(); + lua_getfield(L, -1, "node"); + if (lua_isstring(L, -1)) { + result->setNode(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "jid"); + if (lua_isstring(L, -1)) { + result->setJID(JID(std::string(lua_tostring(L, -1)))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "subscription_id"); + if (lua_isstring(L, -1)) { + result->setSubscriptionID(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + return result; +} + +void PubSubUnsubscribeConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<PubSubUnsubscribe> payload) { + lua_createtable(L, 0, 0); + if (payload->getNode()) { + lua_pushstring(L, (*payload->getNode()).c_str()); + lua_setfield(L, -2, "node"); + } + lua_pushstring(L, payload->getJID().toString().c_str()); + lua_setfield(L, -2, "jid"); + if (payload->getSubscriptionID()) { + lua_pushstring(L, (*payload->getSubscriptionID()).c_str()); + lua_setfield(L, -2, "subscription_id"); + } +} + +boost::optional<LuaElementConvertor::Documentation> PubSubUnsubscribeConvertor::getDocumentation() const { + return Documentation( + "PubSubUnsubscribe", + "This table has the following fields:\n\n" + "- `node`: string (Optional)\n" + "- `jid`: jid (string)\n" + "- `subscription_id`: string (Optional)\n" + ); +} diff --git a/Sluift/ElementConvertors/PubSubUnsubscribeConvertor.h b/Sluift/ElementConvertors/PubSubUnsubscribeConvertor.h new file mode 100644 index 0000000..f4362fe --- /dev/null +++ b/Sluift/ElementConvertors/PubSubUnsubscribeConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/PubSubUnsubscribe.h> + +namespace Swift { + class LuaElementConvertors; + + class PubSubUnsubscribeConvertor : public GenericLuaElementConvertor<PubSubUnsubscribe> { + public: + PubSubUnsubscribeConvertor(LuaElementConvertors* convertors); + virtual ~PubSubUnsubscribeConvertor(); + + virtual boost::shared_ptr<PubSubUnsubscribe> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<PubSubUnsubscribe>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/RawXMLElementConvertor.cpp b/Sluift/ElementConvertors/RawXMLElementConvertor.cpp new file mode 100644 index 0000000..e4cfe05 --- /dev/null +++ b/Sluift/ElementConvertors/RawXMLElementConvertor.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/ElementConvertors/RawXMLElementConvertor.h> + +#include <iostream> +#include <boost/smart_ptr/make_shared.hpp> +#include <lua.hpp> + +#include <Swiften/Elements/RawXMLPayload.h> +#include <Swiften/Serializer/PayloadSerializer.h> +#include <Sluift/Lua/Check.h> + +using namespace Swift; + +RawXMLElementConvertor::RawXMLElementConvertor() { +} + +RawXMLElementConvertor::~RawXMLElementConvertor() { +} + +boost::shared_ptr<Element> RawXMLElementConvertor::convertFromLua(lua_State* L, int index, const std::string& type) { + if (type == "xml") { + return boost::make_shared<RawXMLPayload>(std::string(Lua::checkString(L, index))); + } + return boost::shared_ptr<Payload>(); +} + +boost::optional<std::string> RawXMLElementConvertor::convertToLua(lua_State* L, boost::shared_ptr<Element> element) { + boost::shared_ptr<Payload> payload = boost::dynamic_pointer_cast<Payload>(element); + if (!payload) { + return boost::optional<std::string>(); + } + PayloadSerializer* serializer = serializers.getPayloadSerializer(payload); + assert(serializer); + lua_pushstring(L, serializer->serialize(payload).c_str()); + return std::string("xml"); +} diff --git a/Sluift/ElementConvertors/RawXMLElementConvertor.h b/Sluift/ElementConvertors/RawXMLElementConvertor.h new file mode 100644 index 0000000..2ee76c5 --- /dev/null +++ b/Sluift/ElementConvertors/RawXMLElementConvertor.h @@ -0,0 +1,26 @@ +/* + * 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/LuaElementConvertor.h> +#include <Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h> + +namespace Swift { + class RawXMLElementConvertor : public LuaElementConvertor { + public: + RawXMLElementConvertor(); + virtual ~RawXMLElementConvertor(); + + virtual boost::shared_ptr<Element> convertFromLua(lua_State*, int index, const std::string& type) SWIFTEN_OVERRIDE; + virtual boost::optional<std::string> convertToLua(lua_State*, boost::shared_ptr<Element>) SWIFTEN_OVERRIDE; + + private: + FullPayloadSerializerCollection serializers; + }; +} diff --git a/Sluift/ElementConvertors/ResultSetConvertor.cpp b/Sluift/ElementConvertors/ResultSetConvertor.cpp new file mode 100644 index 0000000..bd517b3 --- /dev/null +++ b/Sluift/ElementConvertors/ResultSetConvertor.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2014 Kevin Smith and Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <boost/numeric/conversion/cast.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <lua.hpp> +#include <Sluift/ElementConvertors/ResultSetConvertor.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +ResultSetConvertor::ResultSetConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<ResultSet>("result_set"), + convertors(convertors) { +} + +ResultSetConvertor::~ResultSetConvertor() { +} + +boost::shared_ptr<ResultSet> ResultSetConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<ResultSet> result = boost::make_shared<ResultSet>(); + lua_getfield(L, -1, "max_items"); + if (lua_isstring(L, -1)) { + result->setMaxItems(boost::numeric_cast<int>(lua_tonumber(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "count"); + if (lua_isnumber(L, -1)) { + result->setCount(boost::numeric_cast<int>(lua_tonumber(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "first_id_index"); + if (lua_isstring(L, -1)) { + result->setFirstIDIndex(boost::numeric_cast<int>(lua_tonumber(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "first_id"); + if (lua_isstring(L, -1)) { + result->setFirstID(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "last_id"); + if (lua_isstring(L, -1)) { + result->setLastID(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "after"); + if (lua_isstring(L, -1)) { + result->setAfter(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "before"); + if (lua_isstring(L, -1)) { + result->setBefore(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + return result; +} + +void ResultSetConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<ResultSet> payload) { + lua_createtable(L, 0, 0); + if (payload->getMaxItems()) { + lua_pushnumber(L, *payload->getMaxItems()); + lua_setfield(L, -2, "max_items"); + } + if (payload->getCount()) { + lua_pushnumber(L, *payload->getCount()); + lua_setfield(L, -2, "count"); + } + if (payload->getFirstIDIndex()) { + lua_pushnumber(L, *payload->getFirstIDIndex()); + lua_setfield(L, -2, "first_id_index"); + } + if (payload->getFirstID()) { + lua_pushstring(L, (*payload->getFirstID()).c_str()); + lua_setfield(L, -2, "first_id"); + } + if (payload->getLastID()) { + lua_pushstring(L, (*payload->getLastID()).c_str()); + lua_setfield(L, -2, "last_id"); + } + if (payload->getAfter()) { + lua_pushstring(L, (*payload->getAfter()).c_str()); + lua_setfield(L, -2, "after"); + } + if (payload->getBefore()) { + lua_pushstring(L, (*payload->getBefore()).c_str()); + lua_setfield(L, -2, "before"); + } +} + +boost::optional<LuaElementConvertor::Documentation> ResultSetConvertor::getDocumentation() const { + return Documentation( + "ResultSet", + "This table has the following fields:\n\n" + "- `max_items`: number (Optional)\n" + "- `count`: number (Optional)\n" + "- `first_id_index`: number (Optional)\n" + "- `first_id`: string (Optional)\n" + "- `last_id`: string (Optional)\n" + "- `after`: string (Optional)\n" + ); +} diff --git a/Sluift/ElementConvertors/ResultSetConvertor.h b/Sluift/ElementConvertors/ResultSetConvertor.h new file mode 100644 index 0000000..a8f4f85 --- /dev/null +++ b/Sluift/ElementConvertors/ResultSetConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2014 Kevin Smith and Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Base/Override.h> +#include <Swiften/Elements/ResultSet.h> + +namespace Swift { + class LuaElementConvertors; + + class ResultSetConvertor : public GenericLuaElementConvertor<ResultSet> { + public: + ResultSetConvertor(LuaElementConvertors* convertors); + virtual ~ResultSetConvertor(); + + virtual boost::shared_ptr<ResultSet> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<ResultSet>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} + diff --git a/Sluift/ElementConvertors/SConscript b/Sluift/ElementConvertors/SConscript new file mode 100644 index 0000000..5ff5379 --- /dev/null +++ b/Sluift/ElementConvertors/SConscript @@ -0,0 +1,54 @@ + +Import('env') + +convertors = [ + env.File("PubSubRetractConvertor.cpp"), + env.File("PubSubAffiliationsConvertor.cpp"), + env.File("PubSubPublishConvertor.cpp"), + env.File("PubSubItemsConvertor.cpp"), + env.File("PubSubOwnerRedirectConvertor.cpp"), + env.File("PubSubEventRedirectConvertor.cpp"), + env.File("UserTuneConvertor.cpp"), + env.File("PubSubConfigureConvertor.cpp"), + env.File("PubSubEventDisassociateConvertor.cpp"), + env.File("PubSubOwnerAffiliationsConvertor.cpp"), + env.File("PubSubOwnerConfigureConvertor.cpp"), + env.File("UserLocationConvertor.cpp"), + env.File("PubSubSubscribeOptionsConvertor.cpp"), + env.File("PubSubOwnerSubscriptionsConvertor.cpp"), + env.File("PubSubDefaultConvertor.cpp"), + env.File("PubSubEventCollectionConvertor.cpp"), + env.File("PubSubEventSubscriptionConvertor.cpp"), + env.File("PubSubEventRetractConvertor.cpp"), + env.File("PubSubItemConvertor.cpp"), + env.File("PubSubUnsubscribeConvertor.cpp"), + env.File("PubSubEventDeleteConvertor.cpp"), + env.File("PubSubCreateConvertor.cpp"), + env.File("PubSubOwnerPurgeConvertor.cpp"), + env.File("PubSubEventItemsConvertor.cpp"), + env.File("PubSubOptionsConvertor.cpp"), + env.File("PubSubEventItemConvertor.cpp"), + env.File("PubSubOwnerSubscriptionConvertor.cpp"), + env.File("PubSubOwnerAffiliationConvertor.cpp"), + env.File("PubSubEventPurgeConvertor.cpp"), + env.File("PubSubAffiliationConvertor.cpp"), + env.File("PubSubSubscribeConvertor.cpp"), + env.File("PubSubOwnerDeleteConvertor.cpp"), + env.File("PubSubOwnerDefaultConvertor.cpp"), + env.File("PubSubSubscriptionsConvertor.cpp"), + env.File("PubSubEventAssociateConvertor.cpp"), + env.File("PubSubSubscriptionConvertor.cpp"), + env.File("SecurityLabelConvertor.cpp"), + env.File("PubSubEventConfigurationConvertor.cpp"), + env.File("IQConvertor.cpp"), + env.File("PresenceConvertor.cpp"), + env.File("MessageConvertor.cpp"), + env.File("ResultSetConvertor.cpp"), + env.File("ForwardedConvertor.cpp"), + env.File("MAMResultConvertor.cpp"), + env.File("MAMQueryConvertor.cpp"), + env.File("MAMArchivedConvertor.cpp"), + env.File("SubjectConvertor.cpp"), + env.File("IsodeIQDelegationConvertor.cpp") +] +Return('convertors') diff --git a/Sluift/ElementConvertors/SecurityLabelConvertor.cpp b/Sluift/ElementConvertors/SecurityLabelConvertor.cpp new file mode 100644 index 0000000..026a8d5 --- /dev/null +++ b/Sluift/ElementConvertors/SecurityLabelConvertor.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2010-2014 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/SecurityLabelConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <boost/numeric/conversion/cast.hpp> + +#include <Swiften/Base/foreach.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +SecurityLabelConvertor::SecurityLabelConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<SecurityLabel>("security_label"), + convertors(convertors) { +} + +SecurityLabelConvertor::~SecurityLabelConvertor() { +} + +boost::shared_ptr<SecurityLabel> SecurityLabelConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<SecurityLabel> result = boost::make_shared<SecurityLabel>(); + lua_getfield(L, -1, "equivalent_labels"); + if (lua_type(L, -1) == LUA_TTABLE) { + std::vector< std::string > items; + for(size_t i = 0; i < lua_objlen(L, -1); ++i) { + lua_pushnumber(L, i + 1); + lua_gettable(L, -2); + if (lua_isstring(L, -1)) { + items.push_back(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + } + + result->setEquivalentLabels(items); + } + lua_pop(L, 1); + lua_getfield(L, -1, "foreground_color"); + if (lua_isstring(L, -1)) { + result->setForegroundColor(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "display_marking"); + if (lua_isstring(L, -1)) { + result->setDisplayMarking(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "background_color"); + if (lua_isstring(L, -1)) { + result->setBackgroundColor(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "label"); + if (lua_isstring(L, -1)) { + result->setLabel(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + return result; +} + +void SecurityLabelConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<SecurityLabel> payload) { + lua_createtable(L, 0, 0); + if (!payload->getEquivalentLabels().empty()) { + lua_createtable(L, boost::numeric_cast<int>(payload->getEquivalentLabels().size()), 0); + { + int i = 0; + foreach(const std::string& item, payload->getEquivalentLabels()) { + lua_pushstring(L, item.c_str()); + lua_rawseti(L, -2, boost::numeric_cast<int>(i+1)); + ++i; + } + } + lua_setfield(L, -2, "equivalent_labels"); + } + lua_pushstring(L, payload->getForegroundColor().c_str()); + lua_setfield(L, -2, "foreground_color"); + lua_pushstring(L, payload->getDisplayMarking().c_str()); + lua_setfield(L, -2, "display_marking"); + lua_pushstring(L, payload->getBackgroundColor().c_str()); + lua_setfield(L, -2, "background_color"); + lua_pushstring(L, payload->getLabel().c_str()); + lua_setfield(L, -2, "label"); +} + +boost::optional<LuaElementConvertor::Documentation> SecurityLabelConvertor::getDocumentation() const { + return Documentation( + "SecurityLabel", + "This table has the following fields:\n\n" + "- `equivalent_labels`: array<string>\n" + "- `foreground_color`: string\n" + "- `display_marking`: string\n" + "- `background_color`: string\n" + "- `label`: string\n" + ); +} diff --git a/Sluift/ElementConvertors/SecurityLabelConvertor.h b/Sluift/ElementConvertors/SecurityLabelConvertor.h new file mode 100644 index 0000000..57bd093 --- /dev/null +++ b/Sluift/ElementConvertors/SecurityLabelConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2010-2014 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/SecurityLabel.h> + +namespace Swift { + class LuaElementConvertors; + + class SecurityLabelConvertor : public GenericLuaElementConvertor<SecurityLabel> { + public: + SecurityLabelConvertor(LuaElementConvertors* convertors); + virtual ~SecurityLabelConvertor(); + + virtual boost::shared_ptr<SecurityLabel> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<SecurityLabel>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/SoftwareVersionConvertor.cpp b/Sluift/ElementConvertors/SoftwareVersionConvertor.cpp new file mode 100644 index 0000000..5799614 --- /dev/null +++ b/Sluift/ElementConvertors/SoftwareVersionConvertor.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/ElementConvertors/SoftwareVersionConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <boost/numeric/conversion/cast.hpp> +#include <Sluift/Lua/Check.h> + +using namespace Swift; + +SoftwareVersionConvertor::SoftwareVersionConvertor() : GenericLuaElementConvertor<SoftwareVersion>("software_version") { +} + +SoftwareVersionConvertor::~SoftwareVersionConvertor() { +} + +boost::shared_ptr<SoftwareVersion> SoftwareVersionConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<SoftwareVersion> result = boost::make_shared<SoftwareVersion>(); + lua_getfield(L, -1, "name"); + if (!lua_isnil(L, -1)) { + result->setName(std::string(Lua::checkString(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "version"); + if (!lua_isnil(L, -1)) { + result->setVersion(std::string(Lua::checkString(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "os"); + if (!lua_isnil(L, -1)) { + result->setOS(std::string(Lua::checkString(L, -1))); + } + lua_pop(L, 1); + return result; +} + +void SoftwareVersionConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<SoftwareVersion> payload) { + lua_createtable(L, 0, 0); + lua_pushstring(L, payload->getName().c_str()); + lua_setfield(L, -2, "name"); + lua_pushstring(L, payload->getVersion().c_str()); + lua_setfield(L, -2, "version"); + lua_pushstring(L, payload->getOS().c_str()); + lua_setfield(L, -2, "os"); +} diff --git a/Sluift/ElementConvertors/SoftwareVersionConvertor.h b/Sluift/ElementConvertors/SoftwareVersionConvertor.h new file mode 100644 index 0000000..5fa3cc3 --- /dev/null +++ b/Sluift/ElementConvertors/SoftwareVersionConvertor.h @@ -0,0 +1,23 @@ +/* + * 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/SoftwareVersion.h> + +namespace Swift { + class SoftwareVersionConvertor : public GenericLuaElementConvertor<SoftwareVersion> { + public: + SoftwareVersionConvertor(); + virtual ~SoftwareVersionConvertor(); + + virtual boost::shared_ptr<SoftwareVersion> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<SoftwareVersion>) SWIFTEN_OVERRIDE; + }; +} diff --git a/Sluift/ElementConvertors/StanzaConvertor.h b/Sluift/ElementConvertors/StanzaConvertor.h new file mode 100644 index 0000000..405371b --- /dev/null +++ b/Sluift/ElementConvertors/StanzaConvertor.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2014 Kevin Smith and Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <boost/numeric/conversion/cast.hpp> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Sluift/LuaElementConvertors.h> +#include <Sluift/Lua/Exception.h> +#include <Swiften/Base/foreach.h> +#include <Swiften/Elements/Payload.h> +#include <Swiften/Elements/IQ.h> +#include <Swiften/Elements/Presence.h> +#include <Swiften/Elements/Message.h> + +namespace Swift { + template <typename T> class StanzaConvertor : public GenericLuaElementConvertor<T> { + public: + StanzaConvertor(const std::string& tag) + : GenericLuaElementConvertor<T>(tag) { + } + + virtual ~StanzaConvertor() { + } + + boost::shared_ptr<T> getStanza(lua_State* L, LuaElementConvertors* convertors) { + boost::shared_ptr<T> result = boost::make_shared<T>(); + lua_getfield(L, -1, "id"); + if (lua_isstring(L, -1)) { + result->setID(lua_tostring(L, -1)); + } + lua_pop(L, 1); + lua_getfield(L, -1, "from"); + if (lua_isstring(L, -1)) { + result->setFrom(lua_tostring(L, -1)); + } + lua_pop(L, 1); + lua_getfield(L, -1, "to"); + if (lua_isstring(L, -1)) { + result->setTo(lua_tostring(L, -1)); + } + lua_pop(L, 1); + lua_getfield(L, -1, "payloads"); + if (lua_type(L, -1) == LUA_TTABLE) { + for(size_t i = 0; i < lua_objlen(L, -1); ++i) { + lua_pushnumber(L, i + 1); + lua_gettable(L, -2); + if (!lua_isnil(L, -1)) { + boost::shared_ptr<Payload> payload = boost::dynamic_pointer_cast<Payload>(convertors->convertFromLua(L, -1)); + if (!!payload) { + result->addPayload(payload); + } + } + lua_pop(L, 1); + } + } + lua_pop(L, 1); + return result; + } + + void pushStanza(lua_State* L, const boost::shared_ptr<T> stanza, LuaElementConvertors* convertors) { + lua_createtable(L, 0, 0); + lua_pushstring(L, stanza->getID().c_str()); + lua_setfield(L, -2, "id"); + lua_pushstring(L, stanza->getFrom().toString().c_str()); + lua_setfield(L, -2, "from"); + lua_pushstring(L, stanza->getTo().toString().c_str()); + lua_setfield(L, -2, "to"); + if (!stanza->getPayloads().empty()) { + lua_createtable(L, boost::numeric_cast<int>(stanza->getPayloads().size()), 0); + { + int i = 0; + foreach(const boost::shared_ptr<Payload> &item, stanza->getPayloads()) { + if (convertors->convertToLua(L, item) > 0) { + lua_rawseti(L, -2, boost::numeric_cast<int>(i+1)); + ++i; + } + } + } + lua_setfield(L, -2, "payloads"); + } + } + }; +} diff --git a/Sluift/ElementConvertors/StatusConvertor.cpp b/Sluift/ElementConvertors/StatusConvertor.cpp new file mode 100644 index 0000000..092e9b7 --- /dev/null +++ b/Sluift/ElementConvertors/StatusConvertor.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2014 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/ElementConvertors/StatusConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <boost/numeric/conversion/cast.hpp> +#include <Sluift/Lua/Check.h> + +using namespace Swift; + +StatusConvertor::StatusConvertor() : GenericLuaElementConvertor<Status>("status") { +} + +StatusConvertor::~StatusConvertor() { +} + +boost::shared_ptr<Status> StatusConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<Status> result = boost::make_shared<Status>(); + lua_getfield(L, -1, "text"); + if (lua_isstring(L, -1)) { + result->setText(lua_tostring(L, -1)); + } + lua_pop(L, 1); + return result; +} + +void StatusConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<Status> payload) { + lua_createtable(L, 0, 0); + lua_pushstring(L, payload->getText().c_str()); + lua_setfield(L, -2, "text"); +} diff --git a/Sluift/ElementConvertors/StatusConvertor.h b/Sluift/ElementConvertors/StatusConvertor.h new file mode 100644 index 0000000..ac494c4 --- /dev/null +++ b/Sluift/ElementConvertors/StatusConvertor.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2014 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/Status.h> + +namespace Swift { + class StatusConvertor : public GenericLuaElementConvertor<Status> { + public: + StatusConvertor(); + virtual ~StatusConvertor(); + + virtual boost::shared_ptr<Status> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<Status>) SWIFTEN_OVERRIDE; + }; +} diff --git a/Sluift/ElementConvertors/StatusShowConvertor.cpp b/Sluift/ElementConvertors/StatusShowConvertor.cpp new file mode 100644 index 0000000..27876ff --- /dev/null +++ b/Sluift/ElementConvertors/StatusShowConvertor.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2014 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/ElementConvertors/StatusShowConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <boost/numeric/conversion/cast.hpp> +#include <Sluift/Lua/Check.h> +#include <Sluift/Lua/Exception.h> + +using namespace Swift; + +StatusShowConvertor::StatusShowConvertor() : GenericLuaElementConvertor<StatusShow>("show") { +} + +StatusShowConvertor::~StatusShowConvertor() { +} + +boost::shared_ptr<StatusShow> StatusShowConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<StatusShow> result = boost::make_shared<StatusShow>(); + lua_getfield(L, -1, "type"); + if (lua_isstring(L, -1)) { + result->setType(convertStatusShowTypeFromString(lua_tostring(L, -1))); + } + lua_pop(L, 1); + return result; +} + +void StatusShowConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<StatusShow> payload) { + lua_createtable(L, 0, 0); + const std::string show = convertStatusShowTypeToString(payload->getType()); + if (!show.empty()) { + lua_pushstring(L, show.c_str()); + lua_setfield(L, -2, "type"); + } +} + +std::string StatusShowConvertor::convertStatusShowTypeToString(const StatusShow::Type &show) { + switch (show) { + case StatusShow::Online: return "online"; + case StatusShow::FFC: return "ffc"; + case StatusShow::Away: return "away"; + case StatusShow::XA: return "xa"; + case StatusShow::DND: return "dnd"; + case StatusShow::None: return ""; + } + assert(false); + return ""; +} + +StatusShow::Type StatusShowConvertor::convertStatusShowTypeFromString(const std::string& show) { + if (show == "online") { + return StatusShow::Online; + } + else if (show == "ffc") { + return StatusShow::FFC; + } + else if (show == "away") { + return StatusShow::Away; + } + else if (show == "xa") { + return StatusShow::XA; + } + else if (show == "dnd") { + return StatusShow::DND; + } + else { + throw Lua::Exception("Illegal status show: '" + show + "'"); + } +} diff --git a/Sluift/ElementConvertors/StatusShowConvertor.h b/Sluift/ElementConvertors/StatusShowConvertor.h new file mode 100644 index 0000000..ef8ed48 --- /dev/null +++ b/Sluift/ElementConvertors/StatusShowConvertor.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2014 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/StatusShow.h> + +namespace Swift { + class StatusShowConvertor : public GenericLuaElementConvertor<StatusShow> { + public: + StatusShowConvertor(); + virtual ~StatusShowConvertor(); + + virtual boost::shared_ptr<StatusShow> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<StatusShow>) SWIFTEN_OVERRIDE; + + static std::string convertStatusShowTypeToString(const StatusShow::Type &show); + static StatusShow::Type convertStatusShowTypeFromString(const std::string& show); + }; +} diff --git a/Sluift/ElementConvertors/SubjectConvertor.cpp b/Sluift/ElementConvertors/SubjectConvertor.cpp new file mode 100644 index 0000000..29e2c4f --- /dev/null +++ b/Sluift/ElementConvertors/SubjectConvertor.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2014 Kevin Smith and Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/ElementConvertors/SubjectConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <Sluift/Lua/LuaUtils.h> + +using namespace Swift; + +SubjectConvertor::SubjectConvertor() : GenericLuaElementConvertor<Subject>("subject") { +} + +SubjectConvertor::~SubjectConvertor() { +} + +boost::shared_ptr<Subject> SubjectConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<Subject> result = boost::make_shared<Subject>(); + if (boost::optional<std::string> value = Lua::getStringField(L, -1, "text")) { + result->setText(*value); + } + return result; +} + +void SubjectConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<Subject> payload) { + lua_createtable(L, 0, 0); + if (!payload->getText().empty()) { + lua_pushstring(L, payload->getText().c_str()); + lua_setfield(L, -2, "text"); + } +} diff --git a/Sluift/ElementConvertors/SubjectConvertor.h b/Sluift/ElementConvertors/SubjectConvertor.h new file mode 100644 index 0000000..4b3716e --- /dev/null +++ b/Sluift/ElementConvertors/SubjectConvertor.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2014 Kevin Smith and 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/Subject.h> + +namespace Swift { + class LuaElementConvertors; + + class SubjectConvertor : public GenericLuaElementConvertor<Subject> { + public: + SubjectConvertor(); + virtual ~SubjectConvertor(); + + virtual boost::shared_ptr<Subject> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<Subject>) SWIFTEN_OVERRIDE; + }; +} diff --git a/Sluift/ElementConvertors/UserLocationConvertor.cpp b/Sluift/ElementConvertors/UserLocationConvertor.cpp new file mode 100644 index 0000000..1ecf68d --- /dev/null +++ b/Sluift/ElementConvertors/UserLocationConvertor.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/UserLocationConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <boost/numeric/conversion/cast.hpp> + +#include <Swiften/Base/DateTime.h> + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +UserLocationConvertor::UserLocationConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<UserLocation>("user_location"), + convertors(convertors) { +} + +UserLocationConvertor::~UserLocationConvertor() { +} + +boost::shared_ptr<UserLocation> UserLocationConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<UserLocation> result = boost::make_shared<UserLocation>(); + lua_getfield(L, -1, "area"); + if (lua_isstring(L, -1)) { + result->setArea(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "altitude"); + if (lua_isnumber(L, -1)) { + result->setAltitude(boost::numeric_cast<float>(lua_tonumber(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "locality"); + if (lua_isstring(L, -1)) { + result->setLocality(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "latitude"); + if (lua_isnumber(L, -1)) { + result->setLatitude(boost::numeric_cast<float>(lua_tonumber(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "accuracy"); + if (lua_isnumber(L, -1)) { + result->setAccuracy(boost::numeric_cast<float>(lua_tonumber(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "description"); + if (lua_isstring(L, -1)) { + result->setDescription(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "country_code"); + if (lua_isstring(L, -1)) { + result->setCountryCode(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "timestamp"); + if (lua_isstring(L, -1)) { + result->setTimestamp(stringToDateTime(std::string(lua_tostring(L, -1)))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "floor"); + if (lua_isstring(L, -1)) { + result->setFloor(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "building"); + if (lua_isstring(L, -1)) { + result->setBuilding(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "room"); + if (lua_isstring(L, -1)) { + result->setRoom(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "country"); + if (lua_isstring(L, -1)) { + result->setCountry(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "region"); + if (lua_isstring(L, -1)) { + result->setRegion(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "uri"); + if (lua_isstring(L, -1)) { + result->setURI(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "longitude"); + if (lua_isnumber(L, -1)) { + result->setLongitude(boost::numeric_cast<float>(lua_tonumber(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "error"); + if (lua_isnumber(L, -1)) { + result->setError(boost::numeric_cast<float>(lua_tonumber(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "postal_code"); + if (lua_isstring(L, -1)) { + result->setPostalCode(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "bearing"); + if (lua_isnumber(L, -1)) { + result->setBearing(boost::numeric_cast<float>(lua_tonumber(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "text"); + if (lua_isstring(L, -1)) { + result->setText(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "datum"); + if (lua_isstring(L, -1)) { + result->setDatum(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "street"); + if (lua_isstring(L, -1)) { + result->setStreet(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "speed"); + if (lua_isnumber(L, -1)) { + result->setSpeed(boost::numeric_cast<float>(lua_tonumber(L, -1))); + } + lua_pop(L, 1); + return result; +} + +void UserLocationConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<UserLocation> payload) { + lua_createtable(L, 0, 0); + if (payload->getArea()) { + lua_pushstring(L, (*payload->getArea()).c_str()); + lua_setfield(L, -2, "area"); + } + if (payload->getAltitude()) { + lua_pushnumber(L, (*payload->getAltitude())); + lua_setfield(L, -2, "altitude"); + } + if (payload->getLocality()) { + lua_pushstring(L, (*payload->getLocality()).c_str()); + lua_setfield(L, -2, "locality"); + } + if (payload->getLatitude()) { + lua_pushnumber(L, (*payload->getLatitude())); + lua_setfield(L, -2, "latitude"); + } + if (payload->getAccuracy()) { + lua_pushnumber(L, (*payload->getAccuracy())); + lua_setfield(L, -2, "accuracy"); + } + if (payload->getDescription()) { + lua_pushstring(L, (*payload->getDescription()).c_str()); + lua_setfield(L, -2, "description"); + } + if (payload->getCountryCode()) { + lua_pushstring(L, (*payload->getCountryCode()).c_str()); + lua_setfield(L, -2, "country_code"); + } + if (payload->getTimestamp()) { + lua_pushstring(L, dateTimeToString((*payload->getTimestamp())).c_str()); + lua_setfield(L, -2, "timestamp"); + } + if (payload->getFloor()) { + lua_pushstring(L, (*payload->getFloor()).c_str()); + lua_setfield(L, -2, "floor"); + } + if (payload->getBuilding()) { + lua_pushstring(L, (*payload->getBuilding()).c_str()); + lua_setfield(L, -2, "building"); + } + if (payload->getRoom()) { + lua_pushstring(L, (*payload->getRoom()).c_str()); + lua_setfield(L, -2, "room"); + } + if (payload->getCountry()) { + lua_pushstring(L, (*payload->getCountry()).c_str()); + lua_setfield(L, -2, "country"); + } + if (payload->getRegion()) { + lua_pushstring(L, (*payload->getRegion()).c_str()); + lua_setfield(L, -2, "region"); + } + if (payload->getURI()) { + lua_pushstring(L, (*payload->getURI()).c_str()); + lua_setfield(L, -2, "uri"); + } + if (payload->getLongitude()) { + lua_pushnumber(L, (*payload->getLongitude())); + lua_setfield(L, -2, "longitude"); + } + if (payload->getError()) { + lua_pushnumber(L, (*payload->getError())); + lua_setfield(L, -2, "error"); + } + if (payload->getPostalCode()) { + lua_pushstring(L, (*payload->getPostalCode()).c_str()); + lua_setfield(L, -2, "postal_code"); + } + if (payload->getBearing()) { + lua_pushnumber(L, (*payload->getBearing())); + lua_setfield(L, -2, "bearing"); + } + if (payload->getText()) { + lua_pushstring(L, (*payload->getText()).c_str()); + lua_setfield(L, -2, "text"); + } + if (payload->getDatum()) { + lua_pushstring(L, (*payload->getDatum()).c_str()); + lua_setfield(L, -2, "datum"); + } + if (payload->getStreet()) { + lua_pushstring(L, (*payload->getStreet()).c_str()); + lua_setfield(L, -2, "street"); + } + if (payload->getSpeed()) { + lua_pushnumber(L, (*payload->getSpeed())); + lua_setfield(L, -2, "speed"); + } +} + +boost::optional<LuaElementConvertor::Documentation> UserLocationConvertor::getDocumentation() const { + return Documentation( + "UserLocation", + "This table has the following fields:\n\n" + "- `area`: string (Optional)\n" + "- `altitude`: @{float} (Optional)\n" + "- `locality`: string (Optional)\n" + "- `latitude`: @{float} (Optional)\n" + "- `accuracy`: @{float} (Optional)\n" + "- `description`: string (Optional)\n" + "- `country_code`: string (Optional)\n" + "- `timestamp`: datetime (string) (Optional)\n" + "- `floor`: string (Optional)\n" + "- `building`: string (Optional)\n" + "- `room`: string (Optional)\n" + "- `country`: string (Optional)\n" + "- `region`: string (Optional)\n" + "- `uri`: string (Optional)\n" + "- `longitude`: @{float} (Optional)\n" + "- `error`: @{float} (Optional)\n" + "- `postal_code`: string (Optional)\n" + "- `bearing`: @{float} (Optional)\n" + "- `text`: string (Optional)\n" + "- `datum`: string (Optional)\n" + "- `street`: string (Optional)\n" + "- `speed`: @{float} (Optional)\n" + ); +} diff --git a/Sluift/ElementConvertors/UserLocationConvertor.h b/Sluift/ElementConvertors/UserLocationConvertor.h new file mode 100644 index 0000000..3e92bd1 --- /dev/null +++ b/Sluift/ElementConvertors/UserLocationConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/UserLocation.h> + +namespace Swift { + class LuaElementConvertors; + + class UserLocationConvertor : public GenericLuaElementConvertor<UserLocation> { + public: + UserLocationConvertor(LuaElementConvertors* convertors); + virtual ~UserLocationConvertor(); + + virtual boost::shared_ptr<UserLocation> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<UserLocation>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/UserTuneConvertor.cpp b/Sluift/ElementConvertors/UserTuneConvertor.cpp new file mode 100644 index 0000000..d4fa16a --- /dev/null +++ b/Sluift/ElementConvertors/UserTuneConvertor.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2014 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Sluift/ElementConvertors/UserTuneConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <boost/numeric/conversion/cast.hpp> + + + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +UserTuneConvertor::UserTuneConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<UserTune>("user_tune"), + convertors(convertors) { +} + +UserTuneConvertor::~UserTuneConvertor() { +} + +boost::shared_ptr<UserTune> UserTuneConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<UserTune> result = boost::make_shared<UserTune>(); + lua_getfield(L, -1, "rating"); + if (lua_isnumber(L, -1)) { + result->setRating(boost::numeric_cast<unsigned int>(lua_tonumber(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "title"); + if (lua_isstring(L, -1)) { + result->setTitle(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "track"); + if (lua_isstring(L, -1)) { + result->setTrack(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "artist"); + if (lua_isstring(L, -1)) { + result->setArtist(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "uri"); + if (lua_isstring(L, -1)) { + result->setURI(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "source"); + if (lua_isstring(L, -1)) { + result->setSource(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "length"); + if (lua_isnumber(L, -1)) { + result->setLength(boost::numeric_cast<unsigned int>(lua_tonumber(L, -1))); + } + lua_pop(L, 1); + return result; +} + +void UserTuneConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<UserTune> payload) { + lua_createtable(L, 0, 0); + if (payload->getRating()) { + lua_pushnumber(L, (*payload->getRating())); + lua_setfield(L, -2, "rating"); + } + if (payload->getTitle()) { + lua_pushstring(L, (*payload->getTitle()).c_str()); + lua_setfield(L, -2, "title"); + } + if (payload->getTrack()) { + lua_pushstring(L, (*payload->getTrack()).c_str()); + lua_setfield(L, -2, "track"); + } + if (payload->getArtist()) { + lua_pushstring(L, (*payload->getArtist()).c_str()); + lua_setfield(L, -2, "artist"); + } + if (payload->getURI()) { + lua_pushstring(L, (*payload->getURI()).c_str()); + lua_setfield(L, -2, "uri"); + } + if (payload->getSource()) { + lua_pushstring(L, (*payload->getSource()).c_str()); + lua_setfield(L, -2, "source"); + } + if (payload->getLength()) { + lua_pushnumber(L, (*payload->getLength())); + lua_setfield(L, -2, "length"); + } +} + +boost::optional<LuaElementConvertor::Documentation> UserTuneConvertor::getDocumentation() const { + return Documentation( + "UserTune", + "This table has the following fields:\n\n" + "- `rating`: number (Optional)\n" + "- `title`: string (Optional)\n" + "- `track`: string (Optional)\n" + "- `artist`: string (Optional)\n" + "- `uri`: string (Optional)\n" + "- `source`: string (Optional)\n" + "- `length`: number (Optional)\n" + ); +} diff --git a/Sluift/ElementConvertors/UserTuneConvertor.h b/Sluift/ElementConvertors/UserTuneConvertor.h new file mode 100644 index 0000000..5044cb3 --- /dev/null +++ b/Sluift/ElementConvertors/UserTuneConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2014 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/UserTune.h> + +namespace Swift { + class LuaElementConvertors; + + class UserTuneConvertor : public GenericLuaElementConvertor<UserTune> { + public: + UserTuneConvertor(LuaElementConvertors* convertors); + virtual ~UserTuneConvertor(); + + virtual boost::shared_ptr<UserTune> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<UserTune>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/ElementConvertors/VCardConvertor.cpp b/Sluift/ElementConvertors/VCardConvertor.cpp new file mode 100644 index 0000000..a1c57be --- /dev/null +++ b/Sluift/ElementConvertors/VCardConvertor.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/ElementConvertors/VCardConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <boost/numeric/conversion/cast.hpp> +#include <Sluift/Lua/LuaUtils.h> +#include <Swiften/Base/ByteArray.h> + +using namespace Swift; + +VCardConvertor::VCardConvertor() : GenericLuaElementConvertor<VCard>("vcard") { +} + +VCardConvertor::~VCardConvertor() { +} + +boost::shared_ptr<VCard> VCardConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<VCard> result = boost::make_shared<VCard>(); + lua_getfield(L, -1, "photo"); + if (lua_isstring(L, -1)) { + size_t len; + const char* data = lua_tolstring(L, -1, &len); + result->setPhoto(createByteArray(data, len)); + } + lua_pop(L, 1); + + // TODO + + return result; +} + +void VCardConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<VCard> payload) { + lua_newtable(L); + if (!payload->getPhoto().empty()) { + lua_pushlstring(L, + reinterpret_cast<const char*>(vecptr(payload->getPhoto())), + payload->getPhoto().size()); + lua_setfield(L, -2, "photo"); + } +} diff --git a/Sluift/ElementConvertors/VCardConvertor.h b/Sluift/ElementConvertors/VCardConvertor.h new file mode 100644 index 0000000..1cf4e9f --- /dev/null +++ b/Sluift/ElementConvertors/VCardConvertor.h @@ -0,0 +1,23 @@ +/* + * 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/VCard.h> + +namespace Swift { + class VCardConvertor : public GenericLuaElementConvertor<VCard> { + public: + VCardConvertor(); + virtual ~VCardConvertor(); + + virtual boost::shared_ptr<VCard> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<VCard>) SWIFTEN_OVERRIDE; + }; +} diff --git a/Sluift/ElementConvertors/VCardUpdateConvertor.cpp b/Sluift/ElementConvertors/VCardUpdateConvertor.cpp new file mode 100644 index 0000000..0010ca5 --- /dev/null +++ b/Sluift/ElementConvertors/VCardUpdateConvertor.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/ElementConvertors/VCardUpdateConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <boost/numeric/conversion/cast.hpp> +#include <Sluift/Lua/LuaUtils.h> + +using namespace Swift; + +VCardUpdateConvertor::VCardUpdateConvertor() : GenericLuaElementConvertor<VCardUpdate>("vcard_update") { +} + +VCardUpdateConvertor::~VCardUpdateConvertor() { +} + +boost::shared_ptr<VCardUpdate> VCardUpdateConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<VCardUpdate> result = boost::make_shared<VCardUpdate>(); + if (boost::optional<std::string> value = Lua::getStringField(L, -1, "photo_hash")) { + result->setPhotoHash(*value); + } + return result; +} + +void VCardUpdateConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<VCardUpdate> payload) { + lua_newtable(L); + if (!payload->getPhotoHash().empty()) { + lua_pushstring(L, payload->getPhotoHash().c_str()); + lua_setfield(L, -2, "photo_hash"); + } +} diff --git a/Sluift/ElementConvertors/VCardUpdateConvertor.h b/Sluift/ElementConvertors/VCardUpdateConvertor.h new file mode 100644 index 0000000..e9bcd0f --- /dev/null +++ b/Sluift/ElementConvertors/VCardUpdateConvertor.h @@ -0,0 +1,23 @@ +/* + * 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/VCardUpdate.h> + +namespace Swift { + class VCardUpdateConvertor : public GenericLuaElementConvertor<VCardUpdate> { + public: + VCardUpdateConvertor(); + virtual ~VCardUpdateConvertor(); + + virtual boost::shared_ptr<VCardUpdate> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<VCardUpdate>) SWIFTEN_OVERRIDE; + }; +} 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/Examples/CollectVersions.lua b/Sluift/Examples/CollectVersions.lua index c93c8c8..38bf6ac 100644 --- a/Sluift/Examples/CollectVersions.lua +++ b/Sluift/Examples/CollectVersions.lua @@ -1,22 +1,22 @@ --- --- Copyright (c) 2010 Remko Tronçon --- Licensed under the GNU General Public License v3. --- See Documentation/Licenses/GPLv3.txt for more information. --- +--[[ --- This script logs into an XMPP server, and collects statistics about --- the server software of all contacts in your roster + Copyright (c) 2010-2013 Remko Tronçon + Licensed under the GNU General Public License v3. + See Documentation/Licenses/GPLv3.txt for more information. -require "sluift" + This script logs into an XMPP server, and collects statistics about + the server software of all contacts in your roster -c = sluift.new_client(os.getenv("SLUIFT_JID"), os.getenv("SLUIFT_PASS")) -c:connect() +--]] +require 'sluift' + +c = sluift.new_client(os.getenv('SLUIFT_JID'), os.getenv('SLUIFT_PASS')) +c:connect(function () versions = {} -for jid, _ in pairs(c:get_contacts()) do - v = c:get_version(sluift.jid_domain(jid)) - if v then versions[v["name"]] = (versions[v["name"]] or 0) + 1 end + for jid in pairs(c:get_contacts()) do + local v = c:get_software_version {to = sluift.jid.domain(jid), timeout = 3000} or {name = 'Unknown'} + versions[v['name']] = (versions[v['name']] or 0) + 1 end -for name, count in pairs(versions) do print(name .. ": " .. count) end - -c:disconnect() + for name, count in pairs(versions) do print(name .. ': ' .. count) end +end) diff --git a/Sluift/Examples/Component.lua b/Sluift/Examples/Component.lua new file mode 100644 index 0000000..b5d6539 --- /dev/null +++ b/Sluift/Examples/Component.lua @@ -0,0 +1,55 @@ +--[[ + Copyright (c) 2014 Edwin Mons and Remko Tronçon + Licensed under the GNU General Public License v3. + See Documentation/Licenses/GPLv3.txt for more information. +--]] + +--[[ + + Component example. + + This script connects to an XMPP server as a component, and listens to + messages received. + + The following environment variables are used: + * SLUIFT_COMP_DOMAIN: Component domain name + * SLUIFT_COMP_SECRET: Component secret + * SLUIFT_COMP_HOST: XMPP server host name + * SLUIFT_COMP_PORT: XMPP server component port + * SLUIFT_JID: Recipient of presence and initial message + * SLUIFT_DEBUG: Sets whether debugging should be turned on + +--]] + +require "sluift" + +sluift.debug = os.getenv("SLUIFT_DEBUG") or false + +config = { + domain = os.getenv('SLUIFT_COMP_DOMAIN'), + secret = os.getenv('SLUIFT_COMP_SECRET'), + host = os.getenv('SLUIFT_COMP_HOST'), + port = os.getenv('SLUIFT_COMP_PORT'), + jid = os.getenv('SLUIFT_JID') +} + +-- Create the component, and connect +comp = sluift.new_component(config.domain, config.secret); +comp:connect(config) + +-- Send initial presence and message +-- Assumes the remote client already has this component user on his roster +comp:send_presence{from='root@' .. config.domain, to=config.jid} +comp:send_message{from='root@' .. config.domain, to=config.jid, body='Component active'} + +-- Listen for messages, and reply if one is received +for message in comp:messages() do + print("Received a message from " .. message.from) + comp:send_message{to=message.from, from=message.to, body='I received: ' .. message['body']} + + -- Send out a ping to demonstrate we can do more than just send messages + comp:get{to=message.from, query='<ping xmlns="urn:xmpp:ping"/>'} +end + +comp:disconnect() + diff --git a/Sluift/Examples/ContactsMap.lua b/Sluift/Examples/ContactsMap.lua new file mode 100644 index 0000000..d248dc7 --- /dev/null +++ b/Sluift/Examples/ContactsMap.lua @@ -0,0 +1,120 @@ +--[[ + Copyright (c) 2013 Remko Tronçon + Licensed under the GNU General Public License v3. + See Documentation/Licenses/GPLv3.txt for more information. +--]] + +--[[ + + Contacts map + + Creates an HTML file of a map with all your contacts on it. + + The following environment variables are used: + - SLUIFT_JID, SWIFT_PASS: JID and password to log in with + +--]] + +require "sluift" + +output_dir = arg[1] +if not output_dir then + error("Please specify the directory to write the map to") +end + +-- Collect all data +geolocs = {} +avatars = {} +c = sluift.new_client(os.getenv("SLUIFT_JID"), os.getenv("SLUIFT_PASS")) +c:connect(function () + -- Indicate we're interested in getting user location information, and send initial presence + c:set_caps_node("http://swift.im/ContactsMap") + c:set_disco_info({identities = {{name = 'ContactsMap'}}, features = { + sluift.disco.features.DISCO_INFO, + sluift.disco.features.USER_LOCATION .. '+notify', + }}) + c:send_presence() + + -- Collect geoloc info + for event in c:pubsub_events {timeout = 10000} do + local from = sluift.jid.to_bare(event.from) + if event._type == 'pubsub_event_items' and event.item then + if event.node == sluift.disco.features.USER_LOCATION then + local lat, lon = event.item.latitude, event.item.longitude + if lat and lon then geolocs[from] = {lat = lat, lon = lon} end + end + end + end + + -- Download the necessary avatars + for contact in pairs(geolocs) do + local vcard = c:get_vcard {to = contact} + if vcard and vcard.photo then + local avatar_hash = sluift.hexify(sluift.sha1(vcard.photo)) + local file = io.open(output_dir.."/" .. avatar_hash .. ".png", "wb") + file:write(vcard.photo) + file:close() + avatars[contact] = avatar_hash + end + end +end) + +-- Generate html +min_lat, max_lat = 90, -90 +min_lon, max_lon = 180, -180 +contacts_html = {} +for contact, geoloc in pairs(geolocs) do + if geoloc.lat < min_lat then min_lat = geoloc.lat end + if geoloc.lon < min_lon then min_lon = geoloc.lon end + if geoloc.lat > max_lat then max_lat = geoloc.lat end + if geoloc.lon > max_lon then max_lon = geoloc.lon end + local image = 'null' + if avatars[contact] then + image = "'" .. avatars[contact] .. ".png'" + end + contacts_html[#contacts_html+1] = "['" .. contact .. "'," .. geoloc.lat .. "," .. geoloc.lon .. "," .. image .. "]" +end +center_html = ((min_lat + max_lat) / 2) .. ',' .. ((min_lon + max_lon) / 2) + +map_html = [[ +<html> + <head> + <title>Contacts Map</title> + <script src="http://maps.google.com/maps/api/js?sensor=false" type="text/javascript"></script> + </head> + <body> + <div id="map" style="height: 100%; width: 100%;"/> + <script> + var map = new google.maps.Map(document.getElementById('map'), { + zoom: 2, + center: new google.maps.LatLng(%(CENTER)), + mapTypeId: google.maps.MapTypeId.ROADMAP + }); + var infowindow = new google.maps.InfoWindow(); + var contacts = [%(CONTACTS)]; + for (var i = 0; i < contacts.length; i++) { + var icon = null; + if (contacts[i][3]) { + icon = { url: contacts[i][3], scaledSize: { width: 30, height: 30} }; + } + var marker = new google.maps.Marker({ + position: new google.maps.LatLng(contacts[i][1], contacts[i][2]), + map: map, + icon: icon, + }); + google.maps.event.addListener(marker, 'click', (function(marker, i) { + return function() { + infowindow.setContent(contacts[i][0]); + infowindow.open(map, marker); + } + })(marker, i)); + } + </script> + </body> +</html> +]] +local file = io.open(output_dir .. "/index.html", "w") +file:write(map_html: + gsub('%%%(CONTACTS%)', table.concat(contacts_html, ",")): + gsub('%%%(CENTER%)', center_html)) +file:close() diff --git a/Sluift/Examples/EchoBot.lua b/Sluift/Examples/EchoBot.lua index 09da63b..fc495c4 100644 --- a/Sluift/Examples/EchoBot.lua +++ b/Sluift/Examples/EchoBot.lua @@ -1,18 +1,20 @@ --- --- Copyright (c) 2010 Remko Tronçon --- Licensed under the GNU General Public License v3. --- See Documentation/Licenses/GPLv3.txt for more information. --- - --- --- An XMPP Echoing Bot --- --- This script logs into an XMPP server, sends initial presence, --- and then waits for incoming messages, and echoes them back. --- --- 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 --- +--[[ + Copyright (c) 2010-2013 Remko Tronçon + Licensed under the GNU General Public License v3. + See Documentation/Licenses/GPLv3.txt for more information. +--]] + +--[[ + + An XMPP Echoing Bot + + This script logs into an XMPP server, sends initial presence, + and then waits for incoming messages, and echoes them back. + + 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" @@ -21,7 +23,9 @@ sluift.debug = os.getenv("SLUIFT_DEBUG") or false c = sluift.new_client(os.getenv("SLUIFT_JID"), os.getenv("SLUIFT_PASS")) -c:connect() +c:connect(function () + c:set_version{name = "EchoBot", version = "0.1"} c:send_presence("Send me a message") -c:for_event(function(e) - if e["type"] == "message" then c:send_message(e["from"], e["body"]) end + for message in c:messages() do + c:send_message{to = message["from"], body = message["body"]} + end end) diff --git a/Sluift/Examples/EchoBot_With.lua b/Sluift/Examples/EchoBot_With.lua new file mode 100644 index 0000000..1f7d0bb --- /dev/null +++ b/Sluift/Examples/EchoBot_With.lua @@ -0,0 +1,32 @@ +--[[ + Copyright (c) 2010-2013 Remko Tronçon + Licensed under the GNU General Public License v3. + See Documentation/Licenses/GPLv3.txt for more information. +--]] + +--[[ + + Alternative version of EchoBot that uses with() + + This script logs into an XMPP server, sends initial presence, + and then waits for incoming messages, and echoes them back. + + 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 + +client = sluift.new_client(os.getenv("SLUIFT_JID"), os.getenv("SLUIFT_PASS")) +sluift.with(client, function () + connect() + set_version{name = "EchoBot", version = "0.1"} + send_presence("Send me a message") + for message in messages() do + send_message{to = message["from"], body = message["body"]} + end +end) diff --git a/Sluift/Examples/Login.lua b/Sluift/Examples/Login.lua index 1733bb9..fadb651 100644 --- a/Sluift/Examples/Login.lua +++ b/Sluift/Examples/Login.lua @@ -1,15 +1,19 @@ --- --- Copyright (c) 2010 Remko Tronçon --- Licensed under the GNU General Public License v3. --- See Documentation/Licenses/GPLv3.txt for more information. --- +--[[ + Copyright (c) 2010-2014 Remko Tronçon + Licensed under the GNU General Public License v3. + See Documentation/Licenses/GPLv3.txt for more information. +--]] --- This script logs into an XMPP server, and sends initial presence --- Useful as initialization script for an interactive session ('-i'), --- or as a starting point for scripts. --- --- 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 +--[[ + + This script logs into an XMPP server, and sends initial presence + Useful as initialization script for an interactive session ('-i'), + or as a starting point for scripts. + + 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" @@ -18,6 +22,8 @@ sluift.debug = os.getenv("SLUIFT_DEBUG") or false print("Connecting " .. os.getenv("SLUIFT_JID") .. " ...") c = sluift.new_client(os.getenv("SLUIFT_JID"), os.getenv("SLUIFT_PASS")) -c:set_options({compress = false, tls = false}) -c:connect():send_presence("") +c:set_options{compress = false, tls = false} +c:connect() +c:send_presence("") print("Connected ...") +print("Use the 'c' variable to communicate.") diff --git a/Sluift/Examples/MAMRSM.lua b/Sluift/Examples/MAMRSM.lua new file mode 100644 index 0000000..c8a1e85 --- /dev/null +++ b/Sluift/Examples/MAMRSM.lua @@ -0,0 +1,23 @@ +-- A query using Result Set Management +-- Usage: ./sluift MAMRSM.lua <jid> <password> <query_dest> <max_results> + +sluift.debug = true + +c = sluift.new_client(arg[1], arg[2]) + +c:connect(); + +query = { + result_set={max_items=arg[4]} +} + +c:set_mam{mam=query, to=arg[3]} + +c:for_each_message(function(e) + if e.payloads[1].tag == 'fin' then return true end + if e.payloads[1]._type == 'mam_result' then + print(e.payloads[1].payload.stanza.payloads[1].text) + end +end) + +c:disconnect() diff --git a/Sluift/Examples/MAMRSMPage.lua b/Sluift/Examples/MAMRSMPage.lua new file mode 100644 index 0000000..cb3307c --- /dev/null +++ b/Sluift/Examples/MAMRSMPage.lua @@ -0,0 +1,36 @@ +-- A page query using Result Set Management +-- Usage: ./sluift MAMRSMPage.lua <jid> <password> <query_dest> <pages> + +sluift.debug = true + +c = sluift.new_client(arg[1], arg[2]) + +c:set_options{compress = false, tls = false} + +c:connect(); + +query = { + result_set={max_items=5} +} + +done = false +page = 0 +while not done and page < tonumber(arg[4]) do + page = page + 1 + c:set_mam{mam=query, to=arg[3]} + c:for_each_message(function(e) + if e.payloads[1].tag == 'fin' then + if e.payloads[2].last_id then + query.result_set.after = e.payloads[2].last_id + else + done = true + end + return true + end + if e.payloads[1]._type == 'mam_result' then + print(e.payloads[1].payload.stanza.payloads[1].text) + end + end) +end + +c:disconnect() diff --git a/Sluift/Examples/MAMSimple.lua b/Sluift/Examples/MAMSimple.lua new file mode 100644 index 0000000..13ab1a0 --- /dev/null +++ b/Sluift/Examples/MAMSimple.lua @@ -0,0 +1,19 @@ +-- Querying the archive for messages +-- Usage: ./sluift MAMSimple.lua <jid> <password> <query_dest> + +sluift.debug = true + +c = sluift.new_client(arg[1], arg[2]) + +c:connect(); + +c:set_mam{mam={}, to=arg[3]} + +c:for_each_message(function(e) + if e.payloads[1].tag == 'fin' then return true end + if e.payloads[1]._type == 'mam_result' then + print(e.payloads[1].payload.stanza.payloads[1].text) + end +end) + +c:disconnect() diff --git a/Sluift/Examples/MAMSupportedFields.lua b/Sluift/Examples/MAMSupportedFields.lua new file mode 100644 index 0000000..0417924 --- /dev/null +++ b/Sluift/Examples/MAMSupportedFields.lua @@ -0,0 +1,18 @@ +-- Retrieving form fields +-- Usage: ./sluift MAMSupportedFields.lua <jid> <password> + +sluift.debug = true + +c = sluift.new_client(arg[1], arg[2]) + +c:connect(); + +mam_result = c:get_mam{} + +for i=1,#mam_result.form.fields do + if mam_result.form.fields[i].type ~= "hidden" then + print("Server supports: " .. mam_result.form.fields[i].name) + end +end + +c:disconnect() diff --git a/Sluift/Examples/MAMUser.lua b/Sluift/Examples/MAMUser.lua new file mode 100644 index 0000000..e4a7c28 --- /dev/null +++ b/Sluift/Examples/MAMUser.lua @@ -0,0 +1,25 @@ +-- Querying for all messages to/from a particular JID +-- Usage: ./sluift MAMUser.lua <jid> <password> <query_dest> <query_jid> + +sluift.debug = true + +c = sluift.new_client(arg[1], arg[2]) + +c:connect(); + +fields = { + with = arg[4] +} + +query_form = sluift.create_form{fields, form_type="urn:xmpp:mam:0"} + +c:set_mam{mam={form=query_form}, to=arg[3]} + +c:for_each_message(function(e) + if e.payloads[1].tag == 'fin' then return true end + if e.payloads[1]._type == 'mam_result' then + print(e.payloads[1].payload.stanza.payloads[1].text) + end +end) + +c:disconnect() diff --git a/Sluift/Examples/PEPListener.lua b/Sluift/Examples/PEPListener.lua new file mode 100644 index 0000000..f196f84 --- /dev/null +++ b/Sluift/Examples/PEPListener.lua @@ -0,0 +1,46 @@ +--[[ + Copyright (c) 2010-2013 Remko Tronçon + Licensed under the GNU General Public License v3. + See Documentation/Licenses/GPLv3.txt for more information. +--]] + +--[[ + + PEP Listener + + Listens to a series of PEP events of all contacts. + + 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 + +pep_protocols = { + [sluift.disco.features.USER_LOCATION] = true, + [sluift.disco.features.USER_TUNE] = true, + [sluift.disco.features.USER_ACTIVITY] = true, + [sluift.disco.features.USER_AVATAR_METADATA] = true, + [sluift.disco.features.USER_PROFILE] = true, +} + +client = sluift.new_client(os.getenv("SLUIFT_JID"), os.getenv("SLUIFT_PASS")) +client:connect(function (c) + features = {sluift.disco.features.DISCO_INFO} + for protocol in pairs(pep_protocols) do + features[#features+1] = protocol .. '+notify' + end + + c:set_caps_node("http://swift.im/PEPListener") + c:set_disco_info({identities = {{name = 'PEPListener'}}, features = features}) + c:send_presence() + for event in c:pubsub_events() do + if event._type == 'pubsub_event_items' and pep_protocols[event.node] then + print("<" .. event.from .. "> " .. tostring(event.item)) + end + end +end) diff --git a/Sluift/Examples/RemoveUnreachableContacts.lua b/Sluift/Examples/RemoveUnreachableContacts.lua index 90122df..a202e62 100644 --- a/Sluift/Examples/RemoveUnreachableContacts.lua +++ b/Sluift/Examples/RemoveUnreachableContacts.lua @@ -1,37 +1,38 @@ --- --- Copyright (c) 2010 Remko Tronçon --- Licensed under the GNU General Public License v3. --- See Documentation/Licenses/GPLv3.txt for more information. --- +--[[ + Copyright (c) 2010-2013 Remko Tronçon + Licensed under the GNU General Public License v3. + See Documentation/Licenses/GPLv3.txt for more information. +--]] --- This script logs into an XMPP server, iterates over all roster items, --- and checks if their server is still alive. If not, the script asks you --- whether it should remove the contact from your contact list. --- --- 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 +--[[ + This script logs into an XMPP server, iterates over all roster items, + and checks if their server is still alive. If not, the script asks you + whether it should remove the contact from your contact list. -require "sluift" -sluift.debug = os.getenv("SLUIFT_DEBUG") + 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 +--]] -print "Connecting ..." -c = sluift.new_client(os.getenv("SLUIFT_JID"), os.getenv("SLUIFT_PASS")) -c:connect() +require 'sluift' +sluift.debug = os.getenv('SLUIFT_DEBUG') -print "Checking for unreachable contacts ..." -for jid, _ in pairs(c:get_contacts()) do - _, err = c:get_version(sluift.jid_domain(jid), 10000) - if err == "Remote server not found" or err == "Timeout" then - print("Delete " .. jid .. " (" .. err .. ") ? [y/n/q]") +print 'Connecting ...' +c = sluift.new_client(os.getenv('SLUIFT_JID'), os.getenv('SLUIFT_PASS')) +c:connect(function (c) + print 'Checking for unreachable contacts ...' + for jid in pairs(c:get_contacts()) do + _, err = c:get_software_version {to = sluift.jid.domain(jid), timeout = 10000} + print(err) + if err == 'Remote server not found' or err == 'Remote server timeout' then + print('Delete ' .. jid .. ' (' .. err .. ') ? [y/n/q]') answer = io.read() - if answer == "y" then + if answer == 'y' then c:remove_contact(jid) - elseif answer == "q" then + elseif answer == 'q' then break end end end - -print "Done. Exiting ..." -c:disconnect() + print 'Done. Exiting ...' +end) diff --git a/Sluift/Examples/Tunes.lua b/Sluift/Examples/Tunes.lua new file mode 100644 index 0000000..37ad996 --- /dev/null +++ b/Sluift/Examples/Tunes.lua @@ -0,0 +1,68 @@ +--[[ + Copyright (c) 2014 Remko Tronçon + Licensed under the GNU General Public License v3. + See Documentation/Licenses/GPLv3.txt for more information. +--]] + +--[[ + + Tune Publisher/Listener + + Publishes the currently playing tune in iTunes, and prints + the playing tunes of contacts + + 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 + +client = sluift.new_client(os.getenv('SLUIFT_JID'), os.getenv('SLUIFT_PASS')) +client:connect(function (c) + -- Send initial presence (with service discovery information) + c:set_caps_node('http://swift.im/Tunes') + c:set_disco_info{ + identities = {{name = 'Tunes'}}, + features = { + sluift.disco.features.DISCO_INFO, + sluift.disco.features.USER_TUNE .. '+notify' + }} + c:send_presence{priority = -1} + + local pubsub = c:pubsub(sluift.jid.to_bare(c:jid())) + local tunes_node = pubsub:node(sluift.disco.features.USER_TUNE) + local current_track = nil + while true do + -- Publish currently playing tune + if sluift.itunes then + local track = sluift.itunes.get_current_track() + if track ~= current_track then + tunes_node:publish{item = { + _type = 'user_tune', + title = track.name, + artist = track.artist, + track = track.track_number, + source = track.album, + length = track.length, + rating = track.rating and track.rating / 10 or nil + }} + current_track = track + end + end + + -- Print incoming events for a while + for event in c:pubsub_events{timeout = 1000} do + if event._type == 'pubsub_event_items' + and event.node == sluift.disco.features.USER_TUNE + and event.item.title then + print(event.from .. ' is playing "' + .. (event.item.artist or '<Unknown Artist>') .. ' - ' + .. event.item.title .. '"') + end + end + end +end) diff --git a/Sluift/Examples/Wonderland.lua b/Sluift/Examples/Wonderland.lua index 58c00ca..426ec07 100755 --- a/Sluift/Examples/Wonderland.lua +++ b/Sluift/Examples/Wonderland.lua @@ -9,44 +9,96 @@ require "sluift" ---sluift.debug = true +sluift.debug = true characters = { - {jid = "alice@wonderland.lit", name = "Alice", groups = {}, presence = "<presence/>"}, - {jid = "hatter@wonderland.lit", name = "Mad Hatter", groups = {}, presence = "<presence><show>away</show><status>At the Tea Party</status></presence>"}, - {jid ="queen@wonderland.lit", name = "Queen of Hearts", groups = {}, presence = "<presence><show>dnd</show><status>Executing</status></presence>"}, - {jid = "rabbit@wonderland.lit", name = "White Rabbit", groups = {"Animals"}, presence = "<presence><status>Oh dear!</status></presence>"}, - {jid = "turtle@wonderland.lit", name = "Mock Turtle", groups = {"Animals"}, presence = "<presence/>"}, + ["Alice"] = { + jid = "alice@wonderland.lit", groups = {}, presence = "<presence/>" + }, + ["Mad Hatter"] = { + jid = "hatter@wonderland.lit", groups = {}, + presence = "<presence><show>away</show><status>At the Tea Party</status></presence>" + }, + ["Queen of Hearts"] = { + jid ="queen@wonderland.lit", groups = {}, + presence = "<presence><show>dnd</show><status>Executing</status></presence>" + }, + ["White Rabbit"] = { + jid = "rabbit@wonderland.lit", groups = {"Animals"}, + presence = "<presence><status>Oh dear!</status></presence>"}, + ["Mock Turtle"] = { + jid = "turtle@wonderland.lit", groups = {"Animals"}, + presence = "<presence/>" + }, } -clients = {} -for _, character in ipairs(characters) do - print("Connecting " .. character["name"] .. "...") - client = sluift.new_client(character["jid"], os.getenv("SLUIFT_PASS")) +for name, character in pairs(characters) do + print("Connecting " .. name .. "...") + local client = sluift.new_client(character.jid, os.getenv("SLUIFT_PASS")) client:set_options({compress = false, tls = false}) client:connect() client:get_contacts() - client:send(character["presence"]) - table.insert(clients, client) - for _, contact in ipairs(characters) do - if contact["jid"] ~= character["jid"] then + client:send(character.presence) + for contact_name, contact in pairs(characters) do + if contact.jid ~= character.jid then client:add_contact(contact) end end + character.client = client end print("Confirming subscriptions") -for _, client in ipairs(clients) do - for _, contact in ipairs(characters) do - client:confirm_subscription(contact["jid"]) +for _, character in pairs(characters) do + for _, contact in pairs(characters) do + character.client:confirm_subscription(contact.jid) end end +print("Setting up PubSub nodes") +local hatters_riddles = characters["Mad Hatter"].client:pubsub("pubsub.wonderland.lit"):node("hatters_riddles") +hatters_riddles:delete() +assert(hatters_riddles:create()) + +local queen_quotes = characters["Queen of Hearts"].client:pubsub("pubsub.wonderland.lit"):node("queen_quotes") +queen_quotes:delete() +assert(queen_quotes:create()) +queen_quotes:publish{id = 'quote1', item = {_type = 'body', text = 'Off with his head!'}} +queen_quotes:publish{id = 'quote2', item = {_type = 'body', text = 'Off with her head!'}} +queen_quotes:publish{id = 'quote3', item = {_type = 'body', text = 'Off with their heads!'}} + +characters['Mad Hatter'].client:pubsub():node('http://jabber.org/protocol/geoloc'):publish{ + item = {_type = 'user_location', latitude = 50.376739, longitude = -4.200709}} +characters['Queen of Hearts'].client:pubsub():node('http://jabber.org/protocol/geoloc'):publish{ + item = {_type = 'user_location', latitude = 50.551123, longitude = -4.141654}} +characters['Mock Turtle'].client:pubsub():node('http://jabber.org/protocol/geoloc'):publish{ + item = {_type = 'user_location', latitude = 50.366630, longitude = -4.134518}} +characters['White Rabbit'].client:pubsub():node('http://jabber.org/protocol/geoloc'):publish{ + item = {_type = 'user_location', latitude = 50.332907, longitude = -4.759194}} + + + +print("Disconnecting alice") +characters['Alice'].client:disconnect() + print("Done. Waiting ...") while true do - for _, client in ipairs(clients) do - client:for_event(function(e) - if e["type"] == "message" then client:send_message(e["from"], "Off with their heads!") end - end, 1000) + for name, character in pairs(characters) do + if name == 'Queen of Hearts' then + for message in character.client:messages{timeout = 1000} do + if message.body == 'publish' then + queen_quotes:publish{item = {_type = 'body', text = 'Off with her head!'}} + queen_quotes:publish{item = {_type = 'body', text = 'Off with his head!'}} + else + character.client:send_message{to = e["from"], body = "Off with their heads!"} + end + end + elseif name == "Mad Hatter" then + for message in character.client:messages{timeout = 1000} do + if message.body == 'publish' then + hatters_riddles:publish{item = {_type = 'body', text = 'Why is a raven like a writing desk?'}} + end + end + else + for message in character.client:messages{timeout = 100} do end + end end - sluift.sleep(1000) end diff --git a/Sluift/GenerateDocumentation.lua b/Sluift/GenerateDocumentation.lua new file mode 100644 index 0000000..69693ea --- /dev/null +++ b/Sluift/GenerateDocumentation.lua @@ -0,0 +1,90 @@ +--[[ + Copyright (c) 2013 Remko Tronçon + Licensed under the GNU General Public License. + See the COPYING file for more information. +--]] + +require "sluift" + +local function get_anchor(...) + return table.concat({...}, "-") +end + +local function convert_links(text) + result = text:gsub("(@{(%w*)})", "[`%2`](#%2)") + return result +end + +local function add_help(help, document, class, level) + -- Collect help of children + local methods = {} + for _, method in pairs(help.methods or {}) do + local description + local method_help = sluift.get_help(method.ref) + if method_help and method_help.description then + description = method_help.synopsis + end + methods[#methods+1] = { name = method.name, description = description, ref = method.ref } + end + local fields = sluift.copy(help.fields or {}) + + table.sort(methods, function (a, b) return (a.name or "") < (b.name or "") end) + table.sort(fields, function (a, b) return (a.name or "") < (b.name or "") end) + + local classes = {} + for _, class in pairs(help.classes or {}) do + classes[#classes+1] = { name = class, description = sluift.get_help(class).synopsis } + end + + table.insert(document, convert_links(help.description or "")) + for _, p in ipairs({ + {"Parameters", help.parameters}, {"Options", help.options}, {"Fields", fields}, {"Methods", methods}}) do + if p[2] and next(p[2]) ~= nil then + table.insert(document, "\n\n" .. level .. " " .. p[1] .. "\n") + for _, parameter in ipairs(p[2]) do + parameter_name = "`" .. parameter.name .. "`" + if p[1] == "Methods" then + parameter_name = "[" .. parameter_name .. "](#" .. get_anchor(class, parameter.name) .. ")" + end + if parameter.description then + table.insert(document, "- " .. parameter_name .. ": " .. convert_links(parameter.description)) + else + table.insert(document, "- " .. parameter_name) + end + end + if p[1] == "Methods" then + for _, method in ipairs(p[2]) do + table.insert(document, "\n#### <a name=\"" .. get_anchor(class, method.name) .. "\"></a> `" .. method.name .. "`\n") + if method.ref then + add_help(sluift.get_help(method.ref) or {}, document, class, level .. "#") + end + end + end + end + end +end + +local document = {} + +table.insert(document, [[ +# Sluift + +This document describes the API of the `sluift` module. + +The entry points of Sluift are in the `sluift` module, described below. + +## `sluift` module +]]) + +help = sluift.get_help(sluift) +add_help(help, document, "sluift", "###") +for _, class in pairs(help.classes) do + class_help = sluift.get_help(class) + if class_help then + table.insert(document, "\n## <a name=\"" .. class .. "\"></a> `" .. class .. "` class\n") + add_help(class_help, document, class, "###") + end +end + +document = table.concat(document, "\n") .. "\n" +print(document) diff --git a/Sluift/GenericLuaElementConvertor.h b/Sluift/GenericLuaElementConvertor.h new file mode 100644 index 0000000..3753348 --- /dev/null +++ b/Sluift/GenericLuaElementConvertor.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#pragma once + +#include <lua.hpp> +#include <string> + +#include <Swiften/Base/Override.h> +#include <Sluift/LuaElementConvertor.h> +#include <Sluift/Lua/Check.h> +#include <Sluift/Lua/LuaUtils.h> + +namespace Swift { + template<typename T> + class GenericLuaElementConvertor : public LuaElementConvertor { + public: + GenericLuaElementConvertor(const std::string& type) : type(type) { + } + + virtual ~GenericLuaElementConvertor() {} + + virtual boost::shared_ptr<Element> convertFromLua(lua_State* L, int index, const std::string& payloadType) SWIFTEN_OVERRIDE { + if (payloadType == type) { + Lua::checkType(L, index, LUA_TTABLE); + lua_pushvalue(L, index); + boost::shared_ptr<Element> result = doConvertFromLua(L); + lua_pop(L, 1); + return result; + } + return boost::shared_ptr<Element>(); + } + + virtual boost::optional<std::string> convertToLua( + lua_State* L, boost::shared_ptr<Element> payload) SWIFTEN_OVERRIDE { + if (boost::shared_ptr<T> actualPayload = boost::dynamic_pointer_cast<T>(payload)) { + doConvertToLua(L, actualPayload); + assert(lua_type(L, -1) == LUA_TTABLE); + return type; + } + return NO_RESULT; + } + + protected: + virtual boost::shared_ptr<T> doConvertFromLua(lua_State*) = 0; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<T>) = 0; + + private: + std::string type; + }; +} diff --git a/Sluift/Helpers.cpp b/Sluift/Helpers.cpp new file mode 100644 index 0000000..756ae59 --- /dev/null +++ b/Sluift/Helpers.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2013-2014 Kevin Smith and Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/Helpers.h> + +#include <Swiften/Client/ClientError.h> +#include <Swiften/Component/ComponentError.h> + +using namespace Swift; + +std::string Swift::getErrorString(const ClientError& error) { + std::string reason = "Disconnected: "; + switch(error.getType()) { + case ClientError::UnknownError: reason += "Unknown Error"; break; + case ClientError::ConnectionError: reason += "Error connecting to server"; break; + case ClientError::ConnectionReadError: reason += "Error while receiving server data"; break; + case ClientError::ConnectionWriteError: reason += "Error while sending data to the server"; break; + case ClientError::XMLError: reason += "Error parsing server data"; break; + case ClientError::AuthenticationFailedError: reason += "Login/password invalid"; break; + case ClientError::UnexpectedElementError: reason += "Unexpected response"; break; + case ClientError::DomainNameResolveError: reason += "Unable to find server"; break; + case ClientError::CompressionFailedError: reason += "Error while compressing stream"; break; + case ClientError::ServerVerificationFailedError: reason += "Server verification failed"; break; + case ClientError::NoSupportedAuthMechanismsError: reason += "Authentication mechanisms not supported"; break; + case ClientError::ResourceBindError: reason += "Error binding resource"; break; + case ClientError::RevokedError: reason += "Certificate got revoked"; break; + case ClientError::RevocationCheckFailedError: reason += "Failed to do revokation check"; break; + case ClientError::SessionStartError: reason += "Error starting session"; break; + case ClientError::StreamError: reason += "Stream error"; break; + case ClientError::TLSError: reason += "Encryption error"; break; + case ClientError::ClientCertificateLoadError: reason += "Error loading certificate (Invalid password?)"; break; + case ClientError::ClientCertificateError: reason += "Certificate not authorized"; break; + case ClientError::UnknownCertificateError: reason += "Unknown certificate"; break; + case ClientError::CertificateCardRemoved: reason += "Certificate card removed"; break; + case ClientError::CertificateExpiredError: reason += "Certificate has expired"; break; + case ClientError::CertificateNotYetValidError: reason += "Certificate is not yet valid"; break; + case ClientError::CertificateSelfSignedError: reason += "Certificate is self-signed"; break; + case ClientError::CertificateRejectedError: reason += "Certificate has been rejected"; break; + case ClientError::CertificateUntrustedError: reason += "Certificate is not trusted"; break; + case ClientError::InvalidCertificatePurposeError: reason += "Certificate cannot be used for encrypting your connection"; break; + case ClientError::CertificatePathLengthExceededError: reason += "Certificate path length constraint exceeded"; break; + case ClientError::InvalidCertificateSignatureError: reason += "Invalid certificate signature"; break; + case ClientError::InvalidCAError: reason += "Invalid Certificate Authority"; break; + case ClientError::InvalidServerIdentityError: reason += "Certificate does not match the host identity"; break; + } + return reason; +} + +std::string Swift::getErrorString(const ComponentError& error) { + std::string reason = "Disconnected: "; + switch(error.getType()) { + case ComponentError::UnknownError: reason += "Unknown Error"; break; + case ComponentError::ConnectionError: reason += "Error connecting to server"; break; + case ComponentError::ConnectionReadError: reason += "Error while receiving server data"; break; + case ComponentError::ConnectionWriteError: reason += "Error while sending data to the server"; break; + case ComponentError::XMLError: reason += "Error parsing server data"; break; + case ComponentError::AuthenticationFailedError: reason += "Login/password invalid"; break; + case ComponentError::UnexpectedElementError: reason += "Unexpected response"; break; + } + return reason; +} + diff --git a/Sluift/Helpers.h b/Sluift/Helpers.h new file mode 100644 index 0000000..157e116 --- /dev/null +++ b/Sluift/Helpers.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2013-2014 Kevin Smith and 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 <Swiften/Base/API.h> + +#include <string> + +namespace Swift { + class ClientError; + class ComponentError; + + std::string getErrorString(const ClientError& error); + std::string getErrorString(const ComponentError& error); +} diff --git a/Sluift/ITunesInterface.h b/Sluift/ITunesInterface.h new file mode 100644 index 0000000..c3cccd2 --- /dev/null +++ b/Sluift/ITunesInterface.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#pragma once + +#include <Swiften/Base/API.h> + +#include <boost/shared_ptr.hpp> +#include <boost/optional/optional_fwd.hpp> + +namespace Swift { + class SWIFTEN_API ITunesInterface { + public: + struct Track { + std::string name; + std::string artist; + std::string album; + long trackNumber; + double duration; + long rating; + }; + + ITunesInterface(); + virtual ~ITunesInterface(); + + boost::optional<Track> getCurrentTrack() const; + + private: + bool haveApplication() const; + + private: + struct Private; + boost::shared_ptr<Private> p; + }; +} diff --git a/Sluift/ITunesInterface.mm b/Sluift/ITunesInterface.mm new file mode 100644 index 0000000..19c6253 --- /dev/null +++ b/Sluift/ITunesInterface.mm @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2014 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/ITunesInterface.h> + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wfour-char-constants" +#import <Sluift/iTunes.h> +#pragma clang diagnostic pop +#include <ScriptingBridge/ScriptingBridge.h> + +#include <boost/smart_ptr/make_shared.hpp> +#include <boost/optional.hpp> +#include <SwifTools/Cocoa/CocoaUtil.h> + +using namespace Swift; + +struct ITunesInterface::Private { + Private() : iTunes(nil) { + } + + iTunesApplication* iTunes; +}; + +ITunesInterface::ITunesInterface() : p(boost::make_shared<Private>()) { +} + +ITunesInterface::~ITunesInterface() { +} + +boost::optional<ITunesInterface::Track> ITunesInterface::getCurrentTrack() const { + if (!haveApplication()) { + return boost::optional<ITunesInterface::Track>(); + } + iTunesTrack* currentTrack = p->iTunes.currentTrack; + if (!currentTrack) { + return boost::optional<ITunesInterface::Track>(); + } + ITunesInterface::Track result; + result.name = NS2STDSTRING(currentTrack.name); + result.artist = NS2STDSTRING(currentTrack.artist); + result.album = NS2STDSTRING(currentTrack.album); + result.trackNumber = currentTrack.trackNumber; + result.duration = currentTrack.duration; + result.rating = currentTrack.rating; + return result; +} + + +bool ITunesInterface::haveApplication() const { + if (!p->iTunes) { + p->iTunes = [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"]; + } + return p->iTunes != nil && [p->iTunes isRunning]; +} diff --git a/Sluift/Lua/Check.cpp b/Sluift/Lua/Check.cpp new file mode 100644 index 0000000..a9b8f02 --- /dev/null +++ b/Sluift/Lua/Check.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2013-2014 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/Lua/Check.h> + +#include <boost/numeric/conversion/cast.hpp> +#include <iostream> +#include <sstream> +#include <lua.hpp> + +#include <Sluift/Lua/Exception.h> +#include <Swiften/Base/ByteArray.h> + +using namespace Swift; + + +static std::string getArgTypeError(lua_State* L, int arg, int tag) { + std::ostringstream s; + s << "Arg " << arg << ": expected " << lua_typename(L, tag) << ", got " << luaL_typename(L, arg); + return s.str(); +} + +void Lua::checkType(lua_State* L, int arg, int type) { + if (lua_type(L, arg) != type) { + throw Lua::Exception(getArgTypeError(L, arg, type)); + } +} + +int Lua::checkIntNumber(lua_State* L, int arg) { + if (!lua_isnumber(L, arg)) { + throw Lua::Exception(getArgTypeError(L, arg, LUA_TNUMBER)); + } + return boost::numeric_cast<int>(lua_tonumber(L, arg)); +} + +std::string Lua::checkString(lua_State* L, int arg) { + const char *s = lua_tolstring(L, arg, NULL); + if (!s) { + throw Lua::Exception(getArgTypeError(L, arg, LUA_TSTRING)); + } + return std::string(s); +} + +ByteArray Lua::checkByteArray(lua_State* L, int arg) { + size_t len; + const char *s = lua_tolstring(L, arg, &len); + if (!s) { + throw Lua::Exception(getArgTypeError(L, arg, LUA_TSTRING)); + } + return createByteArray(s, len); +} + +void* Lua::checkUserDataRaw(lua_State* L, int arg) { + void* userData = lua_touserdata(L, arg); + if (!userData) { + throw Lua::Exception(getArgTypeError(L, arg, LUA_TUSERDATA)); + } + if (!lua_getmetatable(L, arg)) { + throw Lua::Exception(getArgTypeError(L, arg, LUA_TUSERDATA)); + } + lua_pop(L, 1); + return userData; +} diff --git a/Sluift/Lua/Check.h b/Sluift/Lua/Check.h new file mode 100644 index 0000000..c22751b --- /dev/null +++ b/Sluift/Lua/Check.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013-2014 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#pragma once + +#include <string> + +#include <Swiften/Base/ByteArray.h> + +struct lua_State; + +namespace Swift { + namespace Lua { + void checkType(lua_State* L, int arg, int type); + int checkIntNumber(lua_State* L, int arg); + std::string checkString(lua_State* L, int arg); + ByteArray checkByteArray(lua_State* L, int arg); + + void* checkUserDataRaw(lua_State* L, int arg); + + template<typename T> + T** checkUserData(lua_State* L, int arg) { + return reinterpret_cast<T**>(checkUserDataRaw(L, arg)); + } + } +} diff --git a/Sluift/Lua/Debug.h b/Sluift/Lua/Debug.h new file mode 100644 index 0000000..8e86b89 --- /dev/null +++ b/Sluift/Lua/Debug.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 <lua.hpp> +#include <iostream> + +namespace Swift { + namespace Lua { + inline void dumpStack(lua_State *L) { + for (int i = 1; i <= lua_gettop(L); i++) { + int type = lua_type(L, i); + std::cout << i << ": [" << lua_typename(L, type) << "] "; + switch (type) { + case LUA_TSTRING: std::cout << lua_tostring(L, i); break; + case LUA_TNUMBER: std::cout << lua_tonumber(L, i); break; + case LUA_TBOOLEAN: std::cout << lua_toboolean(L, i); break; + default: break; + } + std::cout << std::endl; + } + } + } +} diff --git a/Sluift/Lua/Exception.cpp b/Sluift/Lua/Exception.cpp new file mode 100644 index 0000000..889c931 --- /dev/null +++ b/Sluift/Lua/Exception.cpp @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/Lua/Exception.h> + +using namespace Swift::Lua; + +Exception::Exception(const std::string& what) : std::runtime_error(what) { +} + +Exception::~Exception() SWIFTEN_NOEXCEPT { +} diff --git a/Sluift/Lua/Exception.h b/Sluift/Lua/Exception.h new file mode 100644 index 0000000..a68b925 --- /dev/null +++ b/Sluift/Lua/Exception.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2013-2014 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <stdexcept> +#include <Swiften/Base/API.h> + +namespace Swift { + namespace Lua { + class Exception : public std::runtime_error { + public: + Exception(const std::string& what); + SWIFTEN_DEFAULT_COPY_CONSTRUCTOR(Exception) + virtual ~Exception() SWIFTEN_NOEXCEPT; + }; + } +} + diff --git a/Sluift/Lua/FunctionRegistration.cpp b/Sluift/Lua/FunctionRegistration.cpp new file mode 100644 index 0000000..ddfa1f0 --- /dev/null +++ b/Sluift/Lua/FunctionRegistration.cpp @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/Lua/FunctionRegistration.h> + +using namespace Swift::Lua; + +FunctionRegistration::FunctionRegistration(const std::string& name, lua_CFunction function, const std::string& type, const std::string& helpDescription, const std::string& helpParameters, const std::string& helpOptions) { + FunctionRegistry::getInstance().addFunction(name, function, type, helpDescription, helpParameters, helpOptions); +} + +FunctionRegistration::~FunctionRegistration() { +} diff --git a/Sluift/Lua/FunctionRegistration.h b/Sluift/Lua/FunctionRegistration.h new file mode 100644 index 0000000..74269e2 --- /dev/null +++ b/Sluift/Lua/FunctionRegistration.h @@ -0,0 +1,43 @@ +/* + * 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 <Swiften/Base/API.h> +#include <Sluift/Lua/FunctionRegistry.h> +#include <lua.hpp> +#include <string> + +namespace Swift { + namespace Lua { + class FunctionRegistration { + public: + FunctionRegistration( + const std::string& name, lua_CFunction function, const std::string& type, + const std::string& helpDescription, const std::string& helpParameters, const std::string& helpOptions); + ~FunctionRegistration(); + }; + } +} + + +#define SLUIFT_LUA_FUNCTION_WITH_HELP(TYPE, NAME, HELP_DESCRIPTION, HELP_PARAMETERS, HELP_OPTIONS) \ + static int TYPE##_##NAME(lua_State* L); \ + static int TYPE##_##NAME##_wrapper(lua_State* L); \ + static ::Swift::Lua::FunctionRegistration TYPE##_##NAME##_registration( #NAME , TYPE##_##NAME##_wrapper, #TYPE, HELP_DESCRIPTION, HELP_PARAMETERS, HELP_OPTIONS); \ + static int TYPE##_##NAME##_wrapper(lua_State* L) { \ + try { \ + return TYPE ## _ ## NAME (L); \ + } \ + catch (const std::exception& e) { \ + return luaL_error(L, e.what()); \ + } \ + } \ + static int TYPE ## _ ## NAME (lua_State* L) + +#define SLUIFT_LUA_FUNCTION(TYPE, NAME) \ + SLUIFT_LUA_FUNCTION_WITH_HELP(TYPE, NAME, "", "", "") diff --git a/Sluift/Lua/FunctionRegistry.cpp b/Sluift/Lua/FunctionRegistry.cpp new file mode 100644 index 0000000..df24d9c --- /dev/null +++ b/Sluift/Lua/FunctionRegistry.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/Lua/FunctionRegistry.h> + +#include <Swiften/Base/foreach.h> +#include <Sluift/Lua/LuaUtils.h> +#include <Sluift/Lua/Exception.h> +#include <Sluift/globals.h> + +using namespace Swift::Lua; + +FunctionRegistry::FunctionRegistry() { +} + +FunctionRegistry::~FunctionRegistry() { +} + +FunctionRegistry& FunctionRegistry::getInstance() { + static FunctionRegistry instance; + return instance; +} + +void FunctionRegistry::addFunction( + const std::string& name, lua_CFunction function, const std::string& type, + const std::string& helpDescription, const std::string& helpParameters, const std::string& helpOptions) { + Registration registration; + registration.name = name; + registration.function = function; + registration.type = type; + registration.helpDescription = helpDescription; + registration.helpParameters = helpParameters; + registration.helpOptions = helpOptions; + registrations.push_back(registration); +} + +void FunctionRegistry::createFunctionTable(lua_State* L, const std::string& type) { + lua_newtable(L); + addFunctionsToTable(L, type); +} + +void FunctionRegistry::addFunctionsToTable(lua_State* L, const std::string& type) { + foreach(const Registration& registration, registrations) { + if (registration.type == type) { + lua_pushcclosure(L, registration.function, 0); + if (!registration.helpDescription.empty()) { + Lua::registerHelp(L, -1, registration.helpDescription, registration.helpParameters, registration.helpOptions); + } + else { + Lua::registerExtraHelp(L, -1, registration.type + "." + registration.name); + } + lua_setfield(L, -2, registration.name.c_str()); + } + } +} diff --git a/Sluift/Lua/FunctionRegistry.h b/Sluift/Lua/FunctionRegistry.h new file mode 100644 index 0000000..b20108d --- /dev/null +++ b/Sluift/Lua/FunctionRegistry.h @@ -0,0 +1,47 @@ +/* + * 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 <lua.hpp> +#include <string> +#include <vector> + +namespace Swift { + namespace Lua { + class FunctionRegistry { + public: + ~FunctionRegistry(); + static FunctionRegistry& getInstance(); + + void addFunction(const std::string& name, lua_CFunction function, const std::string& type, + const std::string& helpDescription, const std::string& helpParameters, const std::string& helpOptions); + + void createFunctionTable(lua_State* L, const std::string& type); + + /** + * Adds the functions to the table on the top of the stack. + */ + void addFunctionsToTable(lua_State* L, const std::string& type); + + private: + FunctionRegistry(); + + + private: + struct Registration { + std::string name; + lua_CFunction function; + std::string type; + std::string helpDescription; + std::string helpParameters; + std::string helpOptions; + }; + std::vector<Registration> registrations; + }; + } +} diff --git a/Sluift/Lua/LuaUtils.cpp b/Sluift/Lua/LuaUtils.cpp new file mode 100644 index 0000000..9ea5cba --- /dev/null +++ b/Sluift/Lua/LuaUtils.cpp @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/Lua/LuaUtils.h> + +#include <lua.hpp> + +#include <boost/scope_exit.hpp> +#include <Sluift/Lua/Exception.h> +#include <iostream> +#include <cassert> +#include <sstream> +#include <boost/numeric/conversion/cast.hpp> +#include <boost/algorithm/string/trim.hpp> +#include <Sluift/globals.h> + +using namespace Swift::Lua; + +static const std::string INDENT = " "; + +void Swift::Lua::registerTableToString(lua_State* L, int index) { + index = Lua::absoluteOffset(L, index); + lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.coreLibIndex); + lua_getfield(L, -1, "register_table_tostring"); + lua_pushvalue(L, index); + if (lua_pcall(L, 1, 0, 0) != 0) { + throw Lua::Exception(lua_tostring(L, -1)); + } + lua_pop(L, 1); +} + +void Swift::Lua::registerTableEquals(lua_State* L, int index) { + index = Lua::absoluteOffset(L, index); + lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.coreLibIndex); + lua_getfield(L, -1, "register_table_equals"); + lua_pushvalue(L, index); + if (lua_pcall(L, 1, 0, 0) != 0) { + throw Lua::Exception(lua_tostring(L, -1)); + } + lua_pop(L, 1); +} + +void Swift::Lua::registerGetByTypeIndex(lua_State* L, int index) { + index = Lua::absoluteOffset(L, index); + lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.coreLibIndex); + lua_getfield(L, -1, "register_get_by_type_index"); + lua_pushvalue(L, index); + if (lua_pcall(L, 1, 0, 0) != 0) { + throw Lua::Exception(lua_tostring(L, -1)); + } + lua_pop(L, 1); +} + +boost::optional<std::string> Swift::Lua::getStringField(lua_State* L, int index, const std::string& field) { + lua_getfield(L, index, field.c_str()); + // Seems to generate warnings with some versions of CLang that i can't turn off. + // Leaving the more elegant code here, hoping we can re-enable it later (newer boost? c++11?). + // The same applies to the other get*Field functions. + //BOOST_SCOPE_EXIT(&L) { lua_pop(L,1); } BOOST_SCOPE_EXIT_END + //return lua_isstring(L, -1) ? std::string(lua_tostring(L, -1)) : boost::optional<std::string>(); + + boost::optional<std::string> result; + if (lua_isstring(L, -1)) { + result = std::string(lua_tostring(L, -1)); + } + lua_pop(L, 1); + return result; +} + +boost::optional<bool> Swift::Lua::getBooleanField(lua_State* L, int index, const std::string& field) { + lua_getfield(L, index, field.c_str()); + boost::optional<bool> result; + if (lua_isboolean(L, -1)) { + result = lua_toboolean(L, -1); + } + lua_pop(L, 1); + return result; +} + +boost::optional<int> Swift::Lua::getIntField(lua_State* L, int index, const std::string& field) { + lua_getfield(L, index, field.c_str()); + boost::optional<int> result; + if (lua_isnumber(L, -1)) { + result = boost::numeric_cast<int>(lua_tonumber(L, -1)); + } + lua_pop(L, 1); + return result; +} + +void Swift::Lua::registerHelp(lua_State* L, int index, const std::string& description, const std::string& parameters, const std::string& options) { + index = Lua::absoluteOffset(L, index); + lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.coreLibIndex); + lua_getfield(L, -1, "register_help"); + lua_pushvalue(L, index); + + lua_newtable(L); + lua_pushstring(L, description.c_str()); + lua_rawseti(L, -2, 1); + + if (!parameters.empty()) { + std::istringstream s(parameters); + lua_newtable(L); + int i = 1; + for (std::string line; std::getline(s, line); ) { + std::string trimmedLine = boost::trim_copy(line); + if (trimmedLine.empty()) { + continue; + } + size_t splitIndex = trimmedLine.find_first_of(" \t"); + std::string key; + std::string value; + if (splitIndex == std::string::npos) { + key = trimmedLine; + } + else { + key = trimmedLine.substr(0, splitIndex); + value = boost::trim_copy(trimmedLine.substr(splitIndex+1)); + } + lua_createtable(L, 2, 0); + lua_pushstring(L, key.c_str()); + lua_rawseti(L, -2, 1); + lua_pushstring(L, value.c_str()); + lua_rawseti(L, -2, 2); + + lua_rawseti(L, -2, i++); + } + lua_setfield(L, -2, "parameters"); + } + if (!options.empty()) { + std::istringstream s(options); + lua_newtable(L); + for (std::string line; std::getline(s, line); ) { + std::string trimmedLine = boost::trim_copy(line); + if (trimmedLine.empty()) { + continue; + } + size_t splitIndex = trimmedLine.find_first_of(" \t"); + std::string key; + std::string value; + if (splitIndex == std::string::npos) { + key = trimmedLine; + } + else { + key = trimmedLine.substr(0, splitIndex); + value = boost::trim_copy(trimmedLine.substr(splitIndex+1)); + } + lua_pushstring(L, value.c_str()); + lua_setfield(L, -2, key.c_str()); + } + lua_setfield(L, -2, "options"); + } + + if (lua_pcall(L, 2, 0, 0) != 0) { + throw Lua::Exception(lua_tostring(L, -1)); + } + lua_pop(L, 1); +} + +void Swift::Lua::registerClassHelp(lua_State* L, const std::string& name, const std::string& description) { + lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.coreLibIndex); + lua_getfield(L, -1, "register_class_help"); + lua_pushstring(L, name.c_str()); + + lua_newtable(L); + lua_pushstring(L, description.c_str()); + lua_rawseti(L, -2, 1); + + if (lua_pcall(L, 2, 0, 0) != 0) { + throw Lua::Exception(lua_tostring(L, -1)); + } + lua_pop(L, 1); +} + +void Swift::Lua::registerExtraHelp(lua_State* L, int index, const std::string& name) { + index = Lua::absoluteOffset(L, index); + lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.coreLibIndex); + lua_getfield(L, -1, "extra_help"); + lua_getfield(L, -1, name.c_str()); + if (!lua_isnil(L, -1)) { + lua_getfield(L, -3, "register_help"); + lua_pushvalue(L, index); + lua_pushvalue(L, -3); + if (lua_pcall(L, 2, 0, 0) != 0) { + throw Lua::Exception(lua_tostring(L, -1)); + } + } + lua_pop(L, 3); +} + +void Swift::Lua::pushStringArray(lua_State* L, const std::vector<std::string>& strings) { + lua_createtable(L, boost::numeric_cast<int>(strings.size()), 0); + for (size_t i = 0; i < strings.size(); ++i) { + lua_pushstring(L, strings[i].c_str()); + lua_rawseti(L, -2, boost::numeric_cast<int>(i+1)); + } +} diff --git a/Sluift/Lua/LuaUtils.h b/Sluift/Lua/LuaUtils.h new file mode 100644 index 0000000..ed4fcc1 --- /dev/null +++ b/Sluift/Lua/LuaUtils.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#pragma once + +#include <lua.hpp> +#include <boost/optional.hpp> +#include <string> +#include <vector> + +#if LUA_VERSION_NUM < 502 +#define lua_pushglobaltable(L) lua_pushvalue(L, LUA_GLOBALSINDEX) +#endif + +namespace Swift { + namespace Lua { + /** + * Can be used as __tostring metamethod on a table. + */ + int convertTableToString(lua_State* L); + + void registerTableToString(lua_State* L, int index); + void registerTableEquals(lua_State* L, int index); + void registerGetByTypeIndex(lua_State* L, int index); + void registerHelp(lua_State* L, int index, + const std::string& description, const std::string& parameters, const std::string& options); + void registerClassHelp(lua_State* L, const std::string& name, const std::string& description); + void registerExtraHelp(lua_State* L, int index, const std::string& name); + + inline int absoluteOffset(lua_State* L, int index) { + return index > 0 ? index : lua_gettop(L) + index + 1; + } + + boost::optional<std::string> getStringField(lua_State* L, int index, const std::string&); + boost::optional<bool> getBooleanField(lua_State* L, int index, const std::string&); + boost::optional<int> getIntField(lua_State* L, int index, const std::string&); + + void pushStringArray(lua_State* L, const std::vector<std::string>& strings); + } +} diff --git a/Sluift/Lua/Value.cpp b/Sluift/Lua/Value.cpp index 3164ec6..4f8078e 100644 --- a/Sluift/Lua/Value.cpp +++ b/Sluift/Lua/Value.cpp @@ -7,6 +7,9 @@ #include "Value.h" +extern "C" { #include <lualib.h> +} #include <boost/variant/apply_visitor.hpp> +#include <boost/numeric/conversion/cast.hpp> #include <Swiften/Base/foreach.h> @@ -36,13 +39,13 @@ namespace { void operator()(const std::vector<Value>& values) const { - lua_createtable(state, values.size(), 0); + lua_createtable(state, boost::numeric_cast<int>(values.size()), 0); for(size_t i = 0; i < values.size(); ++i) { boost::apply_visitor(PushVisitor(state), values[i]); - lua_rawseti(state, -2, i + 1); + lua_rawseti(state, -2, boost::numeric_cast<int>(i + 1)); } } void operator()(const std::map<std::string, boost::shared_ptr<Value> >& table) const { - lua_createtable(state, 0, table.size()); + lua_createtable(state, 0, boost::numeric_cast<int>(table.size())); for(std::map<std::string, boost::shared_ptr<Value> >::const_iterator i = table.begin(); i != table.end(); ++i) { boost::apply_visitor(PushVisitor(state), *i->second); diff --git a/Sluift/Lua/Value.h b/Sluift/Lua/Value.h index 7b10cd2..55aa347 100644 --- a/Sluift/Lua/Value.h +++ b/Sluift/Lua/Value.h @@ -1,4 +1,4 @@ /* - * Copyright (c) 2011 Remko Tronçon + * Copyright (c) 2010-2013 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. @@ -12,4 +12,5 @@ #include <boost/variant.hpp> #include <boost/shared_ptr.hpp> +#include <boost/smart_ptr/make_shared.hpp> struct lua_State; @@ -29,4 +30,29 @@ namespace Swift { typedef std::map<std::string, boost::shared_ptr<Value> > Table; + typedef std::vector<Value> Array; + + inline boost::shared_ptr<Value> nilRef() { + return boost::make_shared<Value>(Nil()); + } + + inline boost::shared_ptr<Value> valueRef(const std::string& value) { + return boost::make_shared<Value>(value); + } + + inline boost::shared_ptr<Value> intRef(int value) { + return boost::make_shared<Value>(value); + } + + inline boost::shared_ptr<Value> boolRef(bool value) { + return boost::make_shared<Value>(value); + } + + inline boost::shared_ptr<Value> valueRef(const Table& table) { + return boost::make_shared<Value>(table); + } + + inline boost::shared_ptr<Value> valueRef(const Array& array) { + return boost::make_shared<Value>(array); + } void pushValue(lua_State* state, const Value& value); diff --git a/Sluift/LuaElementConvertor.cpp b/Sluift/LuaElementConvertor.cpp new file mode 100644 index 0000000..c1d0720 --- /dev/null +++ b/Sluift/LuaElementConvertor.cpp @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/LuaElementConvertor.h> + +using namespace Swift; + +LuaElementConvertor::~LuaElementConvertor() { +} + +boost::optional<std::string> LuaElementConvertor::NO_RESULT; diff --git a/Sluift/LuaElementConvertor.h b/Sluift/LuaElementConvertor.h new file mode 100644 index 0000000..5e55add --- /dev/null +++ b/Sluift/LuaElementConvertor.h @@ -0,0 +1,40 @@ +/* + * 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 <string> +#include <boost/shared_ptr.hpp> +#include <boost/optional.hpp> + +struct lua_State; + +namespace Swift { + class Element; + + class LuaElementConvertor { + public: + static boost::optional<std::string> NO_RESULT; + + struct Documentation { + Documentation(const std::string& className, const std::string& description) : + className(className), description(description) {} + std::string className; + std::string description; + }; + + virtual ~LuaElementConvertor(); + + virtual boost::shared_ptr<Element> convertFromLua(lua_State*, int index, const std::string& type) = 0; + virtual boost::optional<std::string> convertToLua(lua_State*, boost::shared_ptr<Element>) = 0; + + virtual boost::optional<Documentation> getDocumentation() const { + return boost::optional<Documentation>(); + } + }; +} diff --git a/Sluift/LuaElementConvertors.cpp b/Sluift/LuaElementConvertors.cpp new file mode 100644 index 0000000..253a820 --- /dev/null +++ b/Sluift/LuaElementConvertors.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2013-2014 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/LuaElementConvertors.h> + +#include <boost/smart_ptr/make_shared.hpp> + +#include <Swiften/Base/foreach.h> +#include <Sluift/LuaElementConvertor.h> +#include <Sluift/ElementConvertors/RawXMLElementConvertor.h> +#include <Sluift/ElementConvertors/PubSubEventConvertor.h> +#include <Sluift/ElementConvertors/DOMElementConvertor.h> +#include <Sluift/ElementConvertors/DefaultElementConvertor.h> +#include <Sluift/ElementConvertors/DiscoItemsConvertor.h> +#include <Sluift/ElementConvertors/DiscoInfoConvertor.h> +#include <Sluift/ElementConvertors/FormConvertor.h> +#include <Sluift/ElementConvertors/SoftwareVersionConvertor.h> +#include <Sluift/ElementConvertors/VCardUpdateConvertor.h> +#include <Sluift/ElementConvertors/VCardConvertor.h> +#include <Sluift/ElementConvertors/BodyConvertor.h> +#include <Sluift/ElementConvertors/SubjectConvertor.h> +#include <Sluift/ElementConvertors/CommandConvertor.h> +#include <Sluift/ElementConvertors/StatusShowConvertor.h> +#include <Sluift/ElementConvertors/StatusConvertor.h> +#include <Sluift/ElementConvertors/DelayConvertor.h> +#include <Sluift/ElementConvertors/IQConvertor.h> +#include <Sluift/ElementConvertors/PresenceConvertor.h> +#include <Sluift/ElementConvertors/MessageConvertor.h> +#include <Sluift/ElementConvertors/ResultSetConvertor.h> +#include <Sluift/ElementConvertors/ForwardedConvertor.h> +#include <Sluift/ElementConvertors/MAMResultConvertor.h> +#include <Sluift/ElementConvertors/MAMQueryConvertor.h> +#include <Sluift/ElementConvertors/MAMArchivedConvertor.h> +#include <Sluift/Lua/LuaUtils.h> +#include <Sluift/Lua/Exception.h> + +using namespace Swift; + +LuaElementConvertors::LuaElementConvertors() { + registerConvertors(); + convertors.push_back(boost::make_shared<StatusConvertor>()); + convertors.push_back(boost::make_shared<StatusShowConvertor>()); + convertors.push_back(boost::make_shared<DelayConvertor>()); + 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<SubjectConvertor>()); + convertors.push_back(boost::make_shared<VCardConvertor>()); + convertors.push_back(boost::make_shared<VCardUpdateConvertor>()); + convertors.push_back(boost::make_shared<FormConvertor>()); + convertors.push_back(boost::make_shared<SoftwareVersionConvertor>()); + convertors.push_back(boost::make_shared<DiscoInfoConvertor>()); + convertors.push_back(boost::make_shared<DiscoItemsConvertor>()); + convertors.push_back(boost::make_shared<IQConvertor>(this)); + convertors.push_back(boost::make_shared<PresenceConvertor>(this)); + convertors.push_back(boost::make_shared<MessageConvertor>(this)); + convertors.push_back(boost::make_shared<ResultSetConvertor>(this)); + convertors.push_back(boost::make_shared<ForwardedConvertor>(this)); + convertors.push_back(boost::make_shared<MAMResultConvertor>(this)); + convertors.push_back(boost::make_shared<MAMQueryConvertor>(this)); + convertors.push_back(boost::make_shared<MAMArchivedConvertor>(this)); + convertors.push_back(boost::make_shared<DOMElementConvertor>()); + convertors.push_back(boost::make_shared<RawXMLElementConvertor>()); + convertors.push_back(boost::make_shared<DefaultElementConvertor>()); +} + +LuaElementConvertors::~LuaElementConvertors() { +} + +#include <Sluift/ElementConvertors/ElementConvertors.ipp> + +boost::shared_ptr<Element> LuaElementConvertors::convertFromLua(lua_State* L, int index) { + if (lua_isstring(L, index)) { + return convertFromLuaUntyped(L, index, "xml"); + } + else if (lua_istable(L, index)) { + lua_getfield(L, index, "_type"); + if (lua_isstring(L, -1)) { + std::string type = lua_tostring(L, -1); + lua_pop(L, 1); + return convertFromLuaUntyped(L, index, type); + } + lua_pop(L, 1); + } + throw Lua::Exception("Unable to determine type"); +} + +boost::shared_ptr<Element> LuaElementConvertors::convertFromLuaUntyped(lua_State* L, int index, const std::string& type) { + index = Lua::absoluteOffset(L, index); + foreach (boost::shared_ptr<LuaElementConvertor> convertor, convertors) { + if (boost::shared_ptr<Element> result = convertor->convertFromLua(L, index, type)) { + return result; + } + } + return boost::shared_ptr<Element>(); +} + + +int LuaElementConvertors::convertToLua(lua_State* L, boost::shared_ptr<Element> payload) { + if (boost::optional<std::string> type = doConvertToLuaUntyped(L, payload)) { + if (lua_istable(L, -1)) { + lua_pushstring(L, type->c_str()); + lua_setfield(L, -2, "_type"); + Lua::registerTableToString(L, -1); + } + else { + assert(*type == "xml"); + } + return 1; + } + return 0; +} + +int LuaElementConvertors::convertToLuaUntyped(lua_State* L, boost::shared_ptr<Element> payload) { + if (doConvertToLuaUntyped(L, payload)) { + return 1; + } + return 0; +} + +boost::optional<std::string> LuaElementConvertors::doConvertToLuaUntyped( + lua_State* L, boost::shared_ptr<Element> payload) { + if (!payload) { + return LuaElementConvertor::NO_RESULT; + } + foreach (boost::shared_ptr<LuaElementConvertor> convertor, convertors) { + if (boost::optional<std::string> type = convertor->convertToLua(L, payload)) { + return *type; + } + } + return LuaElementConvertor::NO_RESULT; +} + diff --git a/Sluift/LuaElementConvertors.h b/Sluift/LuaElementConvertors.h new file mode 100644 index 0000000..d2e2a43 --- /dev/null +++ b/Sluift/LuaElementConvertors.h @@ -0,0 +1,51 @@ +/* + * 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 <vector> +#include <boost/shared_ptr.hpp> +#include <boost/optional.hpp> + +struct lua_State; + +namespace Swift { + class LuaElementConvertor; + class Element; + + class LuaElementConvertors { + public: + LuaElementConvertors(); + virtual ~LuaElementConvertors(); + + boost::shared_ptr<Element> convertFromLua(lua_State*, int index); + int convertToLua(lua_State*, boost::shared_ptr<Element>); + + /** + * Adds a toplevel type+data table with the given type. + */ + boost::shared_ptr<Element> convertFromLuaUntyped(lua_State*, int index, const std::string& type); + + /** + * Strips the toplevel type+data table, and only return the + * data. + */ + int convertToLuaUntyped(lua_State*, boost::shared_ptr<Element>); + + const std::vector< boost::shared_ptr<LuaElementConvertor> >& getConvertors() const { + return convertors; + } + + private: + boost::optional<std::string> doConvertToLuaUntyped(lua_State*, boost::shared_ptr<Element>); + void registerConvertors(); + + private: + std::vector< boost::shared_ptr<LuaElementConvertor> > convertors; + }; +} diff --git a/Sluift/README b/Sluift/README deleted file mode 100644 index 65fb648..0000000 --- a/Sluift/README +++ /dev/null @@ -1,2 +0,0 @@ -For example scripts, see the 'login.lua' script and the scripts in -Swiften/QA/ScriptedTests. diff --git a/Sluift/README.md b/Sluift/README.md new file mode 100644 index 0000000..ae43846 --- /dev/null +++ b/Sluift/README.md @@ -0,0 +1,52 @@ +# Sluift + +## Building/Installing + +If you have a Lua installed on your system, edit `config.py` to point the build +to it using the `lua_libdir`, `lua_includedir`, and `lua_libname` (usually +only needed on Windows) variables. + +In case no Lua installation is found, a bundled Lua version will be used. +Note that there are several limitations when using the bundled Lua: + +- The standalone Sluift binary will not support dynamic loading of modules. +- The Sluift module will not be built + +The standalone executable will be placed into `Sluift/exe`, and the loadable +Lua module in `Sluift/dll`. + +In order to use the Sluift Lua module, make sure it is copied to a location in the +Lua search path (`LUA_PATH`, or the built-in path). On Linux and Mac OS X, this is +typically `<LUA_INSTALLPREFIX>/lib/lua/<LUA_VERSION>/`. If `require 'sluift'` fails, +the error message will give an indication what this path is. + + +## Examples + +Example scripts can be found in `Examples/`, and in the Swift tree under +`Swiften/QA/ScriptedTests`. + +## Extending + +### Adding support for payloads + +To add support for a specific Swiften payload, create a new `LuaElementConvertor` +in `ElementConvertors/` by inheriting from `GenericLuaElementConvertor`, and register +it it `LuaElementConvertors.cpp`. The name of the convertor passed in the constructor +of `GenericLuaElementConvertor` should correspond to the snake case version of the +Swiften element it converts. For examples, see the existing convertors in +`ElementConvertors/`. + +When the convertor is registered, you can use it in message bodies, or in get/set +queries. For example, for the `command` convertor: + + client:set{to = 'alice@wonderland.lit', query = {_type = 'command', + type = 'execute', node = 'uptime'}} + +Optionally, you can add a convenience `set_<type>` shortcut on the client object by +adding it to the list of `get_set_shortcuts` in `boot.lua`. With such a shortcut, the +command above can be rewritten as: + + client:set_command{to = 'alice@wonderland.lit', command = { + type = 'execute', node = 'uptime'}} + diff --git a/Sluift/Response.cpp b/Sluift/Response.cpp new file mode 100644 index 0000000..5c36a8e --- /dev/null +++ b/Sluift/Response.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/Response.h> + +#include <lua.hpp> + +#include <Sluift/globals.h> +#include <Swiften/Elements/ErrorPayload.h> +#include <Sluift/Lua/LuaUtils.h> + +using namespace Swift; +using namespace Swift::Sluift; + +static std::string getErrorString(boost::shared_ptr<ErrorPayload> error) { + // Copied from ChatControllerBase. + // TODO: Share this code; + std::string defaultMessage = "Error sending message"; + if (!error->getText().empty()) { + return error->getText(); + } + else { + switch (error->getCondition()) { + case ErrorPayload::BadRequest: return "Bad request"; + case ErrorPayload::Conflict: return "Conflict"; + case ErrorPayload::FeatureNotImplemented: return "This feature is not implemented"; + case ErrorPayload::Forbidden: return "Forbidden"; + case ErrorPayload::Gone: return "Recipient can no longer be contacted"; + case ErrorPayload::InternalServerError: return "Internal server error"; + case ErrorPayload::ItemNotFound: return "Item not found"; + case ErrorPayload::JIDMalformed: return "JID Malformed"; + case ErrorPayload::NotAcceptable: return "Message was rejected"; + case ErrorPayload::NotAllowed: return "Not allowed"; + case ErrorPayload::NotAuthorized: return "Not authorized"; + case ErrorPayload::PaymentRequired: return "Payment is required"; + case ErrorPayload::RecipientUnavailable: return "Recipient is unavailable"; + case ErrorPayload::Redirect: return "Redirect"; + case ErrorPayload::RegistrationRequired: return "Registration required"; + case ErrorPayload::RemoteServerNotFound: return "Recipient's server not found"; + case ErrorPayload::RemoteServerTimeout: return "Remote server timeout"; + case ErrorPayload::ResourceConstraint: return "The server is low on resources"; + case ErrorPayload::ServiceUnavailable: return "The service is unavailable"; + case ErrorPayload::SubscriptionRequired: return "A subscription is required"; + case ErrorPayload::UndefinedCondition: return "Undefined condition"; + case ErrorPayload::UnexpectedRequest: return "Unexpected request"; + } + } + assert(false); + return defaultMessage; +} + +Response::~Response() { +} + +int Response::convertToLuaResult(lua_State* L) { + if (error) { + lua_pushnil(L); + lua_pushstring(L, getErrorString(error).c_str()); + bool converted = Sluift::globals.elementConvertor.convertToLuaUntyped(L, error); + assert(converted); + Lua::registerTableToString(L, -1); + return 3; + } + else { + if (result) { + bool converted = Sluift::globals.elementConvertor.convertToLuaUntyped(L, result); + assert(converted); + Lua::registerTableToString(L, -1); + } + else { + lua_pushboolean(L, 1); + } + return 1; + } + +} + diff --git a/Sluift/Response.h b/Sluift/Response.h new file mode 100644 index 0000000..b007897 --- /dev/null +++ b/Sluift/Response.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#pragma once + +#include <Swiften/Elements/Payload.h> +#include <Swiften/Elements/ErrorPayload.h> +#include <Swiften/Base/API.h> + +struct lua_State; + +namespace Swift { + namespace Sluift { + struct Response { + Response(boost::shared_ptr<Payload> result, boost::shared_ptr<ErrorPayload> error) : result(result), error(error) {} + SWIFTEN_DEFAULT_COPY_CONSTRUCTOR(Response) + ~Response(); + + static Response withResult(boost::shared_ptr<Payload> response) { + return Response(response, boost::shared_ptr<ErrorPayload>()); + } + + static Response withError(boost::shared_ptr<ErrorPayload> error) { + return Response(boost::shared_ptr<Payload>(), error); + } + + int convertToLuaResult(lua_State* L); + + boost::shared_ptr<Payload> result; + boost::shared_ptr<ErrorPayload> error; + }; + } +} diff --git a/Sluift/ResponseSink.h b/Sluift/ResponseSink.h deleted file mode 100644 index 700363a..0000000 --- a/Sluift/ResponseSink.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2011 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. - */ - -#pragma once - -#include <boost/shared_ptr.hpp> - -#include <Swiften/Elements/ErrorPayload.h> - -namespace Swift { - template<typename T> - class ResponseSink { - public: - ResponseSink() : responseReceived(false) { - } - - bool hasResponse() const { - return responseReceived; - } - - boost::shared_ptr<T> getResponsePayload() const { - return payload; - } - - ErrorPayload::ref getResponseError() const { - return error; - } - - void operator()(boost::shared_ptr<T> payload, ErrorPayload::ref error) { - this->payload = payload; - this->error = error; - this->responseReceived = true; - } - - void operator()(ErrorPayload::ref error) { - this->error = error; - this->responseReceived = true; - } - - private: - bool responseReceived; - boost::shared_ptr<T> payload; - ErrorPayload::ref error; - }; -} diff --git a/Sluift/SConscript b/Sluift/SConscript index f6388a0..a2b6748 100644 --- a/Sluift/SConscript +++ b/Sluift/SConscript @@ -1,64 +1,88 @@ import Version, os.path -Import(["env", "conf_env"]) +Import(["env"]) -if env["SCONS_STAGE"] == "build" : - lib_env = env.Clone() - lib_env.UseFlags(env["LUA_FLAGS"]) - lib_env.UseFlags(env["SWIFTEN_FLAGS"]) - lib_env.UseFlags(env["SWIFTEN_DEP_FLAGS"]) - sluift_lib = lib_env.StaticLibrary("SluiftCore", [ +if env["SCONS_STAGE"] == "build" and not GetOption("help") and not env.get("HAVE_LUA", 0) : + print "Warning: Lua was not found. Sluift will not be built." + if "Sluift" in env["PROJECTS"] : + env["PROJECTS"].remove("Sluift") + +elif env["SCONS_STAGE"] == "build" : + sluift_sources = [ "Lua/Value.cpp", + "Lua/Exception.cpp", + "Lua/Check.cpp", + "Lua/FunctionRegistration.cpp", + "Lua/FunctionRegistry.cpp", + "Lua/LuaUtils.cpp", + "LuaElementConvertors.cpp", + "LuaElementConvertor.cpp", + "Response.cpp", + "ElementConvertors/BodyConvertor.cpp", + "ElementConvertors/VCardUpdateConvertor.cpp", + "ElementConvertors/PubSubEventConvertor.cpp", + "ElementConvertors/RawXMLElementConvertor.cpp", + "ElementConvertors/DOMElementConvertor.cpp", + "ElementConvertors/DefaultElementConvertor.cpp", + "ElementConvertors/DiscoInfoConvertor.cpp", + "ElementConvertors/DiscoItemsConvertor.cpp", + "ElementConvertors/FormConvertor.cpp", + "ElementConvertors/SoftwareVersionConvertor.cpp", + "ElementConvertors/VCardConvertor.cpp", + "ElementConvertors/CommandConvertor.cpp", + "ElementConvertors/StatusConvertor.cpp", + "ElementConvertors/StatusShowConvertor.cpp", + "ElementConvertors/DelayConvertor.cpp", + "Helpers.cpp", + "SluiftClient.cpp", + "SluiftComponent.cpp", + "Watchdog.cpp", + "core.c", + "client.cpp", + "component.cpp", "sluift.cpp" - ]); + ] + sluift_sources += env.SConscript("ElementConvertors/SConscript") + + - myenv = env.Clone() - myenv.Append(LIBS = sluift_lib) - myenv.UseFlags(env.get("LUA_FLAGS", {})) - myenv.UseFlags(env["SWIFTEN_FLAGS"]) - myenv.UseFlags(env["SWIFTEN_DEP_FLAGS"]) - myenv["SHLIBPREFIX"] = "" - if myenv["PLATFORM"] == "win32" : - myenv.Append(CPPDEFINES = ["SLUIFT_BUILD_DLL"]) - elif myenv["PLATFORM"] == "darwin" : - myenv["SHLIBSUFFIX"] = ".so" - if env["PLATFORM"] == "win32" : - myenv.Append(CFLAGS = ["/TP"]) - else : - myenv.Append(CFLAGS = ["-x", "c++"]) + sluift_env = env.Clone() + sluift_env.UseFlags(env.get("LUA_FLAGS", {})) + sluift_env.UseFlags(env["SWIFTEN_FLAGS"]) + sluift_env.UseFlags(env["SWIFTEN_DEP_FLAGS"]) + # Support compilation on both Lua 5.1 and Lua 5.2 + sluift_env.Append(CPPDEFINES = ["LUA_COMPAT_ALL"]) + if sluift_env["PLATFORM"] == "win32" : + sluift_env.Append(CPPDEFINES = ["SLUIFT_BUILD_DLL"]) - myenv["SLUIFT_VERSION"] = Version.getBuildVersion(env.Dir("#").abspath, "sluift") - def patchLua(env, target, source) : + if sluift_env["PLATFORM"] == "darwin" and os.path.isdir("/Applications/iTunes.app") : + sluift_env.Append(FRAMEWORKS = ["ScriptingBridge"]) + sluift_env.Command("iTunes.h", "/Applications/iTunes.app", + "sdef ${SOURCE} | sdp -fh --basename iTunes -o ${TARGET.dir}") + sluift_env.Append(CPPDEFINES = ["HAVE_ITUNES"]) + sluift_sources += ["ITunesInterface.mm"] + + # Generate Version.h + version_header = "#pragma once\n\n" + version_header += "#define SLUIFT_VERSION_STRING \"" + Version.getBuildVersion(env.Dir("#").abspath, "sluift") + "\"\n" + sluift_env.WriteVal("Version.h", sluift_env.Value(version_header)) + + # Generate core.c + def generate_embedded_lua(env, target, source) : f = open(source[0].abspath, "r") - contents = f.read() + data = f.read() f.close() - if env["PLATFORM"] == "win32" : - key = "Z" - else : - key = "D" - contents = contents.replace("LUA_RELEASE", "\"== Sluift XMPP Console (%(version)s) == \\nPress Ctrl-%(key)s to exit\"" % {"version": source[1].get_contents(), "key" : key}) - contents = contents.replace("LUA_COPYRIGHT", "") f = open(target[0].abspath, "w") - f.write(contents) + f.write('#include <stddef.h>\n') + f.write('const size_t ' + source[0].name.replace(".", "_") + "_size = " + str(len(data)) + ";\n") + f.write('const char ' + source[0].name.replace(".", "_") + "[] = {" + ", ".join([str(ord(c)) for c in data]) + "};\n") f.close() + sluift_env.Command("core.c", ["core.lua"], env.Action(generate_embedded_lua, cmdstr="$GENCOMSTR")) - myenv.Command("lua.c", ["#/3rdParty/Lua/src/lua.c", myenv.Value(myenv["SLUIFT_VERSION"])], env.Action(patchLua, cmdstr = "$GENCOMSTR")) - if myenv.get("HAVE_READLINE", False) : - myenv.Append(CPPDEFINES = ["LUA_USE_READLINE"]) - myenv.MergeFlags(myenv["READLINE_FLAGS"]) - env["SLUIFT"] = myenv.Program("sluift", [ - "lua.c", - "linit.c", - ]) + sluift_env.WriteVal("dll.c", sluift_env.Value("")) - myenv.WriteVal("dll.c", myenv.Value("")) - myenv.SharedLibrary("sluift", ["dll.c"]) - - if env["PLATFORM"] == "win32" : - ssl_libs = [] - if myenv.get("OPENSSL_DIR", False) : - ssl_libs = [ - os.path.join(env["OPENSSL_DIR"], "bin", "ssleay32.dll"), - os.path.join(env["OPENSSL_DIR"], "bin", "libeay32.dll") - ] - myenv.WindowsBundle("Sluift", resources = {"": ssl_libs}) + sluift_sources = [env.File(x) for x in sluift_sources] + for sluift_variant in ['dll', 'exe'] : + SConscript(["SConscript.variant"], variant_dir = sluift_variant, + duplicate = 0, + exports = ['sluift_sources', 'sluift_variant', 'sluift_env']) diff --git a/Sluift/SConscript.variant b/Sluift/SConscript.variant new file mode 100644 index 0000000..d0e2b18 --- /dev/null +++ b/Sluift/SConscript.variant @@ -0,0 +1,42 @@ +import os + +Import('env') +Import('sluift_env') +Import('sluift_variant') +Import('sluift_sources') + +if sluift_variant == 'exe' : + common_objects = sluift_env.StaticObject(sluift_sources) + + sluift_exe_env = sluift_env.Clone() + tokenize = sluift_exe_env.StaticObject("#/Sluift/tokenize.cpp") + exe_sources = tokenize + [ + "#/Sluift/Console.cpp", + "#/Sluift/Terminal.cpp", + "#/Sluift/StandardTerminal.cpp", + "#/Sluift/Completer.cpp", + "#/Sluift/main.cpp", + ] + + if sluift_exe_env.get("HAVE_EDITLINE", False) : + sluift_exe_env.Append(CPPDEFINES = ["HAVE_EDITLINE"]) + sluift_exe_env.MergeFlags(sluift_exe_env["EDITLINE_FLAGS"]) + exe_sources += ["#/Sluift/EditlineTerminal.cpp"] + + env["SLUIFT"] = sluift_exe_env.Program("sluift", common_objects + exe_sources) + if sluift_exe_env.get("SLUIFT_INSTALLDIR", "") : + sluift_exe_env.Install(os.path.join(sluift_exe_env["SLUIFT_INSTALLDIR"], "bin"), env["SLUIFT"]) + + # Unit tests + env.Append(UNITTEST_OBJECTS = tokenize + ["#/Sluift/UnitTest/TokenizeTest.cpp"]) +else : + sluift_env["SLUIFT_DLL_SUFFIX"] = "${SHLIBSUFFIX}" + if sluift_env["PLATFORM"] == "darwin" : + sluift_env["SLUIFT_DLL_SUFFIX"] = ".so" + sluift_dll = sluift_env.SharedLibrary(sluift_env.File("sluift${SLUIFT_DLL_SUFFIX}"), + sluift_sources + ["#/Sluift/dll.c"]) + if sluift_env.get("SLUIFT_INSTALLDIR", False) and sluift_env.get("LUA_VERSION", False) : + sluift_env.Install( + os.path.join(sluift_env["SLUIFT_INSTALLDIR"], "lib", "lua", sluift_env["LUA_VERSION"]), + sluift_dll) + diff --git a/Sluift/SluiftClient.cpp b/Sluift/SluiftClient.cpp new file mode 100644 index 0000000..69472b8 --- /dev/null +++ b/Sluift/SluiftClient.cpp @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2013-2014 Kevin Smith and Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/SluiftClient.h> + +#include <boost/numeric/conversion/cast.hpp> + +#include <Swiften/Client/ClientXMLTracer.h> +#include <Swiften/Client/Client.h> +#include <Swiften/Roster/XMPPRoster.h> +#include <Sluift/SluiftGlobals.h> +#include <Sluift/Lua/Exception.h> +#include <Swiften/Elements/Message.h> +#include <Swiften/Elements/PubSubEvent.h> +#include <Swiften/Queries/RawRequest.h> +#include <Sluift/Helpers.h> +#include <Swiften/Elements/Presence.h> + +using namespace Swift; + +SluiftClient::SluiftClient( + const JID& jid, + const std::string& password, + NetworkFactories* networkFactories, + SimpleEventLoop* eventLoop) : + networkFactories(networkFactories), + eventLoop(eventLoop), + tracer(NULL) { + client = new Client(jid, password, networkFactories); + client->setAlwaysTrustCertificates(); + client->onDisconnected.connect(boost::bind(&SluiftClient::handleDisconnected, this, _1)); + client->onMessageReceived.connect(boost::bind(&SluiftClient::handleIncomingMessage, this, _1)); + client->onPresenceReceived.connect(boost::bind(&SluiftClient::handleIncomingPresence, this, _1)); + client->getPubSubManager()->onEvent.connect(boost::bind(&SluiftClient::handleIncomingPubSubEvent, this, _1, _2)); + client->getRoster()->onInitialRosterPopulated.connect(boost::bind(&SluiftClient::handleInitialRosterPopulated, this)); +} + +SluiftClient::~SluiftClient() { + delete tracer; + delete client; +} + +void SluiftClient::connect() { + rosterReceived = false; + disconnectedError = boost::optional<ClientError>(); + client->connect(options); +} + +void SluiftClient::connect(const std::string& host, int port) { + rosterReceived = false; + options.manualHostname = host; + options.manualPort = port; + disconnectedError = boost::optional<ClientError>(); + client->connect(options); +} + +void SluiftClient::setTraceEnabled(bool b) { + if (b && !tracer) { + tracer = new ClientXMLTracer(client, options.boshURL.isEmpty()? false: true); + } + else if (!b && tracer) { + delete tracer; + tracer = NULL; + } +} + +void SluiftClient::waitConnected(int timeout) { + Watchdog watchdog(timeout, networkFactories->getTimerFactory()); + while (!watchdog.getTimedOut() && client->isActive() && !client->isAvailable()) { + eventLoop->runUntilEvents(); + } + if (watchdog.getTimedOut()) { + client->disconnect(); + throw Lua::Exception("Timeout while connecting"); + } + if (disconnectedError) { + throw Lua::Exception(getErrorString(*disconnectedError)); + } +} + +bool SluiftClient::isConnected() const { + return client->isAvailable(); +} + +void SluiftClient::disconnect() { + client->disconnect(); + while (client->isActive()) { + eventLoop->runUntilEvents(); + } +} + +void SluiftClient::setSoftwareVersion(const std::string& name, const std::string& version, const std::string& os) { + client->setSoftwareVersion(name, version, os); +} + +boost::optional<SluiftClient::Event> SluiftClient::getNextEvent( + int timeout, boost::function<bool (const Event&)> condition) { + Watchdog watchdog(timeout, networkFactories->getTimerFactory()); + size_t currentIndex = 0; + while (true) { + // Look for pending events in the queue + while (currentIndex < pendingEvents.size()) { + Event event = pendingEvents[currentIndex]; + if (!condition || condition(event)) { + pendingEvents.erase( + pendingEvents.begin() + + boost::numeric_cast<int>(currentIndex)); + return event; + } + ++currentIndex; + } + + // Wait for new events + while (!watchdog.getTimedOut() && currentIndex >= pendingEvents.size() && client->isActive()) { + eventLoop->runUntilEvents(); + } + + // Finish if we're disconnected or timed out + if (watchdog.getTimedOut() || !client->isActive()) { + return boost::optional<Event>(); + } + } +} + +std::vector<XMPPRosterItem> SluiftClient::getRoster() { + if (!rosterReceived) { + // If we haven't requested it yet, request it for the first time + client->requestRoster(); + } + while (!rosterReceived) { + eventLoop->runUntilEvents(); + } + return client->getRoster()->getItems(); +} + +void SluiftClient::handleIncomingMessage(boost::shared_ptr<Message> stanza) { + if (stanza->getPayload<PubSubEvent>()) { + // Already handled by pubsub manager + return; + } + pendingEvents.push_back(Event(stanza)); +} + +void SluiftClient::handleIncomingPresence(boost::shared_ptr<Presence> stanza) { + pendingEvents.push_back(Event(stanza)); +} + +void SluiftClient::handleIncomingPubSubEvent(const JID& from, boost::shared_ptr<PubSubEventPayload> event) { + pendingEvents.push_back(Event(from, event)); +} + +void SluiftClient::handleInitialRosterPopulated() { + rosterReceived = true; +} + +void SluiftClient::handleRequestResponse(boost::shared_ptr<Payload> response, boost::shared_ptr<ErrorPayload> error) { + requestResponse = response; + requestError = error; + requestResponseReceived = true; +} + +void SluiftClient::handleDisconnected(const boost::optional<ClientError>& error) { + disconnectedError = error; +} + +Sluift::Response SluiftClient::doSendRequest(boost::shared_ptr<Request> request, int timeout) { + requestResponse.reset(); + requestError.reset(); + requestResponseReceived = false; + request->send(); + + Watchdog watchdog(timeout, networkFactories->getTimerFactory()); + while (!watchdog.getTimedOut() && !requestResponseReceived) { + eventLoop->runUntilEvents(); + } + return Sluift::Response(requestResponse, watchdog.getTimedOut() ? + boost::make_shared<ErrorPayload>(ErrorPayload::RemoteServerTimeout) : requestError); +} diff --git a/Sluift/SluiftClient.h b/Sluift/SluiftClient.h new file mode 100644 index 0000000..f0192e4 --- /dev/null +++ b/Sluift/SluiftClient.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#pragma once + +#include <deque> +#include <boost/optional.hpp> +#include <boost/bind.hpp> +#include <boost/function.hpp> + +#include <Swiften/Client/ClientOptions.h> +#include <Swiften/Elements/IQ.h> +#include <Swiften/Elements/Message.h> +#include <Swiften/Elements/Presence.h> +#include <Swiften/Queries/GenericRequest.h> +#include <Swiften/Roster/XMPPRosterItem.h> +#include <Swiften/Client/ClientError.h> +#include <Swiften/Network/NetworkFactories.h> +#include <Swiften/Client/Client.h> +#include <Swiften/EventLoop/SimpleEventLoop.h> +#include <Sluift/Watchdog.h> +#include <Swiften/PubSub/PubSubManager.h> +#include <Sluift/Response.h> + +namespace Swift { + struct SluiftGlobals; + class ClientXMLTracer; + class Client; + class Stanza; + class Payload; + class ErrorPayload; + class JID; + + class SluiftClient { + public: + struct Event { + enum Type { + MessageType, + PresenceType, + PubSubEventType + }; + + Event(boost::shared_ptr<Message> stanza) : type(MessageType), stanza(stanza) {} + Event(boost::shared_ptr<Presence> stanza) : type(PresenceType), stanza(stanza) {} + Event(const JID& from, boost::shared_ptr<PubSubEventPayload> payload) : type(PubSubEventType), from(from), pubsubEvent(payload) {} + + Type type; + + // Message & Presence + boost::shared_ptr<Stanza> stanza; + + // PubSubEvent + JID from; + boost::shared_ptr<PubSubEventPayload> pubsubEvent; + }; + + SluiftClient( + const JID& jid, + const std::string& password, + NetworkFactories* networkFactories, + SimpleEventLoop* eventLoop); + ~SluiftClient(); + + Client* getClient() { + return client; + } + + ClientOptions& getOptions() { + return options; + } + + void connect(); + void connect(const std::string& host, int port); + void waitConnected(int timeout); + bool isConnected() const; + void setTraceEnabled(bool b); + + template<typename T> + Sluift::Response sendPubSubRequest( + IQ::Type type, const JID& jid, boost::shared_ptr<T> payload, int timeout) { + return sendRequest(client->getPubSubManager()->createRequest( + type, jid, payload), timeout); + } + + template<typename REQUEST_TYPE> + Sluift::Response sendRequest(REQUEST_TYPE request, int timeout) { + boost::signals::scoped_connection c = request->onResponse.connect( + boost::bind(&SluiftClient::handleRequestResponse, this, _1, _2)); + return doSendRequest(request, timeout); + } + + template<typename REQUEST_TYPE> + Sluift::Response sendVoidRequest(REQUEST_TYPE request, int timeout) { + boost::signals::scoped_connection c = request->onResponse.connect( + boost::bind(&SluiftClient::handleRequestResponse, this, boost::shared_ptr<Payload>(), _1)); + return doSendRequest(request, timeout); + } + + void disconnect(); + void setSoftwareVersion(const std::string& name, const std::string& version, const std::string& os); + boost::optional<SluiftClient::Event> getNextEvent(int timeout, + boost::function<bool (const Event&)> condition = 0); + std::vector<XMPPRosterItem> getRoster(); + + private: + Sluift::Response doSendRequest(boost::shared_ptr<Request> request, int timeout); + + void handleIncomingMessage(boost::shared_ptr<Message> stanza); + void handleIncomingPresence(boost::shared_ptr<Presence> stanza); + void handleIncomingPubSubEvent(const JID& from, boost::shared_ptr<PubSubEventPayload> event); + void handleInitialRosterPopulated(); + void handleRequestResponse(boost::shared_ptr<Payload> response, boost::shared_ptr<ErrorPayload> error); + void handleDisconnected(const boost::optional<ClientError>& error); + + private: + NetworkFactories* networkFactories; + SimpleEventLoop* eventLoop; + Client* client; + ClientOptions options; + ClientXMLTracer* tracer; + bool rosterReceived; + std::deque<Event> pendingEvents; + boost::optional<ClientError> disconnectedError; + bool requestResponseReceived; + boost::shared_ptr<Payload> requestResponse; + boost::shared_ptr<ErrorPayload> requestError; + }; +} diff --git a/Sluift/SluiftComponent.cpp b/Sluift/SluiftComponent.cpp new file mode 100644 index 0000000..c8a17a7 --- /dev/null +++ b/Sluift/SluiftComponent.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2014 Kevin Smith and Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/SluiftComponent.h> + +#include <boost/numeric/conversion/cast.hpp> + +#include <Swiften/Component/ComponentXMLTracer.h> +#include <Swiften/Component/Component.h> +#include <Swiften/Roster/XMPPRoster.h> +#include <Sluift/SluiftGlobals.h> +#include <Sluift/Lua/Exception.h> +#include <Swiften/Elements/Message.h> +#include <Swiften/Queries/RawRequest.h> +#include <Sluift/Helpers.h> +#include <Swiften/Elements/Presence.h> + +using namespace Swift; + +SluiftComponent::SluiftComponent( + const JID& jid, + const std::string& password, + NetworkFactories* networkFactories, + SimpleEventLoop* eventLoop): + networkFactories(networkFactories), + eventLoop(eventLoop), + tracer(NULL) { + component = new Component(jid, password, networkFactories); + component->onError.connect(boost::bind(&SluiftComponent::handleError, this, _1)); + component->onMessageReceived.connect(boost::bind(&SluiftComponent::handleIncomingMessage, this, _1)); + component->onPresenceReceived.connect(boost::bind(&SluiftComponent::handleIncomingPresence, this, _1)); +} + +SluiftComponent::~SluiftComponent() { + delete tracer; + delete component; +} + +void SluiftComponent::connect(const std::string& host, int port) { + disconnectedError = boost::optional<ComponentError>(); + component->connect(host, port); +} + +void SluiftComponent::setTraceEnabled(bool b) { + if (b && !tracer) { + tracer = new ComponentXMLTracer(component); + } + else if (!b && tracer) { + delete tracer; + tracer = NULL; + } +} + +void SluiftComponent::waitConnected(int timeout) { + Watchdog watchdog(timeout, networkFactories->getTimerFactory()); + while (!watchdog.getTimedOut() && !disconnectedError && !component->isAvailable()) { + eventLoop->runUntilEvents(); + } + if (watchdog.getTimedOut()) { + component->disconnect(); + throw Lua::Exception("Timeout while connecting"); + } + if (disconnectedError) { + throw Lua::Exception(getErrorString(*disconnectedError)); + } +} + +bool SluiftComponent::isConnected() const { + return component->isAvailable(); +} + +void SluiftComponent::disconnect() { + component->disconnect(); + while (component->isAvailable()) { + eventLoop->runUntilEvents(); + } +} + +void SluiftComponent::setSoftwareVersion(const std::string& name, const std::string& version, const std::string& /* os */) { + component->setSoftwareVersion(name, version); +} + +boost::optional<SluiftComponent::Event> SluiftComponent::getNextEvent( + int timeout, boost::function<bool (const Event&)> condition) { + Watchdog watchdog(timeout, networkFactories->getTimerFactory()); + size_t currentIndex = 0; + while (true) { + // Look for pending events in the queue + while (currentIndex < pendingEvents.size()) { + Event event = pendingEvents[currentIndex]; + if (!condition || condition(event)) { + pendingEvents.erase( + pendingEvents.begin() + + boost::numeric_cast<int>(currentIndex)); + return event; + } + ++currentIndex; + } + + // Wait for new events + while (!watchdog.getTimedOut() && currentIndex >= pendingEvents.size() && component->isAvailable()) { + eventLoop->runUntilEvents(); + } + + // Finish if we're disconnected or timed out + if (watchdog.getTimedOut() || !component->isAvailable()) { + return boost::optional<Event>(); + } + } +} + +void SluiftComponent::handleIncomingMessage(boost::shared_ptr<Message> stanza) { + pendingEvents.push_back(Event(stanza)); +} + +void SluiftComponent::handleIncomingPresence(boost::shared_ptr<Presence> stanza) { + pendingEvents.push_back(Event(stanza)); +} + +void SluiftComponent::handleRequestResponse(boost::shared_ptr<Payload> response, boost::shared_ptr<ErrorPayload> error) { + requestResponse = response; + requestError = error; + requestResponseReceived = true; +} + +void SluiftComponent::handleError(const boost::optional<ComponentError>& error) { + disconnectedError = error; +} + +Sluift::Response SluiftComponent::doSendRequest(boost::shared_ptr<Request> request, int timeout) { + requestResponse.reset(); + requestError.reset(); + requestResponseReceived = false; + request->send(); + + Watchdog watchdog(timeout, networkFactories->getTimerFactory()); + while (!watchdog.getTimedOut() && !requestResponseReceived) { + eventLoop->runUntilEvents(); + } + return Sluift::Response(requestResponse, watchdog.getTimedOut() ? + boost::make_shared<ErrorPayload>(ErrorPayload::RemoteServerTimeout) : requestError); +} diff --git a/Sluift/SluiftComponent.h b/Sluift/SluiftComponent.h new file mode 100644 index 0000000..ba848e1 --- /dev/null +++ b/Sluift/SluiftComponent.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2014 Kevin Smith and Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#pragma once + +#include <deque> +#include <boost/optional.hpp> +#include <boost/bind.hpp> +#include <boost/function.hpp> + +#include <Swiften/Client/ClientOptions.h> +#include <Swiften/Elements/IQ.h> +#include <Swiften/Elements/Message.h> +#include <Swiften/Elements/Presence.h> +#include <Swiften/Queries/GenericRequest.h> +#include <Swiften/Roster/XMPPRosterItem.h> +#include <Swiften/Component/ComponentError.h> +#include <Swiften/Network/NetworkFactories.h> +#include <Swiften/Component/Component.h> +#include <Swiften/EventLoop/SimpleEventLoop.h> +#include <Sluift/Watchdog.h> +#include <Sluift/Response.h> + +namespace Swift { + struct SluiftGlobals; + class ComponentXMLTracer; + class Component; + class Stanza; + class Payload; + class ErrorPayload; + class JID; + + class SluiftComponent { + public: + struct Event { + enum Type { + MessageType, + PresenceType + }; + + Event(boost::shared_ptr<Message> stanza) : type(MessageType), stanza(stanza) {} + Event(boost::shared_ptr<Presence> stanza) : type(PresenceType), stanza(stanza) {} + + Type type; + + // Message & Presence + boost::shared_ptr<Stanza> stanza; + }; + + SluiftComponent( + const JID& jid, + const std::string& password, + NetworkFactories* networkFactories, + SimpleEventLoop* eventLoop); + ~SluiftComponent(); + + Component* getComponent() { + return component; + } + + void connect(const std::string& host, int port); + void waitConnected(int timeout); + bool isConnected() const; + void setTraceEnabled(bool b); + + template<typename REQUEST_TYPE> + Sluift::Response sendRequest(REQUEST_TYPE request, int timeout) { + boost::signals::scoped_connection c = request->onResponse.connect( + boost::bind(&SluiftComponent::handleRequestResponse, this, _1, _2)); + return doSendRequest(request, timeout); + } + + template<typename REQUEST_TYPE> + Sluift::Response sendVoidRequest(REQUEST_TYPE request, int timeout) { + boost::signals::scoped_connection c = request->onResponse.connect( + boost::bind(&SluiftComponent::handleRequestResponse, this, boost::shared_ptr<Payload>(), _1)); + return doSendRequest(request, timeout); + } + + void disconnect(); + void setSoftwareVersion(const std::string& name, const std::string& version, const std::string& os); + boost::optional<SluiftComponent::Event> getNextEvent(int timeout, + boost::function<bool (const Event&)> condition = 0); + + private: + Sluift::Response doSendRequest(boost::shared_ptr<Request> request, int timeout); + + void handleIncomingMessage(boost::shared_ptr<Message> stanza); + void handleIncomingPresence(boost::shared_ptr<Presence> stanza); + void handleRequestResponse(boost::shared_ptr<Payload> response, boost::shared_ptr<ErrorPayload> error); + void handleError(const boost::optional<ComponentError>& error); + + private: + NetworkFactories* networkFactories; + SimpleEventLoop* eventLoop; + Component* component; + ComponentXMLTracer* tracer; + std::deque<Event> pendingEvents; + boost::optional<ComponentError> disconnectedError; + bool requestResponseReceived; + boost::shared_ptr<Payload> requestResponse; + boost::shared_ptr<ErrorPayload> requestError; + }; +} diff --git a/Sluift/SluiftException.h b/Sluift/SluiftException.h deleted file mode 100644 index 28393ab..0000000 --- a/Sluift/SluiftException.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2011 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. - */ - -#pragma once - -#include <string> - -#include <Swiften/Client/ClientError.h> - -namespace Swift { - class SluiftException { - public: - SluiftException(const std::string& reason) : reason(reason) { - } - - SluiftException(const ClientError& error) { - std::string reason("Disconnected: "); - switch(error.getType()) { - case ClientError::UnknownError: reason += "Unknown Error"; break; - case ClientError::DomainNameResolveError: reason += "Unable to find server"; break; - case ClientError::ConnectionError: reason += "Error connecting to server"; break; - case ClientError::ConnectionReadError: reason += "Error while receiving server data"; break; - case ClientError::ConnectionWriteError: reason += "Error while sending data to the server"; break; - case ClientError::XMLError: reason += "Error parsing server data"; break; - case ClientError::AuthenticationFailedError: reason += "Login/password invalid"; break; - case ClientError::CompressionFailedError: reason += "Error while compressing stream"; break; - case ClientError::ServerVerificationFailedError: reason += "Server verification failed"; break; - case ClientError::NoSupportedAuthMechanismsError: reason += "Authentication mechanisms not supported"; break; - case ClientError::UnexpectedElementError: reason += "Unexpected response"; break; - case ClientError::ResourceBindError: reason += "Error binding resource"; break; - case ClientError::RevokedError: reason += "Certificate got revoked"; break; - case ClientError::RevocationCheckFailedError: reason += "Failed to do revokation check"; break; - case ClientError::SessionStartError: reason += "Error starting session"; break; - case ClientError::StreamError: reason += "Stream error"; break; - case ClientError::TLSError: reason += "Encryption error"; break; - case ClientError::ClientCertificateLoadError: reason += "Error loading certificate (Invalid password?)"; break; - case ClientError::ClientCertificateError: reason += "Certificate not authorized"; break; - case ClientError::UnknownCertificateError: reason += "Unknown certificate"; break; - case ClientError::CertificateCardRemoved: reason += "Certificate card removed"; break; - case ClientError::CertificateExpiredError: reason += "Certificate has expired"; break; - case ClientError::CertificateNotYetValidError: reason += "Certificate is not yet valid"; break; - case ClientError::CertificateSelfSignedError: reason += "Certificate is self-signed"; break; - case ClientError::CertificateRejectedError: reason += "Certificate has been rejected"; break; - case ClientError::CertificateUntrustedError: reason += "Certificate is not trusted"; break; - case ClientError::InvalidCertificatePurposeError: reason += "Certificate cannot be used for encrypting your connection"; break; - case ClientError::CertificatePathLengthExceededError: reason += "Certificate path length constraint exceeded"; break; - case ClientError::InvalidCertificateSignatureError: reason += "Invalid certificate signature"; break; - case ClientError::InvalidCAError: reason += "Invalid Certificate Authority"; break; - case ClientError::InvalidServerIdentityError: reason += "Certificate does not match the host identity"; break; - } - } - - const std::string& getReason() const { - return reason; - } - - private: - std::string reason; - }; -} diff --git a/Sluift/SluiftGlobals.h b/Sluift/SluiftGlobals.h new file mode 100644 index 0000000..9d9cc51 --- /dev/null +++ b/Sluift/SluiftGlobals.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2013-2014 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#pragma once + +#include <Sluift/LuaElementConvertors.h> +#include <Swiften/EventLoop/SimpleEventLoop.h> +#include <Swiften/Network/BoostNetworkFactories.h> +#include <Swiften/TLS/PlatformTLSFactories.h> +#ifdef HAVE_ITUNES +#include <Sluift/ITunesInterface.h> +#endif +#include <signal.h> + +namespace Swift { + struct SluiftGlobals { + SluiftGlobals() : + networkFactories(&eventLoop), + coreLibIndex(-1), + moduleLibIndex(-1), + interruptRequested(0) {} + + LuaElementConvertors elementConvertor; + SimpleEventLoop eventLoop; + BoostNetworkFactories networkFactories; + PlatformTLSFactories tlsFactories; + int coreLibIndex; + int moduleLibIndex; + sig_atomic_t interruptRequested; +#ifdef HAVE_ITUNES + ITunesInterface iTunes; +#endif + }; +} diff --git a/Sluift/StandardTerminal.cpp b/Sluift/StandardTerminal.cpp new file mode 100644 index 0000000..f055684 --- /dev/null +++ b/Sluift/StandardTerminal.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/StandardTerminal.h> + +#include <boost/optional.hpp> +#include <iostream> +#include <stdexcept> + +using namespace Swift; + +StandardTerminal::StandardTerminal() { +} + +StandardTerminal::~StandardTerminal() { +} + +void StandardTerminal::printError(const std::string& message) { + std::cout << message << std::endl; +} + +boost::optional<std::string> StandardTerminal::readLine(const std::string& prompt) { + std::cout << prompt << std::flush; + std::string input; + if (!std::getline(std::cin, input)) { + if (std::cin.eof()) { + return boost::optional<std::string>(); + } + throw std::runtime_error("Input error"); + } + return input; +} + +void StandardTerminal::addToHistory(const std::string&) { +} diff --git a/Sluift/StandardTerminal.h b/Sluift/StandardTerminal.h new file mode 100644 index 0000000..420df6b --- /dev/null +++ b/Sluift/StandardTerminal.h @@ -0,0 +1,22 @@ +/* + * 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/Terminal.h> + +namespace Swift { + class StandardTerminal : public Terminal { + public: + StandardTerminal(); + virtual ~StandardTerminal(); + + virtual boost::optional<std::string> readLine(const std::string& prompt) SWIFTEN_OVERRIDE; + virtual void printError(const std::string& message) SWIFTEN_OVERRIDE; + virtual void addToHistory(const std::string& command); + }; +} diff --git a/Sluift/Terminal.cpp b/Sluift/Terminal.cpp new file mode 100644 index 0000000..9233233 --- /dev/null +++ b/Sluift/Terminal.cpp @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/Terminal.h> + +using namespace Swift; + +Terminal::Terminal() : completer(NULL) { +} + +Terminal::~Terminal() { +} diff --git a/Sluift/Terminal.h b/Sluift/Terminal.h new file mode 100644 index 0000000..2d5e41b --- /dev/null +++ b/Sluift/Terminal.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#pragma once + +#include <string> +#include <boost/optional/optional_fwd.hpp> + +namespace Swift { + class Completer; + + class Terminal { + public: + Terminal(); + virtual ~Terminal(); + + Completer* getCompleter() const { + return completer; + } + + void setCompleter(Completer* completer) { + this->completer = completer; + } + + virtual boost::optional<std::string> readLine(const std::string& prompt) = 0; + virtual void addToHistory(const std::string& command) = 0; + virtual void printError(const std::string& message) = 0; + + private: + Completer* completer; + }; +} diff --git a/Sluift/Tests/ConnectTest.lua b/Sluift/Tests/ConnectTest.lua new file mode 100644 index 0000000..0afa3fa --- /dev/null +++ b/Sluift/Tests/ConnectTest.lua @@ -0,0 +1,6 @@ +-- Test connect with function parameter +client = sluift.new_client(os.getenv("SLUIFT_JID"), os.getenv("SLUIFT_PASS")) +x, y, z = client:connect(function (c) return 1, '2', true end) +assert(x == 1) +assert(y == '2') +assert(z == true) diff --git a/Sluift/Tests/DOMTest.lua b/Sluift/Tests/DOMTest.lua new file mode 100644 index 0000000..0c31183 --- /dev/null +++ b/Sluift/Tests/DOMTest.lua @@ -0,0 +1,14 @@ +t = sluift.from_xml([[ + <foo xmlns='http://swift.im/test' myattr1='myval1' myattr2='myval2'> + <bar myattr3='myval3'> + <baz/> + </bar> + Some Text + <bam myattr4='myval4'> + Some other text + </bam> + </foo> +]]) + +print(t) +print(sluift.to_xml(t)) diff --git a/Sluift/Tests/FormTest.lua b/Sluift/Tests/FormTest.lua new file mode 100644 index 0000000..3b311da --- /dev/null +++ b/Sluift/Tests/FormTest.lua @@ -0,0 +1,92 @@ +--[[ + Copyright (c) 2013 Remko Tronçon + Licensed under the GNU General Public License v3. + See Documentation/Licenses/GPLv3.txt for more information. +--]] + +example_form = [[ +<x xmlns='jabber:x:data' type='form'> + <title>Bot Configuration</title> + <instructions>Fill out this form to configure your new bot!</instructions> + <field type='hidden' var='FORM_TYPE'> + <value>jabber:bot</value> + </field> + <field type='fixed'><value>Section 1: Bot Info</value></field> + <field type='text-single' label='The name of your bot' var='botname'/> + <field type='text-multi' label='Helpful description of your bot' var='description'> + <value>This is</value> + <value>my bot</value> + </field> + <field type='boolean' label='Public bot?' var='public'> + <required/> + </field> + <field type='text-private' label='Password for special access' var='password'/> + <field type='fixed'><value>Section 2: Features</value></field> + <field type='list-multi' label='What features will the bot support?' var='features'> + <option label='Contests'><value>contests</value></option> + <option label='News'><value>news</value></option> + <option label='Polls'><value>polls</value></option> + <option label='Reminders'><value>reminders</value></option> + <option label='Search'><value>search</value></option> + <value>news</value> + <value>search</value> + </field> + <field type='fixed'><value>Section 3: Subscriber List</value></field> + <field type='list-single' label='Maximum number of subscribers' var='maxsubs'> + <value>20</value> + <option label='10'><value>10</value></option> + <option label='20'><value>20</value></option> + <option label='30'><value>30</value></option> + <option label='50'><value>50</value></option> + <option label='100'><value>100</value></option> + <option label='None'><value>none</value></option> + </field> + <field type='fixed'><value>Section 4: Invitations</value></field> + <field type='jid-multi' label='People to invite' var='invitelist'> + <desc>Tell all your friends about your new bot!</desc> + </field> +</x>]] + +form = sluift.from_xml(example_form) +--print(form) + +-- Test form properties +assert(form['title'] == 'Bot Configuration') + +-- Test boolean field +public_field = form['fields'][5] +assert(public_field['name'] == 'public') +assert(type(public_field['value']) == 'boolean') +assert(public_field['required'] == true) + +-- Test multi field +features_field = form['fields'][8] +assert(features_field['name'] == 'features') +assert(type(features_field['value']) == 'table') +assert(#features_field['value'] == 2) +assert(features_field['value'][1] == 'news') +assert(features_field['value'][2] == 'search') + +-- Test shortcut index +assert(form['features']['name'] == 'features') +assert(form['FORM_TYPE']['value'] == 'jabber:bot') + +-- Test response form +submission = form:create_submission() +assert(#(submission.fields) == 8) +description_submit_value = submission['description']['value'] +assert(type(description_submit_value) == 'table') +assert(description_submit_value[1] == 'This is') +assert(description_submit_value[2] == 'my bot') + +submission['description'] = 'my description' +assert(submission['description']['value'] == 'my description') +submission['type'] = 'cancel' + +-- Test text-multi field +text_multi_field = form['fields'][4] +assert(text_multi_field['name'] == 'description') +assert(type(text_multi_field['value']) == 'table') + + +--print(sluift.to_xml({type = 'form', data = form})) diff --git a/Sluift/UnitTest/TokenizeTest.cpp b/Sluift/UnitTest/TokenizeTest.cpp new file mode 100644 index 0000000..1bf20ed --- /dev/null +++ b/Sluift/UnitTest/TokenizeTest.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include <Sluift/tokenize.h> + +using namespace Swift; + +class TokenizeTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(TokenizeTest); + CPPUNIT_TEST(testTokenize); + CPPUNIT_TEST(testTokenize); + CPPUNIT_TEST(testTokenize_String); + CPPUNIT_TEST(testTokenize_IncompleteString); + CPPUNIT_TEST(testTokenize_Identifier); + CPPUNIT_TEST_SUITE_END(); + + public: + void testTokenize() { + std::vector<std::string> tokens = Lua::tokenize("foo.bar + 1.23 - bam"); + + CPPUNIT_ASSERT_EQUAL(7, static_cast<int>(tokens.size())); + CPPUNIT_ASSERT_EQUAL(std::string("foo"), tokens[0]); + CPPUNIT_ASSERT_EQUAL(std::string("."), tokens[1]); + CPPUNIT_ASSERT_EQUAL(std::string("bar"), tokens[2]); + CPPUNIT_ASSERT_EQUAL(std::string("+"), tokens[3]); + CPPUNIT_ASSERT_EQUAL(std::string("1.23"), tokens[4]); + CPPUNIT_ASSERT_EQUAL(std::string("-"), tokens[5]); + CPPUNIT_ASSERT_EQUAL(std::string("bam"), tokens[6]); + } + + void testTokenize_String() { + std::vector<std::string> tokens = Lua::tokenize(" foo .. \"1234\\\"bla blo\""); + + CPPUNIT_ASSERT_EQUAL(3, static_cast<int>(tokens.size())); + CPPUNIT_ASSERT_EQUAL(std::string("foo"), tokens[0]); + CPPUNIT_ASSERT_EQUAL(std::string(".."), tokens[1]); + CPPUNIT_ASSERT_EQUAL(std::string("\"1234\\\"bla blo\""), tokens[2]); + } + + void testTokenize_IncompleteString() { + std::vector<std::string> tokens = Lua::tokenize("\"1234"); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(tokens.size())); + CPPUNIT_ASSERT_EQUAL(std::string("\"1234"), tokens[0]); + } + + void testTokenize_Identifier() { + std::vector<std::string> tokens = Lua::tokenize("foo.bar_baz"); + + CPPUNIT_ASSERT_EQUAL(3, static_cast<int>(tokens.size())); + CPPUNIT_ASSERT_EQUAL(std::string("foo"), tokens[0]); + CPPUNIT_ASSERT_EQUAL(std::string("."), tokens[1]); + CPPUNIT_ASSERT_EQUAL(std::string("bar_baz"), tokens[2]); + } + +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TokenizeTest); diff --git a/Sluift/Watchdog.cpp b/Sluift/Watchdog.cpp new file mode 100644 index 0000000..8b1c9ab --- /dev/null +++ b/Sluift/Watchdog.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2014 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/Watchdog.h> + +#include <Sluift/globals.h> + +static const int INTERVAL_MS = 500; + + +using namespace Swift; + +Watchdog::Watchdog(int timeout, TimerFactory* timerFactory) : + remainingTime(timeout), + timerFactory(timerFactory), + timedOut(false) { + Sluift::globals.interruptRequested = 0; + + int nextTimeout = remainingTime >= 0 ? std::min(remainingTime, INTERVAL_MS) : INTERVAL_MS; + + timer = timerFactory->createTimer(nextTimeout); + timer->onTick.connect(boost::bind(&Watchdog::handleTimerTick, this)); + remainingTime -= nextTimeout; + timer->start(); +} + +Watchdog::~Watchdog() { + if (timer) { + timer->stop(); + } +} + +void Watchdog::handleTimerTick() { + if (Sluift::globals.interruptRequested || remainingTime == 0) { + timedOut = true; + } + else { + int nextTimeout = remainingTime >= 0 ? std::min(remainingTime, INTERVAL_MS) : INTERVAL_MS; + if (nextTimeout != INTERVAL_MS) { + timer->onTick.disconnect(boost::bind(&Watchdog::handleTimerTick, this)); + timer = timerFactory->createTimer(nextTimeout); + timer->onTick.connect(boost::bind(&Watchdog::handleTimerTick, this)); + } + remainingTime -= nextTimeout; + timer->start(); + } +} diff --git a/Sluift/Watchdog.h b/Sluift/Watchdog.h index 95b6971..868621e 100644 --- a/Sluift/Watchdog.h +++ b/Sluift/Watchdog.h @@ -1,4 +1,4 @@ /* - * Copyright (c) 2011 Remko Tronçon + * Copyright (c) 2011-2014 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. @@ -7,4 +7,6 @@ #pragma once +#include <algorithm> + #include <Swiften/Network/TimerFactory.h> @@ -12,20 +14,6 @@ namespace Swift { class Watchdog { public: - Watchdog(int timeout, TimerFactory* timerFactory) : timedOut(false) { - if (timeout > 0) { - timer = timerFactory->createTimer(timeout); - timer->start(); - timer->onTick.connect(boost::bind(&Watchdog::handleTimerTick, this)); - } - else if (timeout == 0) { - timedOut = true; - } - } - - ~Watchdog() { - if (timer) { - timer->stop(); - } - } + Watchdog(int timeout, TimerFactory* timerFactory); + ~Watchdog(); bool getTimedOut() const { @@ -34,10 +22,10 @@ namespace Swift { private: - void handleTimerTick() { - timedOut = true; - } + void handleTimerTick(); private: Timer::ref timer; + int remainingTime; + TimerFactory* timerFactory; bool timedOut; }; diff --git a/Sluift/client.cpp b/Sluift/client.cpp new file mode 100644 index 0000000..e8e6a1a --- /dev/null +++ b/Sluift/client.cpp @@ -0,0 +1,724 @@ +/* + * Copyright (c) 2013-2014 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <boost/lambda/lambda.hpp> +#include <boost/lambda/bind.hpp> +#include <boost/assign/list_of.hpp> +#include <iostream> + +#include <Sluift/SluiftClient.h> +#include <Swiften/JID/JID.h> +#include <Swiften/Elements/SoftwareVersion.h> +#include <Swiften/Elements/Message.h> +#include <Swiften/Elements/Presence.h> +#include <Swiften/Elements/RawXMLPayload.h> +#include <Swiften/Elements/RosterItemPayload.h> +#include <Swiften/Elements/RosterPayload.h> +#include <Swiften/Elements/DiscoInfo.h> +#include <Swiften/Elements/MAMQuery.h> +#include <Swiften/Disco/ClientDiscoManager.h> +#include <Swiften/Queries/GenericRequest.h> +#include <Swiften/Presence/PresenceSender.h> +#include <Swiften/Roster/XMPPRoster.h> +#include <Swiften/Roster/SetRosterRequest.h> +#include <Swiften/Presence/SubscriptionManager.h> +#include <Swiften/Roster/XMPPRosterItem.h> +#include <Swiften/Queries/IQRouter.h> +#include <Swiften/Queries/Requests/GetSoftwareVersionRequest.h> +#include <Sluift/Lua/FunctionRegistration.h> +#include <Swiften/Base/foreach.h> +#include <Swiften/Base/IDGenerator.h> +#include <Sluift/Lua/Check.h> +#include <Sluift/Lua/Value.h> +#include <Sluift/Lua/Exception.h> +#include <Sluift/Lua/LuaUtils.h> +#include <Sluift/globals.h> +#include <Sluift/ElementConvertors/StanzaConvertor.h> +#include <Sluift/ElementConvertors/IQConvertor.h> +#include <Sluift/ElementConvertors/PresenceConvertor.h> +#include <Sluift/ElementConvertors/MessageConvertor.h> +#include <Sluift/ElementConvertors/StatusShowConvertor.h> + +using namespace Swift; +namespace lambda = boost::lambda; + +static inline SluiftClient* getClient(lua_State* L) { + return *Lua::checkUserData<SluiftClient>(L, 1); +} + +static inline int getGlobalTimeout(lua_State* L) { + lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.moduleLibIndex); + lua_getfield(L, -1, "timeout"); + int result = boost::numeric_cast<int>(lua_tointeger(L, -1)); + lua_pop(L, 2); + return result; +} + +static void addPayloadsToTable(lua_State* L, const std::vector<boost::shared_ptr<Payload> >& payloads) { + if (!payloads.empty()) { + lua_createtable(L, boost::numeric_cast<int>(payloads.size()), 0); + for (size_t i = 0; i < payloads.size(); ++i) { + Sluift::globals.elementConvertor.convertToLua(L, payloads[i]); + lua_rawseti(L, -2, boost::numeric_cast<int>(i+1)); + } + Lua::registerGetByTypeIndex(L, -1); + lua_setfield(L, -2, "payloads"); + } +} + +static boost::shared_ptr<Payload> getPayload(lua_State* L, int index) { + if (lua_type(L, index) == LUA_TTABLE) { + return boost::dynamic_pointer_cast<Payload>(Sluift::globals.elementConvertor.convertFromLua(L, index)); + } + else if (lua_type(L, index) == LUA_TSTRING) { + return boost::make_shared<RawXMLPayload>(Lua::checkString(L, index)); + } + else { + return boost::shared_ptr<Payload>(); + } +} + +static std::vector< boost::shared_ptr<Payload> > getPayloadsFromTable(lua_State* L, int index) { + index = Lua::absoluteOffset(L, index); + std::vector< boost::shared_ptr<Payload> > result; + lua_getfield(L, index, "payloads"); + if (lua_istable(L, -1)) { + for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { + boost::shared_ptr<Payload> payload = getPayload(L, -1); + if (payload) { + result.push_back(payload); + } + } + } + lua_pop(L, 1); + return result; +} + +SLUIFT_LUA_FUNCTION(Client, async_connect) { + SluiftClient* client = getClient(L); + + std::string host = client->getOptions().manualHostname; + int port = client->getOptions().manualPort; + if (lua_istable(L, 2)) { + if (boost::optional<std::string> hostString = Lua::getStringField(L, 2, "host")) { + host = *hostString; + } + if (boost::optional<int> portInt = Lua::getIntField(L, 2, "port")) { + port = *portInt; + } + } + client->connect(host, port); + return 0; +} + +SLUIFT_LUA_FUNCTION_WITH_HELP( + Client, set_trace_enabled, + "Enable/disable tracing of the data sent/received.\n\n.", + "self\n" + "enable a boolean specifying whether to enable/disable tracing", + "" +) { + getClient(L)->setTraceEnabled(lua_toboolean(L, 1)); + return 0; +} + +SLUIFT_LUA_FUNCTION_WITH_HELP( + Client, wait_connected, + "Block until the client is connected.\n\nThis is useful after an `async_connect`.", + "self", + "" +) { + getClient(L)->waitConnected(getGlobalTimeout(L)); + return 0; +} + +SLUIFT_LUA_FUNCTION_WITH_HELP( + Client, is_connected, + "Checks whether this client is still connected.\n\nReturns a boolean.", + "self\n", + "" +) { + lua_pushboolean(L, getClient(L)->isConnected()); + return 1; +} + +SLUIFT_LUA_FUNCTION_WITH_HELP( + Client, disconnect, + "Disconnect from the server", + "self\n", + "" +) { + Sluift::globals.eventLoop.runOnce(); + getClient(L)->disconnect(); + return 0; +} + +SLUIFT_LUA_FUNCTION_WITH_HELP( + Client, set_version, + + "Sets the published version of this client.", + + "self", + + "name the name of the client software\n" + "version the version identifier of this client\n" + "os the OS this client is running on\n" +) { + Sluift::globals.eventLoop.runOnce(); + SluiftClient* client = getClient(L); + if (boost::shared_ptr<SoftwareVersion> version = boost::dynamic_pointer_cast<SoftwareVersion>(Sluift::globals.elementConvertor.convertFromLuaUntyped(L, 2, "software_version"))) { + client->setSoftwareVersion(version->getName(), version->getVersion(), version->getOS()); + } + return 0; +} + +SLUIFT_LUA_FUNCTION_WITH_HELP( + Client, get_contacts, + "Returns a table of all the contacts in the contact list.", + "self\n", + "" +) { + Sluift::globals.eventLoop.runOnce(); + + SluiftClient* client = getClient(L); + Lua::Table contactsTable; + foreach(const XMPPRosterItem& item, client->getRoster()) { + std::string subscription; + switch(item.getSubscription()) { + case RosterItemPayload::None: subscription = "none"; break; + case RosterItemPayload::To: subscription = "to"; break; + case RosterItemPayload::From: subscription = "from"; break; + case RosterItemPayload::Both: subscription = "both"; break; + case RosterItemPayload::Remove: subscription = "remove"; break; + } + Lua::Table itemTable = boost::assign::map_list_of + ("jid", boost::make_shared<Lua::Value>(item.getJID().toString())) + ("name", boost::make_shared<Lua::Value>(item.getName())) + ("subscription", boost::make_shared<Lua::Value>(subscription)) + ("groups", boost::make_shared<Lua::Value>(std::vector<Lua::Value>(item.getGroups().begin(), item.getGroups().end()))); + contactsTable[item.getJID().toString()] = boost::make_shared<Lua::Value>(itemTable); + } + pushValue(L, contactsTable); + Lua::registerTableToString(L, -1); + return 1; +} + +SLUIFT_LUA_FUNCTION_WITH_HELP( + Client, send_message, + "Send a message.", + "self\n" + "to the JID to send the message to\n" + "body the body of the message. Can alternatively be specified using the `body` option\n", + + "to the JID to send the message to\n" + "body the body of the message\n" + "subject the subject of the MUC room to set\n" + "type the type of message to send (`normal`, `chat`, `error`, `groupchat`, `headline`)\n" + "payloads payloads to add to the message\n" +) { + Sluift::globals.eventLoop.runOnce(); + JID to; + boost::optional<std::string> body; + boost::optional<std::string> subject; + std::vector<boost::shared_ptr<Payload> > payloads; + int index = 2; + Message::Type type = Message::Chat; + if (lua_isstring(L, index)) { + to = std::string(lua_tostring(L, index)); + ++index; + if (lua_isstring(L, index)) { + body = lua_tostring(L, index); + ++index; + } + } + if (lua_istable(L, index)) { + if (boost::optional<std::string> value = Lua::getStringField(L, index, "to")) { + to = *value; + } + + if (boost::optional<std::string> value = Lua::getStringField(L, index, "body")) { + body = value; + } + + if (boost::optional<std::string> value = Lua::getStringField(L, index, "type")) { + type = MessageConvertor::convertMessageTypeFromString(*value); + } + + if (boost::optional<std::string> value = Lua::getStringField(L, index, "subject")) { + subject = value; + } + + payloads = getPayloadsFromTable(L, index); + } + + if (!to.isValid()) { + throw Lua::Exception("Missing 'to'"); + } + if ((!body || body->empty()) && !subject && payloads.empty()) { + throw Lua::Exception("Missing any of 'body', 'subject' or 'payloads'"); + } + Message::ref message = boost::make_shared<Message>(); + message->setTo(to); + if (body && !body->empty()) { + message->setBody(*body); + } + if (subject) { + message->setSubject(*subject); + } + message->addPayloads(payloads.begin(), payloads.end()); + message->setType(type); + getClient(L)->getClient()->sendMessage(message); + return 0; +} + +SLUIFT_LUA_FUNCTION_WITH_HELP( + Client, send_presence, + "Send presence.", + + "self\n" + "body the text of the presence. Can alternatively be specified using the `status` option\n", + + "to the JID to send the message to\n" + "status the text of the presence\n" + "show the availability of the presence (`online`, `ffc`, `away`, `xa`, `dnd`)\n" + "priority the priority of the presence\n" + "type the type of message to send (`available`, `error`, `probe`, `subscribe`, `subscribed`, `unavailable`, `unsubscribe`, `unsubscribed`)\n" + "payloads payloads to add to the presence\n" +) { + Sluift::globals.eventLoop.runOnce(); + boost::shared_ptr<Presence> presence = boost::make_shared<Presence>(); + + int index = 2; + if (lua_isstring(L, index)) { + presence->setStatus(lua_tostring(L, index)); + ++index; + } + if (lua_istable(L, index)) { + if (boost::optional<std::string> value = Lua::getStringField(L, index, "to")) { + presence->setTo(*value); + } + if (boost::optional<std::string> value = Lua::getStringField(L, index, "status")) { + presence->setStatus(*value); + } + if (boost::optional<int> value = Lua::getIntField(L, index, "priority")) { + presence->setPriority(*value); + } + if (boost::optional<std::string> value = Lua::getStringField(L, index, "type")) { + presence->setType(PresenceConvertor::convertPresenceTypeFromString(*value)); + } + if (boost::optional<std::string> value = Lua::getStringField(L, index, "show")) { + presence->setShow(StatusShowConvertor::convertStatusShowTypeFromString(*value)); + } + std::vector< boost::shared_ptr<Payload> > payloads = getPayloadsFromTable(L, index); + presence->addPayloads(payloads.begin(), payloads.end()); + } + + getClient(L)->getClient()->getPresenceSender()->sendPresence(presence); + lua_pushvalue(L, 1); + return 0; +} + +static int sendQuery(lua_State* L, IQ::Type type) { + SluiftClient* client = getClient(L); + + JID to; + if (boost::optional<std::string> toString = Lua::getStringField(L, 2, "to")) { + to = JID(*toString); + } + + int timeout = getGlobalTimeout(L); + if (boost::optional<int> timeoutInt = Lua::getIntField(L, 2, "timeout")) { + timeout = *timeoutInt; + } + + boost::shared_ptr<Payload> payload; + lua_getfield(L, 2, "query"); + payload = getPayload(L, -1); + lua_pop(L, 1); + + return client->sendRequest( + boost::make_shared< GenericRequest<Payload> >(type, to, payload, client->getClient()->getIQRouter()), timeout).convertToLuaResult(L); +} + +#define DISPATCH_PUBSUB_PAYLOAD(payloadType, container, response) \ + else if (boost::shared_ptr<payloadType> p = boost::dynamic_pointer_cast<payloadType>(payload)) { \ + return client->sendPubSubRequest(type, to, p, timeout).convertToLuaResult(L); \ + } + +SLUIFT_LUA_FUNCTION(Client, query_pubsub) { + SluiftClient* client = getClient(L); + + JID to; + if (boost::optional<std::string> toString = Lua::getStringField(L, 2, "to")) { + to = JID(*toString); + } + + int timeout = getGlobalTimeout(L); + if (boost::optional<int> timeoutInt = Lua::getIntField(L, 2, "timeout")) { + timeout = *timeoutInt; + } + + IQ::Type type; + if (boost::optional<std::string> queryType = Lua::getStringField(L, 2, "type")) { + type = IQConvertor::convertIQTypeFromString(*queryType); + } + else { + throw Lua::Exception("Missing query type"); + } + + lua_getfield(L, 2, "query"); + if (!lua_istable(L, -1)) { + throw Lua::Exception("Missing/incorrect query"); + } + boost::shared_ptr<Payload> payload = getPayload(L, -1); + + if (false) { } + SWIFTEN_PUBSUB_FOREACH_PUBSUB_PAYLOAD_TYPE(DISPATCH_PUBSUB_PAYLOAD) + else { + throw Lua::Exception("Incorrect PubSub payload"); + } +} + + +SLUIFT_LUA_FUNCTION(Client, get) { + return sendQuery(L, IQ::Get); +} + +SLUIFT_LUA_FUNCTION(Client, set) { + return sendQuery(L, IQ::Set); +} + +SLUIFT_LUA_FUNCTION_WITH_HELP( + Client, send, + "Sends a raw string", + + "self\n" + "data the string to send\n", + + "" +) { + Sluift::globals.eventLoop.runOnce(); + + getClient(L)->getClient()->sendData(std::string(Lua::checkString(L, 2))); + lua_pushvalue(L, 1); + return 0; +} + +SLUIFT_LUA_FUNCTION_WITH_HELP( + Client, set_options, + + "Sets the connection options of this client.", + + "self", + + "host The host to connect to. When omitted, is determined from resolving the JID domain.\n" + "port The port to connect to. When omitted, is determined from resolving the JID domain.\n" + "ack Request acknowledgements\n" + "compress Use stream compression when available\n" + "tls Use TLS when available\n" + "bosh_url Connect using the specified BOSH URL\n" + "allow_plain_without_tls Allow PLAIN authentication without a TLS encrypted connection\n" +) { + SluiftClient* client = getClient(L); + Lua::checkType(L, 2, LUA_TTABLE); + lua_getfield(L, 2, "host"); + if (!lua_isnil(L, -1)) { + client->getOptions().manualHostname = lua_tostring(L, -1); + } + lua_getfield(L, 2, "port"); + if (!lua_isnil(L, -1)) { + client->getOptions().manualPort = boost::numeric_cast<int>(lua_tointeger(L, -1)); + } + lua_getfield(L, 2, "ack"); + if (!lua_isnil(L, -1)) { + client->getOptions().useAcks = lua_toboolean(L, -1); + } + lua_getfield(L, 2, "compress"); + if (!lua_isnil(L, -1)) { + client->getOptions().useStreamCompression = lua_toboolean(L, -1); + } + lua_getfield(L, 2, "tls"); + if (!lua_isnil(L, -1)) { + bool useTLS = lua_toboolean(L, -1); + client->getOptions().useTLS = (useTLS ? ClientOptions::UseTLSWhenAvailable : ClientOptions::NeverUseTLS); + } + lua_getfield(L, 2, "bosh_url"); + if (!lua_isnil(L, -1)) { + client->getOptions().boshURL = URL::fromString(lua_tostring(L, -1)); + } + lua_getfield(L, 2, "allow_plain_without_tls"); + if (!lua_isnil(L, -1)) { + client->getOptions().allowPLAINWithoutTLS = lua_toboolean(L, -1); + } + lua_pushvalue(L, 1); + return 0; +} + +static void pushEvent(lua_State* L, const SluiftClient::Event& event) { + switch (event.type) { + case SluiftClient::Event::MessageType: { + Message::ref message = boost::dynamic_pointer_cast<Message>(event.stanza); + Lua::Table result = boost::assign::map_list_of + ("type", boost::make_shared<Lua::Value>(std::string("message"))) + ("from", boost::make_shared<Lua::Value>(message->getFrom().toString())) + ("body", boost::make_shared<Lua::Value>(message->getBody())) + ("message_type", boost::make_shared<Lua::Value>(MessageConvertor::convertMessageTypeToString(message->getType()))); + Lua::pushValue(L, result); + addPayloadsToTable(L, message->getPayloads()); + Lua::registerTableToString(L, -1); + break; + } + case SluiftClient::Event::PresenceType: { + Presence::ref presence = boost::dynamic_pointer_cast<Presence>(event.stanza); + Lua::Table result = boost::assign::map_list_of + ("type", boost::make_shared<Lua::Value>(std::string("presence"))) + ("from", boost::make_shared<Lua::Value>(presence->getFrom().toString())) + ("status", boost::make_shared<Lua::Value>(presence->getStatus())) + ("presence_type", boost::make_shared<Lua::Value>(PresenceConvertor::convertPresenceTypeToString(presence->getType()))); + Lua::pushValue(L, result); + addPayloadsToTable(L, presence->getPayloads()); + Lua::registerTableToString(L, -1); + break; + } + case SluiftClient::Event::PubSubEventType: { + Sluift::globals.elementConvertor.convertToLua(L, event.pubsubEvent); + lua_pushstring(L, "pubsub"); + lua_setfield(L, -2, "type"); + lua_pushstring(L, event.from.toString().c_str()); + lua_setfield(L, -2, "from"); + + lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.coreLibIndex); + lua_getfield(L, -1, "process_pubsub_event"); + lua_pushvalue(L, -3); + lua_call(L, 1, 0); + lua_pop(L, 1); + } + } +} + +struct CallUnaryLuaPredicateOnEvent { + CallUnaryLuaPredicateOnEvent(lua_State* L, int index) : L(L), index(index) { + } + + bool operator()(const SluiftClient::Event& event) { + lua_pushvalue(L, index); + pushEvent(L, event); + if (lua_pcall(L, 1, 1, 0) != 0) { + throw Lua::Exception(lua_tostring(L, -1)); + } + bool result = lua_toboolean(L, -1); + lua_pop(L, 1); + return result; + } + + lua_State* L; + int index; +}; + + +SLUIFT_LUA_FUNCTION(Client, get_next_event) { + Sluift::globals.eventLoop.runOnce(); + SluiftClient* client = getClient(L); + + int timeout = getGlobalTimeout(L); + boost::optional<SluiftClient::Event::Type> type; + int condition = 0; + if (lua_istable(L, 2)) { + if (boost::optional<std::string> typeString = Lua::getStringField(L, 2, "type")) { + if (*typeString == "message") { + type = SluiftClient::Event::MessageType; + } + else if (*typeString == "presence") { + type = SluiftClient::Event::PresenceType; + } + else if (*typeString == "pubsub") { + type = SluiftClient::Event::PubSubEventType; + } + } + if (boost::optional<int> timeoutInt = Lua::getIntField(L, 2, "timeout")) { + timeout = *timeoutInt; + } + lua_getfield(L, 2, "if"); + if (lua_isfunction(L, -1)) { + condition = Lua::absoluteOffset(L, -1); + } + } + + boost::optional<SluiftClient::Event> event; + if (condition) { + event = client->getNextEvent(timeout, CallUnaryLuaPredicateOnEvent(L, condition)); + } + else if (type) { + event = client->getNextEvent( + timeout, lambda::bind(&SluiftClient::Event::type, lambda::_1) == *type); + } + else { + event = client->getNextEvent(timeout); + } + + if (event) { + pushEvent(L, *event); + } + else { + lua_pushnil(L); + } + return 1; +} + + +SLUIFT_LUA_FUNCTION_WITH_HELP( + Client, add_contact, + "Add a contact to the contact list.", + "self\n", + "jid The JID of the contact to add\n" + "name The name to use in the contact list\n" + "groups An array of group names to add the contact to\n") { + Sluift::globals.eventLoop.runOnce(); + SluiftClient* client = getClient(L); + RosterItemPayload item; + if (lua_type(L, 2) == LUA_TTABLE) { + lua_getfield(L, 2, "jid"); + const char* rawJID = lua_tostring(L, -1); + if (rawJID) { + item.setJID(std::string(rawJID)); + } + lua_getfield(L, 2, "name"); + const char* rawName = lua_tostring(L, -1); + if (rawName) { + item.setName(rawName); + } + lua_getfield(L, 2, "groups"); + if (!lua_isnil(L, -1)) { + if (lua_type(L, -1) == LUA_TTABLE) { + for (size_t i = 1; i <= lua_objlen(L, -1); ++i) { + lua_rawgeti(L, -1, boost::numeric_cast<int>(i)); + const char* rawGroup = lua_tostring(L, -1); + if (rawGroup) { + item.addGroup(rawGroup); + } + lua_pop(L, 1); + } + } + else { + throw Lua::Exception("Groups should be a table"); + } + } + } + else { + item.setJID(Lua::checkString(L, 2)); + } + + client->getRoster(); + if (!client->getClient()->getRoster()->containsJID(item.getJID())) { + RosterPayload::ref roster = boost::make_shared<RosterPayload>(); + roster->addItem(item); + + Sluift::Response response = client->sendVoidRequest( + SetRosterRequest::create(roster, client->getClient()->getIQRouter()), -1); + if (response.error) { + return response.convertToLuaResult(L); + } + } + client->getClient()->getSubscriptionManager()->requestSubscription(item.getJID()); + lua_pushboolean(L, true); + return 1; +} + +SLUIFT_LUA_FUNCTION_WITH_HELP( + Client, remove_contact, + "Remove a contact from the contact list.", + "self\n" + "jid the JID of the contact to remove\n", + "" +) { + Sluift::globals.eventLoop.runOnce(); + SluiftClient* client = getClient(L); + JID jid(Lua::checkString(L, 2)); + + RosterPayload::ref roster = boost::make_shared<RosterPayload>(); + roster->addItem(RosterItemPayload(JID(Lua::checkString(L, 2)), "", RosterItemPayload::Remove)); + + return client->sendVoidRequest( + SetRosterRequest::create(roster, client->getClient()->getIQRouter()), -1).convertToLuaResult(L); +} + +SLUIFT_LUA_FUNCTION_WITH_HELP( + Client, confirm_subscription, + "Confirm subscription of a contact.", + "self\n" + "jid the JID of the contact to confirm the subscription of\n", + "" +) { + Sluift::globals.eventLoop.runOnce(); + SluiftClient* client = getClient(L); + JID jid(Lua::checkString(L, 2)); + client->getClient()->getSubscriptionManager()->confirmSubscription(jid); + return 0; +} + +SLUIFT_LUA_FUNCTION_WITH_HELP( + Client, cancel_subscription, + "Cancel the subscription of a contact.", + "self\n" + "jid the JID of the contact to cancel the subscription of\n", + "" +) { + Sluift::globals.eventLoop.runOnce(); + SluiftClient* client = getClient(L); + JID jid(Lua::checkString(L, 2)); + client->getClient()->getSubscriptionManager()->cancelSubscription(jid); + return 0; +} + +SLUIFT_LUA_FUNCTION_WITH_HELP( + Client, set_disco_info, + "Sets the service discovery information for this client", + "self\n" + "disco_info A structured representation of the service discovery information\n", + "" +) { + SluiftClient* client = getClient(L); + if (!lua_istable(L, 2)) { + throw Lua::Exception("Missing disco info"); + } + if (boost::shared_ptr<DiscoInfo> discoInfo = boost::dynamic_pointer_cast<DiscoInfo>(Sluift::globals.elementConvertor.convertFromLuaUntyped(L, 2, "disco_info"))) { + client->getClient()->getDiscoManager()->setDiscoInfo(*discoInfo); + } + else { + throw Lua::Exception("Illegal disco info"); + } + return 0; +} + +SLUIFT_LUA_FUNCTION_WITH_HELP( + Client, set_caps_node, + "Sets the caps node of this client", + "self\n" + "node The caps node (e.g. 'http://swift.im/sluift')\n", + "" +) { + SluiftClient* client = getClient(L); + std::string node(Lua::checkString(L, 2)); + client->getClient()->getDiscoManager()->setCapsNode(Lua::checkString(L, 2)); + return 0; +} + +SLUIFT_LUA_FUNCTION_WITH_HELP( + Client, jid, + "Returns the JID of this client", + "self\n", + "" +) { + SluiftClient* client = getClient(L); + lua_pushstring(L, client->getClient()->getJID().toString().c_str()); + return 1; +} + +SLUIFT_LUA_FUNCTION(Client, __gc) { + SluiftClient* client = getClient(L); + delete client; + return 0; +} diff --git a/Sluift/component.cpp b/Sluift/component.cpp new file mode 100644 index 0000000..6ae78ec --- /dev/null +++ b/Sluift/component.cpp @@ -0,0 +1,472 @@ +/* + * Copyright (c) 2014 Kevin Smith and Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <boost/lambda/lambda.hpp> +#include <boost/lambda/bind.hpp> +#include <boost/assign/list_of.hpp> +#include <iostream> + +#include <Sluift/SluiftComponent.h> +#include <Swiften/JID/JID.h> +#include <Swiften/Elements/SoftwareVersion.h> +#include <Swiften/Elements/Message.h> +#include <Swiften/Elements/Presence.h> +#include <Swiften/Elements/RawXMLPayload.h> +#include <Swiften/Elements/RosterItemPayload.h> +#include <Swiften/Elements/RosterPayload.h> +#include <Swiften/Elements/DiscoInfo.h> +#include <Swiften/Elements/MAMQuery.h> +#include <Swiften/Queries/GenericRequest.h> +#include <Swiften/Presence/PresenceSender.h> +#include <Swiften/Roster/XMPPRoster.h> +#include <Swiften/Roster/SetRosterRequest.h> +#include <Swiften/Presence/SubscriptionManager.h> +#include <Swiften/Roster/XMPPRosterItem.h> +#include <Swiften/Queries/IQRouter.h> +#include <Swiften/Queries/Requests/GetSoftwareVersionRequest.h> +#include <Sluift/Lua/FunctionRegistration.h> +#include <Swiften/Base/foreach.h> +#include <Swiften/Base/IDGenerator.h> +#include <Sluift/Lua/Check.h> +#include <Sluift/Lua/Value.h> +#include <Sluift/Lua/Exception.h> +#include <Sluift/Lua/LuaUtils.h> +#include <Sluift/globals.h> +#include <Sluift/ElementConvertors/StanzaConvertor.h> +#include <Sluift/ElementConvertors/IQConvertor.h> +#include <Sluift/ElementConvertors/PresenceConvertor.h> +#include <Sluift/ElementConvertors/MessageConvertor.h> +#include <Sluift/ElementConvertors/StatusShowConvertor.h> + +using namespace Swift; +namespace lambda = boost::lambda; + +static inline SluiftComponent* getComponent(lua_State* L) { + return *Lua::checkUserData<SluiftComponent>(L, 1); +} + +static inline int getGlobalTimeout(lua_State* L) { + lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.moduleLibIndex); + lua_getfield(L, -1, "timeout"); + int result = boost::numeric_cast<int>(lua_tointeger(L, -1)); + lua_pop(L, 2); + return result; +} + +static void addPayloadsToTable(lua_State* L, const std::vector<boost::shared_ptr<Payload> >& payloads) { + if (!payloads.empty()) { + lua_createtable(L, boost::numeric_cast<int>(payloads.size()), 0); + for (size_t i = 0; i < payloads.size(); ++i) { + Sluift::globals.elementConvertor.convertToLua(L, payloads[i]); + lua_rawseti(L, -2, boost::numeric_cast<int>(i+1)); + } + Lua::registerGetByTypeIndex(L, -1); + lua_setfield(L, -2, "payloads"); + } +} + +static boost::shared_ptr<Payload> getPayload(lua_State* L, int index) { + if (lua_type(L, index) == LUA_TTABLE) { + return boost::dynamic_pointer_cast<Payload>(Sluift::globals.elementConvertor.convertFromLua(L, index)); + } + else if (lua_type(L, index) == LUA_TSTRING) { + return boost::make_shared<RawXMLPayload>(Lua::checkString(L, index)); + } + else { + return boost::shared_ptr<Payload>(); + } +} + +static std::vector< boost::shared_ptr<Payload> > getPayloadsFromTable(lua_State* L, int index) { + index = Lua::absoluteOffset(L, index); + std::vector< boost::shared_ptr<Payload> > result; + lua_getfield(L, index, "payloads"); + if (lua_istable(L, -1)) { + for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { + boost::shared_ptr<Payload> payload = getPayload(L, -1); + if (payload) { + result.push_back(payload); + } + } + } + lua_pop(L, 1); + return result; +} + +SLUIFT_LUA_FUNCTION(Component, async_connect) { + SluiftComponent* component = getComponent(L); + + std::string host; + int port = 0; + if (lua_istable(L, 2)) { + if (boost::optional<std::string> hostString = Lua::getStringField(L, 2, "host")) { + host = *hostString; + } + if (boost::optional<int> portInt = Lua::getIntField(L, 2, "port")) { + port = *portInt; + } + } + component->connect(host, port); + return 0; +} + +SLUIFT_LUA_FUNCTION_WITH_HELP( + Component, set_trace_enabled, + "Enable/disable tracing of the data sent/received.\n\n.", + "self\n" + "enable a boolean specifying whether to enable/disable tracing", + "" +) { + getComponent(L)->setTraceEnabled(lua_toboolean(L, 1)); + return 0; +} + +SLUIFT_LUA_FUNCTION_WITH_HELP( + Component, wait_connected, + "Block until the component is connected.\n\nThis is useful after an `async_connect`.", + "self", + "" +) { + getComponent(L)->waitConnected(getGlobalTimeout(L)); + return 0; +} + +SLUIFT_LUA_FUNCTION_WITH_HELP( + Component, is_connected, + "Checks whether this component is still connected.\n\nReturns a boolean.", + "self\n", + "" +) { + lua_pushboolean(L, getComponent(L)->isConnected()); + return 1; +} + +SLUIFT_LUA_FUNCTION_WITH_HELP( + Component, disconnect, + "Disconnect from the server", + "self\n", + "" +) { + Sluift::globals.eventLoop.runOnce(); + getComponent(L)->disconnect(); + return 0; +} + +SLUIFT_LUA_FUNCTION_WITH_HELP( + Component, set_version, + + "Sets the published version of this component.", + + "self", + + "name the name of the component software\n" + "version the version identifier of this component\n" + "os the OS this component is running on\n" +) { + Sluift::globals.eventLoop.runOnce(); + SluiftComponent* component = getComponent(L); + if (boost::shared_ptr<SoftwareVersion> version = boost::dynamic_pointer_cast<SoftwareVersion>(Sluift::globals.elementConvertor.convertFromLuaUntyped(L, 2, "software_version"))) { + component->setSoftwareVersion(version->getName(), version->getVersion(), version->getOS()); + } + return 0; +} + +SLUIFT_LUA_FUNCTION_WITH_HELP( + Component, send_message, + "Send a message.", + "self\n" + "to the JID to send the message to\n" + "body the body of the message. Can alternatively be specified using the `body` option\n", + + "to the JID to send the message to\n" + "body the body of the message\n" + "subject the subject of the MUC room to set\n" + "type the type of message to send (`normal`, `chat`, `error`, `groupchat`, `headline`)\n" + "payloads payloads to add to the message\n" +) { + Sluift::globals.eventLoop.runOnce(); + JID to; + boost::optional<std::string> from; + boost::optional<std::string> body; + boost::optional<std::string> subject; + std::vector<boost::shared_ptr<Payload> > payloads; + int index = 2; + Message::Type type = Message::Chat; + if (lua_isstring(L, index)) { + to = std::string(lua_tostring(L, index)); + ++index; + if (lua_isstring(L, index)) { + body = lua_tostring(L, index); + ++index; + } + } + if (lua_istable(L, index)) { + if (boost::optional<std::string> value = Lua::getStringField(L, index, "to")) { + to = *value; + } + + if (boost::optional<std::string> value = Lua::getStringField(L, index, "from")) { + from = value; + } + + if (boost::optional<std::string> value = Lua::getStringField(L, index, "body")) { + body = value; + } + + if (boost::optional<std::string> value = Lua::getStringField(L, index, "type")) { + type = MessageConvertor::convertMessageTypeFromString(*value); + } + + if (boost::optional<std::string> value = Lua::getStringField(L, index, "subject")) { + subject = value; + } + + payloads = getPayloadsFromTable(L, index); + } + + if (!to.isValid()) { + throw Lua::Exception("Missing 'to'"); + } + if ((!body || body->empty()) && !subject && payloads.empty()) { + throw Lua::Exception("Missing any of 'body', 'subject' or 'payloads'"); + } + Message::ref message = boost::make_shared<Message>(); + message->setTo(to); + if (from && !from->empty()) { + message->setFrom(*from); + } + if (body && !body->empty()) { + message->setBody(*body); + } + if (subject) { + message->setSubject(*subject); + } + message->addPayloads(payloads.begin(), payloads.end()); + message->setType(type); + getComponent(L)->getComponent()->sendMessage(message); + return 0; +} + +SLUIFT_LUA_FUNCTION_WITH_HELP( + Component, send_presence, + "Send presence.", + + "self\n" + "body the text of the presence. Can alternatively be specified using the `status` option\n", + + "to the JID to send the message to\n" + "from the JID to send the message from\n" + "status the text of the presence\n" + "show the availability of the presence (`online`, `ffc`, `away`, `xa`, `dnd`)\n" + "priority the priority of the presence\n" + "type the type of message to send (`available`, `error`, `probe`, `subscribe`, `subscribed`, `unavailable`, `unsubscribe`, `unsubscribed`)\n" + "payloads payloads to add to the presence\n" +) { + Sluift::globals.eventLoop.runOnce(); + boost::shared_ptr<Presence> presence = boost::make_shared<Presence>(); + + int index = 2; + if (lua_isstring(L, index)) { + presence->setStatus(lua_tostring(L, index)); + ++index; + } + if (lua_istable(L, index)) { + if (boost::optional<std::string> value = Lua::getStringField(L, index, "to")) { + presence->setTo(*value); + } + if (boost::optional<std::string> value = Lua::getStringField(L, index, "from")) { + presence->setFrom(*value); + } + if (boost::optional<std::string> value = Lua::getStringField(L, index, "status")) { + presence->setStatus(*value); + } + if (boost::optional<int> value = Lua::getIntField(L, index, "priority")) { + presence->setPriority(*value); + } + if (boost::optional<std::string> value = Lua::getStringField(L, index, "type")) { + presence->setType(PresenceConvertor::convertPresenceTypeFromString(*value)); + } + if (boost::optional<std::string> value = Lua::getStringField(L, index, "show")) { + presence->setShow(StatusShowConvertor::convertStatusShowTypeFromString(*value)); + } + std::vector< boost::shared_ptr<Payload> > payloads = getPayloadsFromTable(L, index); + presence->addPayloads(payloads.begin(), payloads.end()); + } + + getComponent(L)->getComponent()->sendPresence(presence); + lua_pushvalue(L, 1); + return 0; +} + +static int sendQuery(lua_State* L, IQ::Type type) { + SluiftComponent* component = getComponent(L); + + JID to; + if (boost::optional<std::string> toString = Lua::getStringField(L, 2, "to")) { + to = JID(*toString); + } + + JID from; + if (boost::optional<std::string> fromString = Lua::getStringField(L, 2, "from")) { + from = JID(*fromString); + } + + int timeout = getGlobalTimeout(L); + if (boost::optional<int> timeoutInt = Lua::getIntField(L, 2, "timeout")) { + timeout = *timeoutInt; + } + + boost::shared_ptr<Payload> payload; + lua_getfield(L, 2, "query"); + payload = getPayload(L, -1); + lua_pop(L, 1); + + return component->sendRequest( + boost::make_shared< GenericRequest<Payload> >(type, from, to, payload, component->getComponent()->getIQRouter()), timeout).convertToLuaResult(L); +} + +#define DISPATCH_PUBSUB_PAYLOAD(payloadType, container, response) \ + else if (boost::shared_ptr<payloadType> p = boost::dynamic_pointer_cast<payloadType>(payload)) { \ + return component->sendPubSubRequest(type, to, p, timeout).convertToLuaResult(L); \ + } + +SLUIFT_LUA_FUNCTION(Component, get) { + return sendQuery(L, IQ::Get); +} + +SLUIFT_LUA_FUNCTION(Component, set) { + return sendQuery(L, IQ::Set); +} + +SLUIFT_LUA_FUNCTION_WITH_HELP( + Component, send, + "Sends a raw string", + + "self\n" + "data the string to send\n", + + "" +) { + Sluift::globals.eventLoop.runOnce(); + + getComponent(L)->getComponent()->sendData(std::string(Lua::checkString(L, 2))); + lua_pushvalue(L, 1); + return 0; +} + +static void pushEvent(lua_State* L, const SluiftComponent::Event& event) { + switch (event.type) { + case SluiftComponent::Event::MessageType: { + Message::ref message = boost::dynamic_pointer_cast<Message>(event.stanza); + Lua::Table result = boost::assign::map_list_of + ("type", boost::make_shared<Lua::Value>(std::string("message"))) + ("from", boost::make_shared<Lua::Value>(message->getFrom().toString())) + ("to", boost::make_shared<Lua::Value>(message->getTo().toString())) + ("body", boost::make_shared<Lua::Value>(message->getBody())) + ("message_type", boost::make_shared<Lua::Value>(MessageConvertor::convertMessageTypeToString(message->getType()))); + Lua::pushValue(L, result); + addPayloadsToTable(L, message->getPayloads()); + Lua::registerTableToString(L, -1); + break; + } + case SluiftComponent::Event::PresenceType: { + Presence::ref presence = boost::dynamic_pointer_cast<Presence>(event.stanza); + Lua::Table result = boost::assign::map_list_of + ("type", boost::make_shared<Lua::Value>(std::string("presence"))) + ("from", boost::make_shared<Lua::Value>(presence->getFrom().toString())) + ("to", boost::make_shared<Lua::Value>(presence->getTo().toString())) + ("status", boost::make_shared<Lua::Value>(presence->getStatus())) + ("presence_type", boost::make_shared<Lua::Value>(PresenceConvertor::convertPresenceTypeToString(presence->getType()))); + Lua::pushValue(L, result); + addPayloadsToTable(L, presence->getPayloads()); + Lua::registerTableToString(L, -1); + break; + } + } +} + +struct CallUnaryLuaPredicateOnEvent { + CallUnaryLuaPredicateOnEvent(lua_State* L, int index) : L(L), index(index) { + } + + bool operator()(const SluiftComponent::Event& event) { + lua_pushvalue(L, index); + pushEvent(L, event); + if (lua_pcall(L, 1, 1, 0) != 0) { + throw Lua::Exception(lua_tostring(L, -1)); + } + bool result = lua_toboolean(L, -1); + lua_pop(L, 1); + return result; + } + + lua_State* L; + int index; +}; + + +SLUIFT_LUA_FUNCTION(Component, get_next_event) { + Sluift::globals.eventLoop.runOnce(); + SluiftComponent* component = getComponent(L); + + int timeout = getGlobalTimeout(L); + boost::optional<SluiftComponent::Event::Type> type; + int condition = 0; + if (lua_istable(L, 2)) { + if (boost::optional<std::string> typeString = Lua::getStringField(L, 2, "type")) { + if (*typeString == "message") { + type = SluiftComponent::Event::MessageType; + } + else if (*typeString == "presence") { + type = SluiftComponent::Event::PresenceType; + } + } + if (boost::optional<int> timeoutInt = Lua::getIntField(L, 2, "timeout")) { + timeout = *timeoutInt; + } + lua_getfield(L, 2, "if"); + if (lua_isfunction(L, -1)) { + condition = Lua::absoluteOffset(L, -1); + } + } + + boost::optional<SluiftComponent::Event> event; + if (condition) { + event = component->getNextEvent(timeout, CallUnaryLuaPredicateOnEvent(L, condition)); + } + else if (type) { + event = component->getNextEvent( + timeout, lambda::bind(&SluiftComponent::Event::type, lambda::_1) == *type); + } + else { + event = component->getNextEvent(timeout); + } + + if (event) { + pushEvent(L, *event); + } + else { + lua_pushnil(L); + } + return 1; +} + +SLUIFT_LUA_FUNCTION_WITH_HELP( + Component, jid, + "Returns the JID of this component", + "self\n", + "" +) { + SluiftComponent* component = getComponent(L); + lua_pushstring(L, component->getComponent()->getJID().toString().c_str()); + return 1; +} + +SLUIFT_LUA_FUNCTION(Component, __gc) { + SluiftComponent* component = getComponent(L); + delete component; + return 0; +} diff --git a/Sluift/core.lua b/Sluift/core.lua new file mode 100644 index 0000000..ffbb5f9 --- /dev/null +++ b/Sluift/core.lua @@ -0,0 +1,1243 @@ +--[[ + Copyright (c) 2013-2014 Remko Tronçon + Licensed under the GNU General Public License. + See the COPYING file for more information. +--]] + +local sluift = select(1, ...) +local _G = _G +local pairs, ipairs, print, tostring, type, error, assert, next, rawset, xpcall, unpack, io = pairs, ipairs, print, tostring, type, error, assert, next, rawset, xpcall, unpack, io +local setmetatable, getmetatable = setmetatable, getmetatable +local string = require "string" +local table = require "table" +local debug = require "debug" +_ENV = nil + +-------------------------------------------------------------------------------- +-- Table utility methods +-------------------------------------------------------------------------------- + +local function table_value_tostring(value) + local result = tostring(value) + if type(value) == 'number' then return result + elseif type(value) == 'boolean' then return result + elseif type(value) == 'string' then return "'" .. result .. "'" + else return '<' .. result .. '>' + end +end + +local function table_tostring(table, print_functions, indent, accumulator, history) + local INDENT = ' ' + local accumulator = accumulator or '' + local history = history or {} + local indent = indent or '' + accumulator = accumulator .. '{' + history[table] = true + local is_first = true + for key, value in pairs(table) do + if print_functions or type(value) ~= 'function' then + if not is_first then + accumulator = accumulator .. ',' + end + is_first = false + accumulator = accumulator .. '\n' .. indent .. INDENT .. '[' .. table_value_tostring(key) .. '] = ' + if type(value) == 'table' then + if history[value] then + accumulator = accumulator .. "..." + else + accumulator = table_tostring(value, print_functions, indent .. INDENT, accumulator, history) + end + else + accumulator = accumulator .. table_value_tostring(value) + end + end + end + history[table] = false + if not is_first then + accumulator = accumulator .. '\n' .. indent + end + accumulator = accumulator .. '}' + return accumulator +end + +local function register_table_tostring(table, print_functions) + if type(table) == 'table' then + local metatable = getmetatable(table) + if not metatable then + metatable = {} + setmetatable(table, metatable) + end + if print_functions then + metatable.__tostring = function(table) return table_tostring(table, true) end + else + metatable.__tostring = table_tostring + end + end +end + +-- FIXME: Not really a good or efficiant equals, but does the trick for now +local function table_equals(t1, t2) + return tostring(t1) == tostring(t2) +end + +local function register_table_equals(table) + if type(table) == 'table' then + local metatable = getmetatable(table) + if not metatable then + metatable = {} + setmetatable(table, metatable) + end + metatable.__eq = table_equals + end +end + +local function merge_tables(...) + local result = {} + for _, table in ipairs({...}) do + for k, v in pairs(table) do + result[k] = v + end + end + return result +end + +local function copy(object) + if type(object) == 'table' then + local copy = {} + for key, value in pairs(object) do + copy[key] = value + end + return copy + else + return object + end +end + +local function clear(table) + setmetatable(table, nil) + for key, value in pairs(table) do + rawset(table, key, nil) + end +end + +local function trim(string) + return string:gsub("^%s*(.-)%s*$", "%1") +end + +local function keys(table) + local result = {} + for key in pairs(table) do + result[#result+1] = key + end + return result +end + +local function insert_all(table, values) + for _, value in pairs(values) do + table[#table+1] = value + end +end + +-------------------------------------------------------------------------------- +-- Help +-------------------------------------------------------------------------------- + +-- Contains help for native methods that we want access to from here +local extra_help = {} +local component_extra_help = {} +local help_data = {} +local help_classes = {} +local help_class_metatables = {} + +local _H + +local function get_synopsis(description) + return description:gsub("[\n\r].*", "") +end + +local function format_description(text) + local result = {} + local trim_whitespace + for line in (text .. "\n"):gmatch"(.-)\n" do + if not trim_whitespace and line:find('[^%s]') then + trim_whitespace = line:match("^(%s*)") + end + if trim_whitespace then + line = line:gsub("^" .. trim_whitespace, "") + end + table.insert(result, line) + end + return trim(table.concat(result, "\n")) +end + +local function strip_links(text) + return text:gsub("(@{(%w*)})", "`%2`") +end + +local function register_help(target, help) + assert(target) + if not help then + help = _H + end + assert(help) + + -- Transform description into canonical representation + local parameters = {} + for _, parameter in pairs(help.parameters or {}) do + local parameter_description = parameter[2] + if parameter_description and #parameter_description == 0 then + parameter_description = nil + end + if type(parameter) == "table" then + parameters[#parameters+1] = { name = parameter[1], description = parameter_description } + else + parameters[#parameters+1] = { name = parameter } + end + end + local options = {} + for option_name, option_description in pairs(help.options or {}) do + if type(option_description) == "table" then + options[#options+1] = { name = option_description.name, description = option_description.description } + else + options[#options+1] = { name = option_name, description = option_description } + end + end + local description = format_description(help[1] or help.description or "") + local synopsis = get_synopsis(description) + if #description == 0 then + synopsis = nil + description = nil + end + local data = { + description = description, + synopsis = synopsis, + parameters = parameters, + options = options, + classes = help.classes + } + register_table_tostring(data, true) + help_data[target] = data +end + +local function register_class_help(class, help) + help_classes[#help_classes+1] = class + register_help(class, help) +end + +local function register_class_table_help(target, class, help) + register_help(target, help) + help_class_metatables[class] = target + register_class_help(class, help) +end + +_H = { + [[ + Retrieves the help information from `target`. + + Returns a table with the following fields: + + - `description`: the description of `target` + - `parameters`: an array of parameters of `target` represented as tables with `name` and `description` fields. + - `options`: an array of options (named parameters) of `target` represented as tables with `name` and + `description` fields. + - `methods`: an array of methods + - `fields`: an array of fields + ]], + parameters = { {"target", "The target to retrieve help of"} } +} +local function get_help(target) + if not target then error("Nil argument or argument missing") end + local help = help_data[target] or help_data[getmetatable(target)] or {} + + -- Collect child methods and fields + local children = {} + if type(target) == "table" then children = target end + local mt + if type(target) == "string" then + mt = help_class_metatables[target] + else + mt = getmetatable(target) + end + if mt and type(mt.__index) == "table" then + children = merge_tables(children, mt.__index) + end + + local methods = {} + local fields = {} + for name, value in pairs(children) do + if name:sub(1, 1) ~= "_" then + if type(value) == "function" then + methods[#methods+1] = { name = name, ref = value } + else + fields[#fields+1] = { name = name, description = nil } + end + end + end + if next(methods) ~= nil then + help.methods = methods + end + if next(fields) ~= nil then + help.fields = fields + end + if next(help) then + return help + else + return nil + end +end +register_help(get_help) + +_H = { + [[ + Prints the help of `target`. + + `target` can be any object. When `target` is a string, prints the help of the class with + the given name. + ]], + parameters = { {"target", "The target to retrieve help of"} } +} +local function help(target) + print() + if not target then + print("Call `help(target)` to get the help of a specific `target`.") + print("`target` can be any object. When `target` is a string, prints") + print("the help of the class with the given name.") + print() + print("For general information about sluift, type:") + print(" help(sluift)") + print() + return + end + local data = get_help(target) + if not data then + print("No help available\n") + return + end + + -- Collect help of children + local methods = {} + for _, method in pairs(data.methods or {}) do + local description + local method_help = get_help(method.ref) + if method_help and method_help.description then + description = method_help.synopsis + end + methods[#methods+1] = { name = method.name, description = description } + end + local fields = copy(data.fields or {}) + + table.sort(methods, function (a, b) return (a.name or "") < (b.name or "") end) + table.sort(fields, function (a, b) return (a.name or "") < (b.name or "") end) + + local classes = {} + for _, class in pairs(data.classes or {}) do + classes[#classes+1] = { name = class, description = get_help(class).synopsis } + end + + print(strip_links(data.description) or "(No description available)") + for _, p in ipairs({ + {"Parameters", data.parameters}, {"Options", data.options}, {"Methods", methods}, {"Fields", fields}, {"Classes", classes}}) do + if p[2] and next(p[2]) ~= nil then + print() + print(p[1] .. ":") + for _, parameter in ipairs(p[2]) do + if parameter.description then + print(" " .. parameter.name .. ": " .. strip_links(parameter.description)) + else + print(" " .. parameter.name) + end + end + end + end + + print() +end +register_help(help) + +-------------------------------------------------------------------------------- +-- Utility methods +-------------------------------------------------------------------------------- + +_H = { + [[ Perform a shallow copy of `object`. ]], + parameters = {{"object", "the object to copy"}} +} +register_help(copy) + +_H = { + [[ Pretty-print a table ]], + parameters = {{"table", "the table to print"}} +} +local function tprint(table) + print(table_tostring(table, true)) +end +register_help(tprint) + +local function remove_help_parameters(elements, table) + if type(elements) ~= "table" then + elements = {elements} + end + local result = copy(table) + for k, v in ipairs(table) do + for _, element in ipairs(elements) do + if v.name == element then + result[k] = nil + end + end + end + return result +end + +local function parse_options(unnamed_parameters, arg1, arg2) + local options = {} + local f + if type(arg1) == 'table' then + options = arg1 + f = arg2 + elseif type(arg1) == 'function' then + f = arg1 + end + options.f = f or options.f + return copy(options) +end + + +local function get_by_type(table, typ) + for _, v in ipairs(table) do + if v['_type'] == typ then + return v + end + end +end + +local function register_get_by_type_index(table) + if type(table) == 'table' then + local metatable = getmetatable(table) + if not metatable then + metatable = {} + setmetatable(table, metatable) + end + metatable.__index = get_by_type + end + return table +end + +local function call(options) + local f = options[1] + local result = { xpcall(f, debug.traceback) } + if options.finally then + options.finally() + end + if result[1] then + table.remove(result, 1) + return unpack(result) + else + error(result[2]) + end +end + +local function read_file(file) + local f = io.open(file, 'rb') + local result = f:read('*all') + f:close() + return result +end + +_H = { + [[ Generate a form table, suitable for PubSubConfiguration and MAMQuery ]], + parameters = { {"fields", "The fields that will be converted into a form table"}, + {"form_type", "If specified, add a form_type field with this value"}, + {"type", "Form type, e.g. 'submit'"} } +} +local function create_form(...) + local options = parse_options({}, ...) + local result = { fields = {} } + -- FIXME: make nicer when parse_options binds positional arguments to names + if options.fields then + for var, value in pairs(options.fields) do + result.fields[#result.fields+1] = { name = var, value = value } + end + elseif options[1] then + for var, value in pairs(options[1]) do + result.fields[#result.fields+1] = { name = var, value = value } + end + end + if options.form_type then + result.fields[#result.fields+1] = { name = 'FORM_TYPE', value = options.form_type } + end + result['type'] = options.type + return result +end + +-------------------------------------------------------------------------------- +-- Metatables +-------------------------------------------------------------------------------- + +_H = { + [[ Client interface ]] +} +local Client = { + _with_prompt = function(client) return client:jid() end +} +Client.__index = Client +register_class_table_help(Client, "Client") + +_H = { + [[ Component interface ]] +} +local Component = { + _with_prompt = function(component) return component:jid() end +} +Component.__index = Component +register_class_table_help(Component, "Component") + + +_H = { + [[ Interface to communicate with a PubSub service ]] +} +local PubSub = {} +PubSub.__index = PubSub +register_class_table_help(PubSub, "PubSub") + +_H = { + [[ Interface to communicate with a PubSub node on a service ]] +} +local PubSubNode = {} +PubSubNode.__index = PubSubNode +register_class_table_help(PubSubNode, "PubSubNode") + + +-------------------------------------------------------------------------------- +-- with +-------------------------------------------------------------------------------- + +local original_G + +local function with (target, f) + -- Dynamic scope + if f then + with(target) + return call{f, finally = function() with() end} + end + + -- No scope + if target then + if not original_G then + original_G = copy(_G) + setmetatable(original_G, getmetatable(_G)) + clear(_G) + end + + setmetatable(_G, { + __index = function(_, key) + local value = target[key] + if value then + if type(value) == 'function' then + -- Add 'self' argument to all functions + return function(...) return value(target, ...) end + else + return value + end + else + return original_G[key] + end + end, + __newindex = original_G, + _completions = function () + local result = {} + if type(target) == "table" then + insert_all(result, keys(target)) + end + local mt = getmetatable(target) + if mt and type(mt.__index) == 'table' then + insert_all(result, keys(mt.__index)) + end + insert_all(result, keys(original_G)) + return result + end + }) + + -- Set prompt + local prompt = nil + + -- Try '_with_prompt' in metatable + local target_metatable = getmetatable(target) + if target_metatable then + if type(target_metatable._with_prompt) == "function" then + prompt = target_metatable._with_prompt(target) + else + prompt = target_metatable._with_prompt + end + end + + if not prompt then + -- Use tostring() + local target_string = tostring(target) + if string.len(target_string) > 25 then + prompt = string.sub(target_string, 0, 22) .. "..." + else + prompt = target_string + end + end + rawset(_G, "_PROMPT", prompt .. '> ') + else + -- Reset _G + clear(_G) + for key, value in pairs(original_G) do + _G[key] = value + end + setmetatable(_G, original_G) + end +end + +-------------------------------------------------------------------------------- +-- Client +-------------------------------------------------------------------------------- + +extra_help = { + ["Client.get_next_event"] = { + [[ Returns the next event. ]], + parameters = { "self" }, + options = { + type = "The type of event to return (`message`, `presence`, `pubsub`). When omitted, all event types are returned.", + timeout = "The amount of time to wait for events.", + ["if"] = "A function to filter events. When this function, called with the event as a parameter, returns true, the event will be returned" + } + }, + ["Client.get"] = { + [[ Sends a `get` query. ]], + parameters = { "self" }, + options = { + to = "The JID of the target to send the query to", + query = "The query to send", + timeout = "The amount of time to wait for the query to finish", + } + }, + ["Client.set"] = { + [[ Sends a `set` query. ]], + parameters = { "self" }, + options = { + to = "The JID of the target to send the query to", + query = "The query to send.", + timeout = "The amount of time to wait for the query to finish.", + } + }, + ["Client.async_connect"] = { + [[ + Connect to the server asynchronously. + + This method immediately returns. + ]], + parameters = { "self" }, + options = { + host = "The host to connect to. When omitted, is determined by resolving the client JID.", + port = "The port to connect to. When omitted, is determined by resolving the client JID." + } + } +} + +_H = { + [[ + Connect to the server. + + This method blocks until the connection has been established. + ]], + parameters = { "self" }, + options = extra_help["Client.async_connect"].options +} +function Client:connect (...) + local options = parse_options({}, ...) + local f = options.f + self:async_connect(options) + self:wait_connected() + if f then + return call {function() return f(self) end, finally = function() self:disconnect() end} + end + return true +end +register_help(Client.connect) + + +_H = { + [[ + Returns an iterator over all events. + + This function blocks until `timeout` is reached (or blocks forever if it is omitted). + ]], + parameters = { "self" }, + options = extra_help["Client.get_next_event"].options +} +function Client:events (options) + local function client_events_iterator(s) + return s['client']:get_next_event(s['options']) + end + return client_events_iterator, {client = self, options = options} +end +register_help(Client.events) + + +_H = { + [[ + Calls `f` for each event. + ]], + parameters = { "self" }, + options = merge_tables(get_help(Client.events).options, { + f = "The functor to call with each event. Required." + }) +} +function Client:for_each_event (...) + local options = parse_options({}, ...) + if not type(options.f) == 'function' then error('Expected function') end + for event in self:events(options) do + local result = options.f(event) + if result then + return result + end + end +end +register_help(Client.for_each_event) + +for method, event_type in pairs({message = 'message', presence = 'presence', pubsub_event = 'pubsub'}) do + _H = { + "Call `f` for all events of type `" .. event_type .. "`.", + parameters = { "self" }, + options = remove_help_parameters("type", get_help(Client.for_each_event).options) + } + Client['for_each_' .. method] = function (client, ...) + local options = parse_options({}, ...) + options['type'] = event_type + return client:for_each_event (options) + end + register_help(Client['for_each_' .. method]) + + _H = { + "Get the next event of type `" .. event_type .. "`.", + parameters = { "self" }, + options = remove_help_parameters("type", extra_help["Client.get_next_event"].options) + } + Client['get_next_' .. method] = function (client, ...) + local options = parse_options({}, ...) + options['type'] = event_type + return client:get_next_event(options) + end + register_help(Client['get_next_' .. method]) +end + +for method, event_type in pairs({messages = 'message', pubsub_events = 'pubsub'}) do + _H = { + "Returns an iterator over all events of type `" .. event_type .. "`.", + parameters = { "self" }, + options = remove_help_parameters("type", get_help(Client.for_each_event).options) + } + Client[method] = function (client, ...) + local options = parse_options({}, ...) + options['type'] = event_type + return client:events (options) + end + register_help(Client[method]) +end + +_H = { + [[ + Process all pending events + ]], + parameters = { "self" } +} +function Client:process_events () + for event in self:events{timeout=0} do end +end +register_help(Client.process_events) + + +-- +-- 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', 'mam'}, + set = {'command', 'mam'} +} +for query_action, query_types in pairs(get_set_shortcuts) do + for _, query_type in ipairs(query_types) do + _H = { + "Sends a `" .. query_action .. "` query of type `" .. query_type .. "`.\n" .. + "Apart from the options below, all top level elements of `" .. query_type .. "` can be passed.", + parameters = { "self" }, + options = remove_help_parameters({"query", "type"}, extra_help["Client.get"].options), + } + local method = query_action .. '_' .. query_type + Client[method] = 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 + register_help(Client[method]) + end +end + +_H = { + [[ Returns a @{PubSub} object for communicating with the PubSub service at `jid`. ]], + parameters = { + "self", + {"jid", "The JID of the PubSub service"} + } +} +function Client:pubsub (jid) + local result = { client = self, jid = jid } + setmetatable(result, PubSub) + return result +end +register_help(Client.pubsub) + + +-------------------------------------------------------------------------------- +-- Component +-------------------------------------------------------------------------------- + +component_extra_help = { + ["Component.get_next_event"] = { + [[ Returns the next event. ]], + parameters = { "self" }, + options = { + type = "The type of event to return (`message`, `presence`). When omitted, all event types are returned.", + timeout = "The amount of time to wait for events.", + ["if"] = "A function to filter events. When this function, called with the event as a parameter, returns true, the event will be returned" + } + }, + ["Component.get"] = { + [[ Sends a `get` query. ]], + parameters = { "self" }, + options = { + to = "The JID of the target to send the query to", + query = "The query to send", + timeout = "The amount of time to wait for the query to finish", + } + }, + ["Component.set"] = { + [[ Sends a `set` query. ]], + parameters = { "self" }, + options = { + to = "The JID of the target to send the query to", + query = "The query to send.", + timeout = "The amount of time to wait for the query to finish.", + } + }, + ["Component.async_connect"] = { + [[ + Connect to the server asynchronously. + + This method immediately returns. + ]], + parameters = { "self" }, + options = { + host = "The host to connect to.", + port = "The port to connect to." + } + } +} + +_H = { + [[ + Connect to the server. + + This method blocks until the connection has been established. + ]], + parameters = { "self" }, + options = component_extra_help["Component.async_connect"].options +} +function Component:connect (...) + local options = parse_options({}, ...) + local f = options.f + self:async_connect(options) + self:wait_connected() + if f then + return call {function() return f(self) end, finally = function() self:disconnect() end} + end + return true +end +register_help(Component.connect) + + +_H = { + [[ + Returns an iterator over all events. + + This function blocks until `timeout` is reached (or blocks forever if it is omitted). + ]], + parameters = { "self" }, + options = component_extra_help["Component.get_next_event"].options +} +function Component:events (options) + local function component_events_iterator(s) + return s['component']:get_next_event(s['options']) + end + return component_events_iterator, {component = self, options = options} +end +register_help(Component.events) + + +_H = { + [[ + Calls `f` for each event. + ]], + parameters = { "self" }, + options = merge_tables(get_help(Component.events).options, { + f = "The functor to call with each event. Required." + }) +} +function Component:for_each_event (...) + local options = parse_options({}, ...) + if not type(options.f) == 'function' then error('Expected function') end + for event in self:events(options) do + local result = options.f(event) + if result then + return result + end + end +end +register_help(Component.for_each_event) + +for method, event_type in pairs({message = 'message', presence = 'presence'}) do + _H = { + "Call `f` for all events of type `" .. event_type .. "`.", + parameters = { "self" }, + options = remove_help_parameters("type", get_help(Component.for_each_event).options) + } + Component['for_each_' .. method] = function (component, ...) + local options = parse_options({}, ...) + options['type'] = event_type + return component:for_each_event (options) + end + register_help(Component['for_each_' .. method]) + + _H = { + "Get the next event of type `" .. event_type .. "`.", + parameters = { "self" }, + options = remove_help_parameters("type", component_extra_help["Component.get_next_event"].options) + } + Component['get_next_' .. method] = function (component, ...) + local options = parse_options({}, ...) + options['type'] = event_type + return component:get_next_event(options) + end + register_help(Component['get_next_' .. method]) +end + +for method, event_type in pairs({messages = 'message'}) do + _H = { + "Returns an iterator over all events of type `" .. event_type .. "`.", + parameters = { "self" }, + options = remove_help_parameters("type", get_help(Component.for_each_event).options) + } + Component[method] = function (component, ...) + local options = parse_options({}, ...) + options['type'] = event_type + return component:events (options) + end + register_help(Component[method]) +end + +_H = { + [[ + Process all pending events + ]], + parameters = { "self" } +} +function Component:process_events () + for event in self:events{timeout=0} do end +end +register_help(Component.process_events) + + +-- +-- Register get_* and set_* convenience methods for some type of queries +-- +-- Example usages: +-- component:get_software_version{to = 'alice@wonderland.lit'} +-- component: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 + _H = { + "Sends a `" .. query_action .. "` query of type `" .. query_type .. "`.\n" .. + "Apart from the options below, all top level elements of `" .. query_type .. "` can be passed.", + parameters = { "self" }, + options = remove_help_parameters({"query", "type"}, component_extra_help["Component.get"].options), + } + local method = query_action .. '_' .. query_type + Component[method] = function (component, 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 component[query_action](component, options) + end + register_help(Component[method]) + end +end + +-------------------------------------------------------------------------------- +-- PubSub +-------------------------------------------------------------------------------- + +local function process_pubsub_event (event) + if event._type == 'pubsub_event_items' then + -- Add 'item' shortcut to payload of first item + event.item = event.items and event.items[1] and + event.items[1].data and event.items[1].data[1] + end +end + +function PubSub:list_nodes (options) + return self.client:get_disco_items(merge_tables({to = self.jid}, options)) +end + +function PubSub:node (node) + local result = { client = self.client, jid = self.jid, node = node } + setmetatable(result, PubSubNode) + return result +end + +local simple_pubsub_queries = { + get_default_configuration = 'pubsub_owner_default', + get_subscriptions = 'pubsub_subscriptions', + get_affiliations = 'pubsub_affiliations', + get_default_subscription_options = 'pubsub_default', +} +for method, query_type in pairs(simple_pubsub_queries) do + PubSub[method] = function (service, options) + options = options or {} + return service.client:query_pubsub(merge_tables( + { type = 'get', to = service.jid, query = { _type = query_type } }, + options)) + end +end + +for _, method in ipairs({'events', 'get_next_event', 'for_each_event'}) do + PubSub[method] = function (node, ...) + local options = parse_options({}, ...) + options['if'] = function (event) + return event.type == 'pubsub' and event.from == node.jid and event.node == node + end + return node.client[method](node.client, options) + end +end + +-------------------------------------------------------------------------------- +-- PubSubNode +-------------------------------------------------------------------------------- + +local function pubsub_node_configuration_to_form(configuration) + return create_form{configuration, form_type="http://jabber.org/protocol/pubsub#node_config", type="submit"} +end + +function PubSubNode:list_items (options) + return self.client:get_disco_items(merge_tables({to = self.jid, disco_items = { node = self.node }}, options)) +end + +local simple_pubsub_node_queries = { + get_configuration = 'pubsub_owner_configure', + get_subscriptions = 'pubsub_subscriptions', + get_affiliations = 'pubsub_affiliations', + get_owner_subscriptions = 'pubsub_owner_subscriptions', + get_owner_affiliations = 'pubsub_owner_affiliations', + get_default_subscription_options = 'pubsub_default', +} +for method, query_type in pairs(simple_pubsub_node_queries) do + PubSubNode[method] = function (node, options) + return node.client:query_pubsub(merge_tables({ + type = 'get', to = node.jid, query = { + _type = query_type, node = node.node + }}, options)) + end +end + +function PubSubNode:get_items (...) + local options = parse_options({}, ...) + local items = options.items or {} + if options.maximum_items then + items = merge_tables({maximum_items = options.maximum_items}, items) + end + items = merge_tables({_type = 'pubsub_items', node = self.node}, items) + return self.client:query_pubsub(merge_tables({ + type = 'get', to = self.jid, query = items}, options)) +end + +function PubSubNode:get_item (...) + local options = parse_options({}, ...) + if not type(options.id) == 'string' then error('Expected ID') end + return self:get_items{items = {{id = options.id}}} +end + +function PubSubNode:create (options) + options = options or {} + local configure + if options['configuration'] then + configure = { data = pubsub_node_configuration_to_form(options['configuration']) } + end + return self.client:query_pubsub(merge_tables( + { type = 'set', to = self.jid, query = { + _type = 'pubsub_create', node = self.node, configure = configure } + }, options)) +end + +function PubSubNode:delete (options) + options = options or {} + local redirect + if options['redirect'] then + redirect = {uri = options['redirect']} + end + return self.client:query_pubsub(merge_tables({ type = 'set', to = self.jid, query = { + _type = 'pubsub_owner_delete', node = self.node, redirect = redirect + }}, options)) +end + +function PubSubNode:set_configuration(options) + options = options or {} + local configuration = pubsub_node_configuration_to_form(options['configuration']) + return self.client:query_pubsub(merge_tables( + { type = 'set', to = self.jid, query = { + _type = 'pubsub_owner_configure', node = self.node, data = configuration } + }, options)) +end + +function PubSubNode:set_owner_affiliations(...) + local options = parse_options({}, ...) + return self.client:query_pubsub(merge_tables({ + type = 'set', to = self.jid, query = merge_tables({ + _type = 'pubsub_owner_affiliations', node = self.node, + }, options.affiliations)}, options)) +end + + +function PubSubNode:subscribe(...) + local options = parse_options({}, ...) + local jid = options.jid or sluift.jid.to_bare(self.client:jid()) + return self.client:query_pubsub(merge_tables( + { type = 'set', to = self.jid, query = { + _type = 'pubsub_subscribe', node = self.node, jid = jid } + }, options)) +end + +function PubSubNode:unsubscribe(options) + options = options or {} + return self.client:query_pubsub(merge_tables( + { type = 'set', to = self.jid, query = { + _type = 'pubsub_unsubscribe', node = self.node, jid = options['jid'], + subscription_id = 'subscription_id'} + }, options)) +end + +function PubSubNode:get_subscription_options (options) + return self.client:query_pubsub(merge_tables( + { type = 'get', to = self.jid, query = { + _type = 'pubsub_options', node = self.node, jid = options['jid'] } + }, options)) +end + +function PubSubNode:publish(...) + local options = parse_options({}, ...) + local items = options.items or {} + if options.item then + if type(options.item) == 'string' or options.item._type then + items = {{id = options.id, data = { options.item } }} + options.id = nil + else + items = { options.item } + end + options.item = nil + end + return self.client:query_pubsub(merge_tables( + { type = 'set', to = self.jid, query = { + _type = 'pubsub_publish', node = self.node, items = items } + }, options)) +end + +function PubSubNode:retract(...) + local options = parse_options({}, ...) + local items = options.items + if options.id then + items = {{id = options.id}} + end + return self.client:query_pubsub(merge_tables( + { type = 'set', to = self.jid, query = { + _type = 'pubsub_retract', node = self.node, items = items, notify = options['notify'] + }}, options)) +end + +function PubSubNode:purge(...) + local options = parse_options({}, ...) + return self.client:query_pubsub(merge_tables( + { type = 'set', to = self.jid, query = { + _type = 'pubsub_owner_purge', node = self.node + }}, options)) +end + +-- Iterators over events +for _, method in ipairs({'events', 'get_next_event', 'for_each_event'}) do + PubSubNode[method] = function (node, ...) + local options = parse_options({}, ...) + options['if'] = function (event) + return event.type == 'pubsub' and event.from == node.jid and event.node == node.node + end + return node.client[method](node.client, options) + end +end + +-------------------------------------------------------------------------------- +-- Service discovery +-------------------------------------------------------------------------------- + +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', + USER_ACTIVITY = 'http://jabber.org/protocol/activity', + USER_PROFILE = 'urn:xmpp:tmp:profile' + } +} + +-------------------------------------------------------------------------------- + +_H = nil + +extra_help['sluift'] = { + [[ + This module provides methods for XMPP communication. + + The main entry point of this module is the `new_client` method, which creates a + new client for communicating with an XMPP server. + ]], + classes = help_classes +} + +return { + Client = Client, + Component = Component, + register_help = register_help, + register_class_help = register_class_help, + register_table_tostring = register_table_tostring, + register_table_equals = register_table_equals, + register_get_by_type_index = register_get_by_type_index, + process_pubsub_event = process_pubsub_event, + tprint = tprint, + read_file = read_file, + disco = disco, + get_help = get_help, + help = help, + extra_help = extra_help, + component_extra_help = component_extra_help, + copy = copy, + with = with, + create_form = create_form +} diff --git a/Sluift/globals.h b/Sluift/globals.h new file mode 100644 index 0000000..3e7e1c9 --- /dev/null +++ b/Sluift/globals.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#pragma once + +#include <Sluift/SluiftGlobals.h> + +namespace Swift { + namespace Sluift { + extern SluiftGlobals globals; + } +} diff --git a/Sluift/linit.c b/Sluift/linit.c deleted file mode 100644 index 73f0522..0000000 --- a/Sluift/linit.c +++ /dev/null @@ -1,60 +0,0 @@ -#include <lua.h> -#include <lualib.h> -#include <lauxlib.h> -#include "sluift.h" - -// A built-in table print function -// From: http://lua-users.org/wiki/TableSerialization -static const char tprint[] = - "function tprint (tt, indent, done)\n" - " done = done or {}\n" - " indent = indent or 0\n" - " if type(tt) == \"table\" then\n" - " for key, value in pairs (tt) do\n" - " io.write(string.rep (\" \", indent)) -- indent it\n" - " if type (value) == \"table\" and not done [value] then\n" - " done [value] = true\n" - " io.write(string.format(\"[%s] => table\\n\", tostring (key)));\n" - " io.write(string.rep (\" \", indent+4)) -- indent it\n" - " io.write(\"(\\n\");\n" - " tprint (value, indent + 7, done)\n" - " io.write(string.rep (\" \", indent+4)) -- indent it\n" - " io.write(\")\\n\");\n" - " else\n" - " io.write(string.format(\"[%s] => %s\\n\",\n" - " tostring (key), tostring(value)))\n" - " end\n" - " end\n" - " elseif type(tt) == \"nil\" then\n" - " io.write(\"nil\\n\")\n" - " else\n" - " io.write(tt .. \"\\n\")\n" - " end\n" - "end\n"; - -static const luaL_Reg lualibs[] = { - {"", luaopen_base}, - {LUA_LOADLIBNAME, luaopen_package}, - {LUA_TABLIBNAME, luaopen_table}, - {LUA_IOLIBNAME, luaopen_io}, - {LUA_OSLIBNAME, luaopen_os}, - {LUA_STRLIBNAME, luaopen_string}, - {LUA_MATHLIBNAME, luaopen_math}, - {LUA_DBLIBNAME, luaopen_debug}, - {"sluift", luaopen_sluift}, - {NULL, NULL} -}; - - -LUALIB_API void luaL_openlibs (lua_State *L) { - const luaL_Reg *lib = lualibs; - for (; lib->func; lib++) { - lua_pushcfunction(L, lib->func); - lua_pushstring(L, lib->name); - lua_call(L, 1, 0); - } - if (luaL_dostring(L, tprint) != 0) { - fprintf(stderr, "%s\n", lua_tostring(L, -1)); - lua_pop(L, 1); - } -} diff --git a/Sluift/main.cpp b/Sluift/main.cpp new file mode 100644 index 0000000..505cc5c --- /dev/null +++ b/Sluift/main.cpp @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2013-2014 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <string> +#include <vector> +#include <lua.hpp> +#include <Swiften/Base/foreach.h> +#include <Swiften/Base/Platform.h> +#include <boost/program_options/options_description.hpp> +#include <boost/program_options/variables_map.hpp> +#include <boost/program_options.hpp> +#include <boost/version.hpp> +#include <boost/numeric/conversion/cast.hpp> +#include <boost/assign/list_of.hpp> +#include <Sluift/globals.h> +#include <Sluift/Console.h> +#include <Sluift/StandardTerminal.h> +#include <Sluift/sluift.h> +#include <Sluift/Lua/LuaUtils.h> +#ifdef HAVE_EDITLINE +#include <Sluift/EditlineTerminal.h> +#endif +#include <Sluift/Version.h> + +using namespace Swift; + +#ifdef SWIFTEN_PLATFORM_WINDOWS +#define EXIT_KEY "Z" +#else +#define EXIT_KEY "D" +#endif + +static const std::string SLUIFT_WELCOME_STRING( + "== Sluift XMPP Console (" SLUIFT_VERSION_STRING ")\nPress Ctrl-" EXIT_KEY " to exit. Type help() for help."); + +static const luaL_Reg defaultLibraries[] = { + {"", luaopen_base}, + {LUA_LOADLIBNAME, luaopen_package}, + {LUA_TABLIBNAME, luaopen_table}, + {LUA_IOLIBNAME, luaopen_io}, + {LUA_OSLIBNAME, luaopen_os}, + {LUA_STRLIBNAME, luaopen_string}, + {LUA_MATHLIBNAME, luaopen_math}, + {LUA_DBLIBNAME, luaopen_debug}, + {"sluift", luaopen_sluift}, + {NULL, NULL} +}; + +static void handleInterruptSignal(int) { + Sluift::globals.interruptRequested = 1; +} + +static void checkResult(lua_State* L, int result) { + if (result && !lua_isnil(L, -1)) { + const char* errorMessage = lua_tostring(L, -1); + throw std::runtime_error(errorMessage ? errorMessage : "Unknown error"); + } +} + +static void initialize(lua_State* L) { + lua_gc(L, LUA_GCSTOP, 0); + for (const luaL_Reg* lib = defaultLibraries; lib->func; lib++) { +#if LUA_VERSION_NUM >= 502 + luaL_requiref(L, lib->name, lib->func, 1); + lua_pop(L, 1); +#else + lua_pushcfunction(L, lib->func); + lua_pushstring(L, lib->name); + lua_call(L, 1, 0); +#endif + } + lua_gc(L, LUA_GCRESTART, 0); +} + +static void runScript(lua_State* L, const std::string& script, const std::vector<std::string>& scriptArguments) { + // Create arguments table + lua_createtable(L, boost::numeric_cast<int>(scriptArguments.size()), 0); + for (size_t i = 0; i < scriptArguments.size(); ++i) { + lua_pushstring(L, scriptArguments[i].c_str()); + lua_rawseti(L, -2, boost::numeric_cast<int>(i+1)); + } + lua_setglobal(L, "arg"); + + // Load file + checkResult(L, luaL_loadfile(L, script.c_str())); + foreach (const std::string& scriptArgument, scriptArguments) { + lua_pushstring(L, scriptArgument.c_str()); + } + checkResult(L, Console::call(L, boost::numeric_cast<int>(scriptArguments.size()), false)); +} + +int main(int argc, char* argv[]) { + // Parse program options + boost::program_options::options_description visibleOptions("Options"); + visibleOptions.add_options() + ("help,h", "Display this help message") + ("version,v", "Display version information") + ("interactive,i", "Enter interactive mode after executing script") + ; + boost::program_options::options_description hiddenOptions("Hidden Options"); + hiddenOptions.add_options() + ("script", boost::program_options::value< std::string >(), "Script to be executed") + ("script-arguments", boost::program_options::value< std::vector<std::string> >(), "Script arguments") + ; + boost::program_options::options_description options("All Options"); + options.add(visibleOptions).add(hiddenOptions); + + boost::program_options::positional_options_description positional_options; + positional_options.add("script", 1).add("script-arguments", -1); + + boost::program_options::variables_map arguments; + try { + boost::program_options::store( + boost::program_options::command_line_parser(argc, argv) + .options(options) + .positional(positional_options).run(), arguments); + } + catch (const boost::program_options::unknown_option& option) { +#if BOOST_VERSION >= 104200 + std::cout << "Ignoring unknown option " << option.get_option_name() << " but continuing." << std::endl; +#else + std::cout << "Error: " << option.what() << " (continuing)" << std::endl; +#endif + } + catch (const std::exception& e) { + std::cout << "Error: " << e.what() << std::endl; + return -1; + } + boost::program_options::notify(arguments); + + // Help & version + if (arguments.count("help")) { + std::cout << visibleOptions << "\n"; + return 0; + } + else if (arguments.count("version")) { + std::cout << SLUIFT_VERSION_STRING; + return 0; + } + + lua_State* L = luaL_newstate(); + initialize(L); + try { + // Run script + if (arguments.count("script")) { + std::vector<std::string> scriptArguments; + if (arguments.count("script-arguments")) { + scriptArguments = arguments["script-arguments"].as< std::vector<std::string> >(); + } + runScript(L, arguments["script"].as<std::string>(), scriptArguments); + } + + // Run console + if (arguments.count("interactive") || arguments.count("script") == 0) { + // Set up signal handler + signal(SIGINT, handleInterruptSignal); + + // Import some useful functions into the global namespace + lua_getglobal(L, "sluift"); + std::vector<std::string> globalImports = boost::assign::list_of + ("help")("with"); + foreach (const std::string& globalImport, globalImports) { + lua_getfield(L, -1, globalImport.c_str()); + lua_setglobal(L, globalImport.c_str()); + } + lua_pop(L, 1); + + std::cout << SLUIFT_WELCOME_STRING << std::endl; +#ifdef HAVE_EDITLINE + EditlineTerminal& terminal = EditlineTerminal::getInstance(); +#else + StandardTerminal terminal; +#endif + Console console(L, &terminal); + console.run(); + } + } + catch (const std::exception& e) { + std::cerr << e.what() << std::endl; + lua_close(L); + return -1; + } + lua_close(L); + return 0; +} diff --git a/Sluift/sluift.cpp b/Sluift/sluift.cpp index 4f762fa..bef2e3d 100644 --- a/Sluift/sluift.cpp +++ b/Sluift/sluift.cpp @@ -1,823 +1,499 @@ /* - * Copyright (c) 2011 Remko Tronçon + * Copyright (c) 2011-2014 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ -#include "sluift.h" -#include <lauxlib.h> +#include <Sluift/sluift.h> + +#include <lua.hpp> -#include <iostream> #include <string> -#include <deque> +#include <boost/bind.hpp> +#include <boost/numeric/conversion/cast.hpp> #include <boost/assign/list_of.hpp> - -#include <Swiften/Base/foreach.h> -#include <Swiften/Swiften.h> +#include <boost/filesystem.hpp> #include "Watchdog.h" -#include "SluiftException.h" -#include "ResponseSink.h" -#include "Lua/Value.h" +#include <Sluift/Lua/Check.h> +#include <Sluift/SluiftClient.h> +#include <Sluift/SluiftComponent.h> +#include <Sluift/globals.h> +#include <Sluift/Lua/Exception.h> +#include <Sluift/Lua/LuaUtils.h> +#include <Sluift/Lua/FunctionRegistration.h> +#include <Swiften/Base/sleep.h> +#include <Swiften/Base/foreach.h> +#include <Swiften/Base/IDGenerator.h> +#include <Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h> +#include <Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h> +#include <Swiften/Serializer/PayloadSerializer.h> +#include <Swiften/TLS/Certificate.h> +#include <Swiften/TLS/CertificateFactory.h> +#include <Sluift/LuaElementConvertor.h> +#include <Sluift/Lua/Debug.h> +#include <Swiften/StringCodecs/Base64.h> +#include <Swiften/StringCodecs/Hexify.h> +#include <Swiften/IDN/IDNConverter.h> +#include <Swiften/Crypto/CryptoProvider.h> +#include <Swiften/Crypto/PlatformCryptoProvider.h> +#include <Sluift/ITunesInterface.h> using namespace Swift; -#define SLUIFT_CLIENT "SluiftClient*" +namespace Swift { + namespace Sluift { + SluiftGlobals globals; + } +} -/******************************************************************************* - * Forward declarations - ******************************************************************************/ +extern "C" const char core_lua[]; +extern "C" size_t core_lua_size; + +static inline bool getGlobalDebug(lua_State* L) { + lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.moduleLibIndex); + lua_getfield(L, -1, "debug"); + int result = lua_toboolean(L, -1); + lua_pop(L, 2); + return result; +} -static bool debug = false; -static int globalTimeout = 30000; /******************************************************************************* - * Helper classes + * Module functions ******************************************************************************/ -SimpleEventLoop eventLoop; -BoostNetworkFactories networkFactories(&eventLoop); +SLUIFT_LUA_FUNCTION_WITH_HELP( + Sluift, new_client, + "Creates a new client.\n\nReturns a @{Client} object.\n", -class SluiftClient { - public: - SluiftClient(const JID& jid, const std::string& password) : tracer(NULL) { - client = new Client(jid, password, &networkFactories); - client->setAlwaysTrustCertificates(); - client->onDisconnected.connect(boost::bind(&SluiftClient::handleDisconnected, this, _1)); - client->onMessageReceived.connect(boost::bind(&SluiftClient::handleIncomingEvent, this, _1)); - client->onPresenceReceived.connect(boost::bind(&SluiftClient::handleIncomingEvent, this, _1)); - client->getRoster()->onInitialRosterPopulated.connect(boost::bind(&SluiftClient::handleInitialRosterPopulated, this)); - if (debug) { - tracer = new ClientXMLTracer(client); - } - } + "jid The JID to connect as\n" + "passphrase The passphrase to use\n", - ~SluiftClient() { - delete tracer; - delete client; - } + "" +) { + Lua::checkString(L, 1); + JID jid(std::string(Lua::checkString(L, 1))); + std::string password(Lua::checkString(L, 2)); - Client* getClient() { - return client; - } + SluiftClient** client = reinterpret_cast<SluiftClient**>(lua_newuserdata(L, sizeof(SluiftClient*))); - ClientOptions& getOptions() { - return options; - } + lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.coreLibIndex); + lua_getfield(L, -1, "Client"); + lua_setmetatable(L, -3); + lua_pop(L, 1); - void connect() { - rosterReceived = false; - client->connect(); + *client = new SluiftClient(jid, password, &Sluift::globals.networkFactories, &Sluift::globals.eventLoop); + (*client)->setTraceEnabled(getGlobalDebug(L)); + return 1; } - void connect(const std::string& host) { - rosterReceived = false; - client->connect(host); - } +SLUIFT_LUA_FUNCTION_WITH_HELP( + Sluift, new_component, - void waitConnected() { - Watchdog watchdog(globalTimeout, networkFactories.getTimerFactory()); - while (!watchdog.getTimedOut() && client->isActive() && !client->isAvailable()) { - eventLoop.runUntilEvents(); - } - if (watchdog.getTimedOut()) { - client->disconnect(); - throw SluiftException("Timeout while connecting"); - } - } + "Creates a new component.\n\nReturns a @{Component} object.\n", - bool isConnected() const { - return client->isAvailable(); - } + "jid The JID to connect as\n" + "passphrase The passphrase to use\n", - void sendMessage(const JID& to, const std::string& body) { - Message::ref message = boost::make_shared<Message>(); - message->setTo(to); - message->setBody(body); - client->sendMessage(message); - } + "" +) { + Lua::checkString(L, 1); + JID jid(std::string(Lua::checkString(L, 1))); + std::string password(Lua::checkString(L, 2)); - void sendPresence(const std::string& status) { - client->sendPresence(boost::make_shared<Presence>(status)); - } + SluiftComponent** component = reinterpret_cast<SluiftComponent**>(lua_newuserdata(L, sizeof(SluiftComponent*))); - boost::optional<std::string> sendQuery(const JID& jid, IQ::Type type, const std::string& data, int timeout) { - rawRequestResponse.reset(); - RawRequest::ref request = RawRequest::create(type, jid, data, client->getIQRouter()); - boost::signals::scoped_connection c = request->onResponse.connect(boost::bind(&SluiftClient::handleRawRequestResponse, this, _1)); - request->send(); + lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.coreLibIndex); + lua_getfield(L, -1, "Component"); + lua_setmetatable(L, -3); + lua_pop(L, 1); - Watchdog watchdog(timeout, networkFactories.getTimerFactory()); - while (!watchdog.getTimedOut() && !rawRequestResponse) { - eventLoop.runUntilEvents(); + *component = new SluiftComponent(jid, password, &Sluift::globals.networkFactories, &Sluift::globals.eventLoop); + (*component)->setTraceEnabled(getGlobalDebug(L)); + return 1; } - if (watchdog.getTimedOut()) { - return boost::optional<std::string>(); - } - else { - return *rawRequestResponse; - } +SLUIFT_LUA_FUNCTION_WITH_HELP( + Sluift, sha1, + "Compute the SHA-1 hash of given data", + "data the data to hash", + "" +) { + static boost::shared_ptr<CryptoProvider> crypto(PlatformCryptoProvider::create()); + if (!lua_isstring(L, 1)) { + throw Lua::Exception("Expected string"); + } + size_t len; + const char* data = lua_tolstring(L, 1, &len); + ByteArray result = crypto->getSHA1Hash(createByteArray(data, len)); + lua_pushlstring(L, reinterpret_cast<char*>(vecptr(result)), result.size()); + return 1; } - void disconnect() { - client->disconnect(); - while (client->isActive()) { - eventLoop.runUntilEvents(); +SLUIFT_LUA_FUNCTION_WITH_HELP( + Sluift, sleep, + "Sleeps for the given time.", + "milliseconds the amount of milliseconds to sleep", + "" +) { + Sluift::globals.eventLoop.runOnce(); + int timeout = Lua::checkIntNumber(L, 1); + Watchdog watchdog(timeout, Sluift::globals.networkFactories.getTimerFactory()); + while (!watchdog.getTimedOut()) { + Swift::sleep(boost::numeric_cast<unsigned int>(std::min(100, timeout))); + Sluift::globals.eventLoop.runOnce(); } + return 0; } - void setSoftwareVersion(const std::string& name, const std::string& version, const std::string& os) { - client->setSoftwareVersion(name, version, os); +SLUIFT_LUA_FUNCTION_WITH_HELP( + Sluift, new_uuid, + "Generates a new UUID", "", "" +) { + lua_pushstring(L, IDGenerator().generateID().c_str()); + return 1; } - Stanza::ref getNextEvent(int timeout) { - if (!pendingEvents.empty()) { - Stanza::ref event = pendingEvents.front(); - pendingEvents.pop_front(); - return event; - } - Watchdog watchdog(timeout, networkFactories.getTimerFactory()); - while (!watchdog.getTimedOut() && pendingEvents.empty() && !client->isActive()) { - eventLoop.runUntilEvents(); - } - if (watchdog.getTimedOut() || !client->isActive()) { - return Stanza::ref(); - } - else if (!pendingEvents.empty()) { - Stanza::ref event = pendingEvents.front(); - pendingEvents.pop_front(); - return event; - } - else { - return Stanza::ref(); +SLUIFT_LUA_FUNCTION_WITH_HELP( + Sluift, from_xml, + "Convert a raw XML string into a structured representation.", + "string the string to convert", + "" +) { + PayloadsParserTester parser; + if (!parser.parse(Lua::checkString(L, 1))) { + throw Lua::Exception("Error in XML"); } + return Sluift::globals.elementConvertor.convertToLua(L, parser.getPayload()); } - std::vector<XMPPRosterItem> getRoster() { - if (!rosterReceived) { - // If we haven't requested it yet, request it for the first time - client->requestRoster(); +SLUIFT_LUA_FUNCTION_WITH_HELP( + Sluift, to_xml, + "Convert a structured element into XML.", + "element the element to convert", + "" +) { + static FullPayloadSerializerCollection serializers; + boost::shared_ptr<Payload> payload = boost::dynamic_pointer_cast<Payload>(Sluift::globals.elementConvertor.convertFromLua(L, 1)); + if (!payload) { + throw Lua::Exception("Unrecognized XML"); } - while (!rosterReceived) { - eventLoop.runUntilEvents(); + PayloadSerializer* serializer = serializers.getPayloadSerializer(payload); + if (!payload) { + throw Lua::Exception("Unrecognized XML"); } - return client->getRoster()->getItems(); - } - - private: - void handleIncomingEvent(Stanza::ref stanza) { - pendingEvents.push_back(stanza); + lua_pushstring(L, serializer->serialize(payload).c_str()); + return 1; } - void handleInitialRosterPopulated() { - rosterReceived = true; +SLUIFT_LUA_FUNCTION_WITH_HELP( + Sluift, hexify, + "Convert binary data into hexadecimal format.", + "data the data to convert", + "" +) { + if (!lua_isstring(L, 1)) { + throw Lua::Exception("Expected string"); } - - void handleRawRequestResponse(const std::string& response) { - rawRequestResponse = response; + size_t len; + const char* data = lua_tolstring(L, 1, &len); + lua_pushstring(L, Hexify::hexify(createByteArray(data, len)).c_str()); + return 1; } - void handleDisconnected(const boost::optional<ClientError>& error) { - if (error) { - throw SluiftException(*error); +SLUIFT_LUA_FUNCTION_WITH_HELP( + Sluift, unhexify, + "Convert hexadecimal data into binary data.", + "data the data in hexadecimal format", + "" +) { + if (!lua_isstring(L, 1)) { + throw Lua::Exception("Expected string"); } + ByteArray result = Hexify::unhexify(lua_tostring(L, 1)); + lua_pushlstring(L, reinterpret_cast<char*>(vecptr(result)), result.size()); + return 1; } - private: - Client* client; - ClientOptions options; - ClientXMLTracer* tracer; - bool rosterReceived; - std::deque<Stanza::ref> pendingEvents; - boost::optional<std::string> rawRequestResponse; -}; - /******************************************************************************* - * Client functions. + * Crypto functions ******************************************************************************/ -static inline SluiftClient* getClient(lua_State* L) { - return *reinterpret_cast<SluiftClient**>(luaL_checkudata(L, 1, SLUIFT_CLIENT)); -} +SLUIFT_LUA_FUNCTION_WITH_HELP( + Crypto, new_certificate, + "Creates a new X.509 certificate from DER data.\n", -static int sluift_client_connect(lua_State *L) { - try { - SluiftClient* client = getClient(L); - std::string host(luaL_checkstring(L, 2)); - if (host.empty()) { - client->connect(); - } - else { - client->connect(host); - } - client->waitConnected(); - return 1; - } - catch (const SluiftException& e) { - return luaL_error(L, e.getReason().c_str()); - } -} + "der the DER-encoded certificate data", -static int sluift_client_async_connect(lua_State *L) { - try { - getClient(L)->connect(); - return 1; - } - catch (const SluiftException& e) { - return luaL_error(L, e.getReason().c_str()); - } -} + "") { + ByteArray certData(Lua::checkByteArray(L, 1)); + Certificate::ref cert(Sluift::globals.tlsFactories.getCertificateFactory()->createCertificateFromDER(certData)); + lua_createtable(L, 0, 0); + lua_pushstring(L, cert->getSubjectName().c_str()); + lua_setfield(L, -2, "subject_name"); + lua_pushstring(L, Certificate::getSHA1Fingerprint(cert, Sluift::globals.networkFactories.getCryptoProvider()).c_str()); + lua_setfield(L, -2, "sha1_fingerprint"); -static int sluift_client_wait_connected(lua_State *L) { - try { - getClient(L)->waitConnected(); - return 1; - } - catch (const SluiftException& e) { - return luaL_error(L, e.getReason().c_str()); - } -} + Lua::pushStringArray(L, cert->getCommonNames()); + lua_setfield(L, -2, "common_names"); -static int sluift_client_is_connected(lua_State *L) { - lua_pushboolean(L, getClient(L)->isConnected()); - return 1; -} + Lua::pushStringArray(L, cert->getSRVNames()); + lua_setfield(L, -2, "srv_names"); -static int sluift_client_disconnect(lua_State *L) { - try { - getClient(L)->disconnect(); - return 1; - } - catch (const SluiftException& e) { - return luaL_error(L, e.getReason().c_str()); - } -} + Lua::pushStringArray(L, cert->getDNSNames()); + lua_setfield(L, -2, "dns_names"); -static int sluift_client_set_version(lua_State *L) { - try { - eventLoop.runOnce(); - - SluiftClient* client = getClient(L); - luaL_checktype(L, 2, LUA_TTABLE); - lua_getfield(L, 2, "name"); - const char* rawName = lua_tostring(L, -1); - lua_getfield(L, 2, "version"); - const char* rawVersion = lua_tostring(L, -1); - lua_getfield(L, 2, "os"); - const char* rawOS = lua_tostring(L, -1); - client->setSoftwareVersion(rawName ? rawName : "", rawVersion ? rawVersion : "", rawOS ? rawOS : ""); - lua_pop(L, 3); - lua_pushvalue(L, 1); - return 1; - } - catch (const SluiftException& e) { - return luaL_error(L, e.getReason().c_str()); - } -} + Lua::pushStringArray(L, cert->getXMPPAddresses()); + lua_setfield(L, -2, "xmpp_addresses"); -static int sluift_client_get_contacts(lua_State *L) { - try { - eventLoop.runOnce(); - - SluiftClient* client = getClient(L); - Lua::Table contactsTable; - foreach(const XMPPRosterItem& item, client->getRoster()) { - std::string subscription; - switch(item.getSubscription()) { - case RosterItemPayload::None: subscription = "none"; break; - case RosterItemPayload::To: subscription = "to"; break; - case RosterItemPayload::From: subscription = "from"; break; - case RosterItemPayload::Both: subscription = "both"; break; - case RosterItemPayload::Remove: subscription = "remove"; break; - } - Lua::Value groups(std::vector<Lua::Value>(item.getGroups().begin(), item.getGroups().end())); - Lua::Table itemTable = boost::assign::map_list_of - ("jid", boost::make_shared<Lua::Value>(item.getJID().toString())) - ("name", boost::make_shared<Lua::Value>(item.getName())) - ("subscription", boost::make_shared<Lua::Value>(subscription)) - ("groups", boost::make_shared<Lua::Value>(std::vector<Lua::Value>(item.getGroups().begin(), item.getGroups().end()))); - contactsTable[item.getJID().toString()] = boost::make_shared<Lua::Value>(itemTable); - } - pushValue(L, contactsTable); + Lua::registerTableToString(L, -1); return 1; } - catch (const SluiftException& e) { - return luaL_error(L, e.getReason().c_str()); - } -} -static int sluift_client_get_version(lua_State *L) { - try { - SluiftClient* client = getClient(L); - int timeout = -1; - if (lua_type(L, 3) != LUA_TNONE) { - timeout = luaL_checknumber(L, 3); - } - - ResponseSink<SoftwareVersion> sink; - GetSoftwareVersionRequest::ref request = GetSoftwareVersionRequest::create(std::string(luaL_checkstring(L, 2)), client->getClient()->getIQRouter()); - boost::signals::scoped_connection c = request->onResponse.connect(boost::ref(sink)); - request->send(); - - Watchdog watchdog(timeout, networkFactories.getTimerFactory()); - while (!watchdog.getTimedOut() && !sink.hasResponse()) { - eventLoop.runUntilEvents(); - } +/******************************************************************************* + * Filesystem Functions + ******************************************************************************/ - ErrorPayload::ref error = sink.getResponseError(); - if (error || watchdog.getTimedOut()) { +SLUIFT_LUA_FUNCTION(FS, list) { + boost::filesystem::path dir(std::string(Lua::checkString(L, 1))); + if (!boost::filesystem::exists(dir) || !boost::filesystem::is_directory(dir)) { lua_pushnil(L); - if (watchdog.getTimedOut()) { - lua_pushstring(L, "Timeout"); - } - else if (error->getCondition() == ErrorPayload::RemoteServerNotFound) { - lua_pushstring(L, "Remote server not found"); - } - // TODO - else { - lua_pushstring(L, "Error"); - } + lua_pushstring(L, "Argument is not an existing directory"); return 2; } - else if (SoftwareVersion::ref version = sink.getResponsePayload()) { - Lua::Table result = boost::assign::map_list_of - ("name", boost::make_shared<Lua::Value>(version->getName())) - ("version", boost::make_shared<Lua::Value>(version->getVersion())) - ("os", boost::make_shared<Lua::Value>(version->getOS())); - Lua::pushValue(L, result); - } - else { - lua_pushnil(L); + + boost::filesystem::directory_iterator i(dir); + std::vector<boost::filesystem::path> items( + i, boost::filesystem::directory_iterator()); + + lua_createtable(L, boost::numeric_cast<int>(items.size()), 0); + for (size_t i = 0; i < items.size(); ++i) { + lua_pushstring(L, items[i].string().c_str()); + lua_rawseti(L, -2, boost::numeric_cast<int>(i+1)); } + Lua::registerTableToString(L, -1); return 1; } - catch (const SluiftException& e) { - return luaL_error(L, e.getReason().c_str()); - } -} - -static int sluift_client_send_message(lua_State *L) { - try { - eventLoop.runOnce(); - getClient(L)->sendMessage(std::string(luaL_checkstring(L, 2)), luaL_checkstring(L, 3)); - lua_pushvalue(L, 1); +SLUIFT_LUA_FUNCTION(FS, is_file) { + boost::filesystem::path file(std::string(Lua::checkString(L, 1))); + lua_pushboolean(L, boost::filesystem::is_regular_file(file)); return 1; } - catch (const SluiftException& e) { - return luaL_error(L, e.getReason().c_str()); - } -} -static int sluift_client_send_presence(lua_State *L) { - try { - eventLoop.runOnce(); - getClient(L)->sendPresence(std::string(luaL_checkstring(L, 2))); - lua_pushvalue(L, 1); - return 0; - } - catch (const SluiftException& e) { - return luaL_error(L, e.getReason().c_str()); - } -} +/******************************************************************************* + * JID Functions + ******************************************************************************/ -static int sluift_client_get(lua_State *L) { - try { - SluiftClient* client = getClient(L); - JID jid; - std::string data; - int timeout = -1; - if (lua_type(L, 3) == LUA_TSTRING) { - jid = JID(std::string(luaL_checkstring(L, 2))); - data = std::string(luaL_checkstring(L, 3)); - if (lua_type(L, 4) != LUA_TNONE) { - timeout = luaL_checknumber(L, 4); - } - } - else { - data = std::string(luaL_checkstring(L, 2)); - if (lua_type(L, 3) != LUA_TNONE) { - timeout = luaL_checknumber(L, 3); - } - } - boost::optional<std::string> result = client->sendQuery(jid, IQ::Get, data, timeout); - if (result) { - lua_pushstring(L, result->c_str()); - } - else { - lua_pushnil(L); - } +SLUIFT_LUA_FUNCTION(JID, to_bare) { + JID jid(std::string(Lua::checkString(L, 1))); + lua_pushstring(L, jid.toBare().toString().c_str()); return 1; } - catch (const SluiftException& e) { - return luaL_error(L, e.getReason().c_str()); - } -} -static int sluift_client_set(lua_State *L) { - try { - SluiftClient* client = getClient(L); - JID jid; - std::string data; - int timeout = -1; - if (lua_type(L, 3) == LUA_TSTRING) { - jid = JID(std::string(luaL_checkstring(L, 2))); - data = std::string(luaL_checkstring(L, 3)); - if (lua_type(L, 4) != LUA_TNONE) { - timeout = luaL_checknumber(L, 4); - } - } - else { - data = std::string(luaL_checkstring(L, 2)); - if (lua_type(L, 3) != LUA_TNONE) { - timeout = luaL_checknumber(L, 3); - } - } - boost::optional<std::string> result = client->sendQuery(jid, IQ::Set, data, timeout); - if (result) { - lua_pushstring(L, result->c_str()); - } - else { - lua_pushnil(L); - } +SLUIFT_LUA_FUNCTION(JID, node) { + JID jid(std::string(Lua::checkString(L, 1))); + lua_pushstring(L, jid.getNode().c_str()); return 1; } - catch (const SluiftException& e) { - return luaL_error(L, e.getReason().c_str()); - } -} -static int sluift_client_send(lua_State *L) { - try { - eventLoop.runOnce(); - - getClient(L)->getClient()->sendData(std::string(luaL_checkstring(L, 2))); - lua_pushvalue(L, 1); - return 0; - } - catch (const SluiftException& e) { - return luaL_error(L, e.getReason().c_str()); - } +SLUIFT_LUA_FUNCTION(JID, domain) { + JID jid(std::string(Lua::checkString(L, 1))); + lua_pushstring(L, jid.getDomain().c_str()); + return 1; } -static int sluift_client_set_options(lua_State* L) { - SluiftClient* client = getClient(L); - luaL_checktype(L, 2, LUA_TTABLE); - lua_getfield(L, 2, "compress"); - if (!lua_isnil(L, -1)) { - client->getOptions().useStreamCompression = lua_toboolean(L, -1); - } - lua_getfield(L, 2, "tls"); - if (!lua_isnil(L, -1)) { - bool useTLS = lua_toboolean(L, -1); - client->getOptions().useTLS = (useTLS ? ClientOptions::UseTLSWhenAvailable : ClientOptions::NeverUseTLS); - } - lua_pushvalue(L, 1); - return 0; +SLUIFT_LUA_FUNCTION(JID, resource) { + JID jid(std::string(Lua::checkString(L, 1))); + lua_pushstring(L, jid.getResource().c_str()); + return 1; } -static void pushEvent(lua_State* L, Stanza::ref event) { - if (Message::ref message = boost::dynamic_pointer_cast<Message>(event)) { - Lua::Table result = boost::assign::map_list_of - ("type", boost::make_shared<Lua::Value>(std::string("message"))) - ("from", boost::make_shared<Lua::Value>(message->getFrom().toString())) - ("body", boost::make_shared<Lua::Value>(message->getBody())); - Lua::pushValue(L, result); - } - else if (Presence::ref presence = boost::dynamic_pointer_cast<Presence>(event)) { - Lua::Table result = boost::assign::map_list_of - ("type", boost::make_shared<Lua::Value>(std::string("presence"))) - ("from", boost::make_shared<Lua::Value>(presence->getFrom().toString())) - ("status", boost::make_shared<Lua::Value>(presence->getStatus())); - Lua::pushValue(L, result); - } - else { - lua_pushnil(L); - } +SLUIFT_LUA_FUNCTION(JID, escape_node) { + lua_pushstring(L, JID::getEscapedNode(Lua::checkString(L, 1)).c_str()); + return 1; } -static int sluift_client_for_event(lua_State *L) { - try { - eventLoop.runOnce(); +/******************************************************************************* + * Base64 Functions + ******************************************************************************/ - SluiftClient* client = getClient(L); - luaL_checktype(L, 2, LUA_TFUNCTION); - int timeout = -1; - if (lua_type(L, 3) != LUA_TNONE) { - timeout = lua_tonumber(L, 3); +SLUIFT_LUA_FUNCTION(Base64, encode) { + if (!lua_isstring(L, 1)) { + throw Lua::Exception("Expected string"); } - - while (true) { - Stanza::ref event = client->getNextEvent(timeout); - if (!event) { - // We got a timeout - lua_pushnil(L); + size_t len; + const char* data = lua_tolstring(L, 1, &len); + lua_pushstring(L, Base64::encode(createByteArray(data, len)).c_str()); return 1; } - else { - // Push the function and event on the stack - lua_pushvalue(L, 2); - pushEvent(L, event); - int oldTop = lua_gettop(L) - 2; - lua_call(L, 1, LUA_MULTRET); - int returnValues = lua_gettop(L) - oldTop; - if (returnValues > 0) { - lua_remove(L, -1 - returnValues); - return returnValues; - } - } - } - } - catch (const SluiftException& e) { - return luaL_error(L, e.getReason().c_str()); - } -} -static int sluift_client_get_next_event(lua_State *L) { - try { - eventLoop.runOnce(); - - SluiftClient* client = getClient(L); - int timeout = -1; - if (lua_type(L, 2) != LUA_TNONE) { - timeout = lua_tonumber(L, 2); +SLUIFT_LUA_FUNCTION(Base64, decode) { + if (!lua_isstring(L, 1)) { + throw Lua::Exception("Expected string"); } - pushEvent(L, client->getNextEvent(timeout)); + ByteArray result = Base64::decode(lua_tostring(L, 1)); + lua_pushlstring(L, reinterpret_cast<char*>(vecptr(result)), result.size()); return 1; } - catch (const SluiftException& e) { - return luaL_error(L, e.getReason().c_str()); - } -} +/******************************************************************************* + * IDN Functions + ******************************************************************************/ -static int sluift_client_add_contact(lua_State* L) { - try { - eventLoop.runOnce(); - SluiftClient* client = getClient(L); - RosterItemPayload item; - if (lua_type(L, 2) == LUA_TTABLE) { - lua_getfield(L, 2, "jid"); - const char* rawJID = lua_tostring(L, -1); - if (rawJID) { - item.setJID(std::string(rawJID)); - } - lua_getfield(L, 2, "name"); - const char* rawName = lua_tostring(L, -1); - if (rawName) { - item.setName(rawName); - } - lua_getfield(L, 2, "groups"); - if (!lua_isnil(L, -1)) { - if (lua_type(L, -1) == LUA_TTABLE) { - for (size_t i = 1; i <= lua_objlen(L, -1); ++i) { - lua_rawgeti(L, -1, i); - const char* rawGroup = lua_tostring(L, -1); - if (rawGroup) { - item.addGroup(rawGroup); - } - lua_pop(L, 1); - } - } - else { - return luaL_error(L, "Groups should be a table"); - } - } +SLUIFT_LUA_FUNCTION(IDN, encode) { + IDNConverter* converter = Sluift::globals.networkFactories.getIDNConverter(); + boost::optional<std::string> encoded = converter->getIDNAEncoded(Lua::checkString(L, 1)); + if (!encoded) { + lua_pushnil(L); + lua_pushstring(L, "Error encoding domain name"); + return 2; } - else { - item.setJID(luaL_checkstring(L, 2)); + lua_pushstring(L, encoded->c_str()); + return 1; } - client->getRoster(); - if (!client->getClient()->getRoster()->containsJID(item.getJID())) { - RosterPayload::ref roster = boost::make_shared<RosterPayload>(); - roster->addItem(item); - - ResponseSink<RosterPayload> sink; - SetRosterRequest::ref request = SetRosterRequest::create(roster, client->getClient()->getIQRouter()); - boost::signals::scoped_connection c = request->onResponse.connect(boost::ref(sink)); - request->send(); - while (!sink.hasResponse()) { - eventLoop.runUntilEvents(); - } - if (sink.getResponseError()) { - lua_pushboolean(L, false); - return 1; +SLUIFT_LUA_FUNCTION(IDN, stringprep) { + IDNConverter* converter = Sluift::globals.networkFactories.getIDNConverter(); + IDNConverter::StringPrepProfile profile; + std::string profileString = Lua::checkString(L, 2); + if (profileString == "nameprep") { + profile = IDNConverter::NamePrep; } + else if (profileString == "xmpp_nodeprep") { + profile = IDNConverter::XMPPNodePrep; } - client->getClient()->getSubscriptionManager()->requestSubscription(item.getJID()); - lua_pushboolean(L, true); - return 1; + else if (profileString == "xmpp_resourceprep") { + profile = IDNConverter::XMPPResourcePrep; } - catch (const SluiftException& e) { - return luaL_error(L, e.getReason().c_str()); + else if (profileString == "saslprep") { + profile = IDNConverter::SASLPrep; } + else { + throw Lua::Exception("Invalid profile"); } - -static int sluift_client_remove_contact(lua_State* L) { try { - eventLoop.runOnce(); - SluiftClient* client = getClient(L); - JID jid(luaL_checkstring(L, 2)); - - RosterPayload::ref roster = boost::make_shared<RosterPayload>(); - roster->addItem(RosterItemPayload(JID(luaL_checkstring(L, 2)), "", RosterItemPayload::Remove)); - ResponseSink<RosterPayload> sink; - SetRosterRequest::ref request = SetRosterRequest::create(roster, client->getClient()->getIQRouter()); - boost::signals::scoped_connection c = request->onResponse.connect(boost::ref(sink)); - request->send(); - while (!sink.hasResponse()) { - eventLoop.runUntilEvents(); - } - lua_pushboolean(L, !sink.getResponseError()); - return 1; + lua_pushstring(L, converter->getStringPrepared(Lua::checkString(L, 1), profile).c_str()); } - catch (const SluiftException& e) { - return luaL_error(L, e.getReason().c_str()); + catch (const std::exception&) { + throw Lua::Exception("Error"); } + return 1; } -static int sluift_client_confirm_subscription(lua_State* L) { - try { - eventLoop.runOnce(); - SluiftClient* client = getClient(L); - JID jid(luaL_checkstring(L, 2)); - client->getClient()->getSubscriptionManager()->confirmSubscription(jid); - return 0; - } - catch (const SluiftException& e) { - return luaL_error(L, e.getReason().c_str()); - } -} +/******************************************************************************* + * iTunes Functions + ******************************************************************************/ -static int sluift_client_cancel_subscription(lua_State* L) { - try { - eventLoop.runOnce(); - SluiftClient* client = getClient(L); - JID jid(luaL_checkstring(L, 2)); - client->getClient()->getSubscriptionManager()->cancelSubscription(jid); +#ifdef HAVE_ITUNES +SLUIFT_LUA_FUNCTION(iTunes, get_current_track) { + boost::optional<ITunesInterface::Track> track = Sluift::globals.iTunes.getCurrentTrack(); + if (!track) { return 0; } - catch (const SluiftException& e) { - return luaL_error(L, e.getReason().c_str()); - } -} - -static int sluift_client_gc (lua_State *L) { - SluiftClient* client = getClient(L); - delete client; - return 0; + lua_createtable(L, 0, 0); + lua_pushstring(L, track->artist.c_str()); + lua_setfield(L, -2, "artist"); + lua_pushstring(L, track->name.c_str()); + lua_setfield(L, -2, "name"); + lua_pushstring(L, track->album.c_str()); + lua_setfield(L, -2, "album"); + lua_pushinteger(L, track->trackNumber); + lua_setfield(L, -2, "track_number"); + lua_pushnumber(L, track->duration); + lua_setfield(L, -2, "duration"); + lua_pushinteger(L, track->rating); + lua_setfield(L, -2, "rating"); + Lua::registerTableToString(L, -1); + Lua::registerTableEquals(L, -1); + return 1; } - -static const luaL_reg sluift_client_functions[] = { - {"connect", sluift_client_connect}, - {"async_connect", sluift_client_async_connect}, - {"wait_connected", sluift_client_wait_connected}, - {"is_connected", sluift_client_is_connected}, - {"disconnect", sluift_client_disconnect}, - {"send_message", sluift_client_send_message}, - {"send_presence", sluift_client_send_presence}, - {"get", sluift_client_get}, - {"set", sluift_client_set}, - {"send", sluift_client_send}, - {"set_version", sluift_client_set_version}, - {"get_contacts", sluift_client_get_contacts}, - {"get_version", sluift_client_get_version}, - {"set_options", sluift_client_set_options}, - {"for_event", sluift_client_for_event}, - {"get_next_event", sluift_client_get_next_event}, - {"add_contact", sluift_client_add_contact}, - {"remove_contact", sluift_client_remove_contact}, - {"confirm_subscription", sluift_client_confirm_subscription}, - {"cancel_subscription", sluift_client_cancel_subscription}, - {"__gc", sluift_client_gc}, - {NULL, NULL} -}; +#endif /******************************************************************************* - * Module functions + * Module registration ******************************************************************************/ -static int sluift_new_client(lua_State *L) { - try { - JID jid(std::string(luaL_checkstring(L, 1))); - std::string password(luaL_checkstring(L, 2)); - - SluiftClient** client = reinterpret_cast<SluiftClient**>(lua_newuserdata(L, sizeof(SluiftClient*))); - luaL_getmetatable(L, SLUIFT_CLIENT); - lua_setmetatable(L, -2); +static const luaL_Reg sluift_functions[] = { {NULL, NULL} }; - *client = new SluiftClient(jid, password); - return 1; - } - catch (const SluiftException& e) { - return luaL_error(L, e.getReason().c_str()); - } -} - -static int sluift_jid_to_bare(lua_State *L) { - JID jid(std::string(luaL_checkstring(L, 1))); - lua_pushstring(L, jid.toBare().toString().c_str()); - return 1; -} +SLUIFT_API int luaopen_sluift(lua_State* L) { + // Initialize & store the module table + luaL_register(L, lua_tostring(L, 1), sluift_functions); + lua_pushinteger(L, -1); + lua_setfield(L, -2, "timeout"); + lua_pushboolean(L, 0); + lua_setfield(L, -2, "debug"); -static int sluift_jid_node(lua_State *L) { - JID jid(std::string(luaL_checkstring(L, 1))); - lua_pushstring(L, jid.getNode().c_str()); - return 1; -} + lua_pushvalue(L, -1); + Sluift::globals.moduleLibIndex = luaL_ref(L, LUA_REGISTRYINDEX); -static int sluift_jid_domain(lua_State *L) { - JID jid(std::string(luaL_checkstring(L, 1))); - lua_pushstring(L, jid.getDomain().c_str()); - return 1; + // Load core lib code + if (luaL_loadbuffer(L, core_lua, core_lua_size, "core.lua") != 0) { + lua_error(L); } + lua_pushvalue(L, -2); + lua_call(L, 1, 1); + Sluift::globals.coreLibIndex = luaL_ref(L, LUA_REGISTRYINDEX); -static int sluift_jid_resource(lua_State *L) { - JID jid(std::string(luaL_checkstring(L, 1))); - lua_pushstring(L, jid.getResource().c_str()); - return 1; + // Register functions + Lua::FunctionRegistry::getInstance().addFunctionsToTable(L, "Sluift"); + Lua::FunctionRegistry::getInstance().createFunctionTable(L, "JID"); + lua_setfield(L, -2, "jid"); + Lua::FunctionRegistry::getInstance().createFunctionTable(L, "Base64"); + lua_setfield(L, -2, "base64"); + Lua::FunctionRegistry::getInstance().createFunctionTable(L, "IDN"); + lua_setfield(L, -2, "idn"); + Lua::FunctionRegistry::getInstance().createFunctionTable(L, "Crypto"); + lua_setfield(L, -2, "crypto"); + Lua::FunctionRegistry::getInstance().createFunctionTable(L, "FS"); + lua_setfield(L, -2, "fs"); +#ifdef HAVE_ITUNES + Lua::FunctionRegistry::getInstance().createFunctionTable(L, "iTunes"); + lua_setfield(L, -2, "itunes"); +#endif + + // Register convenience functions + lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.coreLibIndex); + std::vector<std::string> coreLibExports = boost::assign::list_of + ("tprint")("disco")("help")("get_help")("copy")("with")("read_file")("create_form"); + foreach (const std::string& coreLibExport, coreLibExports) { + lua_getfield(L, -1, coreLibExport.c_str()); + lua_setfield(L, -3, coreLibExport.c_str()); } + lua_pop(L, 1); -static int sluift_sleep(lua_State *L) { - try { - eventLoop.runOnce(); - - int timeout = luaL_checknumber(L, 1); - Watchdog watchdog(timeout, networkFactories.getTimerFactory()); - while (!watchdog.getTimedOut()) { - Swift::sleep(std::min(100, timeout)); - eventLoop.runOnce(); - } - return 0; - } - catch (const SluiftException& e) { - return luaL_error(L, e.getReason().c_str()); - } + // Load client metatable + lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.coreLibIndex); + std::vector<std::string> tables = boost::assign::list_of("Client"); + foreach(const std::string& table, tables) { + lua_getfield(L, -1, table.c_str()); + Lua::FunctionRegistry::getInstance().addFunctionsToTable(L, table); + lua_pop(L, 1); } + lua_pop(L, 1); -static int sluift_index(lua_State *L) { - std::string key(luaL_checkstring(L, 2)); - if (key == "debug") { - lua_pushboolean(L, debug); - return 1; - } - else if (key == "timeout") { - lua_pushnumber(L, globalTimeout); - return 1; - } - else { - return luaL_error(L, "Invalid index"); - } + // Load component metatable + lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.coreLibIndex); + std::vector<std::string> comp_tables = boost::assign::list_of("Component"); + foreach(const std::string& table, comp_tables) { + lua_getfield(L, -1, table.c_str()); + Lua::FunctionRegistry::getInstance().addFunctionsToTable(L, table); + lua_pop(L, 1); } + lua_pop(L, 1); -static int sluift_newindex(lua_State *L) { - std::string key(luaL_checkstring(L, 2)); - if (key == "debug") { - debug = lua_toboolean(L, 3); - return 0; - } - else if (key == "timeout") { - globalTimeout = luaL_checknumber(L, 3); - return 0; - } - else { - return luaL_error(L, "Invalid index"); + // Register documentation for all elements + foreach (boost::shared_ptr<LuaElementConvertor> convertor, Sluift::globals.elementConvertor.getConvertors()) { + boost::optional<LuaElementConvertor::Documentation> documentation = convertor->getDocumentation(); + if (documentation) { + Lua::registerClassHelp(L, documentation->className, documentation->description); } } -static const luaL_reg sluift_functions[] = { - {"new_client", sluift_new_client}, - {"jid_to_bare", sluift_jid_to_bare}, - {"jid_node", sluift_jid_node}, - {"jid_domain", sluift_jid_domain}, - {"jid_resource", sluift_jid_resource}, - {"sleep", sluift_sleep}, - {NULL, NULL} -}; - - -/******************************************************************************* - * Module registration - ******************************************************************************/ - -SLUIFT_API int luaopen_sluift(lua_State *L) { - // Register functions - luaL_register(L, "sluift", sluift_functions); - lua_createtable(L, 0, 0); - lua_pushcclosure(L, sluift_index, 0); - lua_setfield(L, -2, "__index"); - lua_pushcclosure(L, sluift_newindex, 0); - lua_setfield(L, -2, "__newindex"); - lua_setmetatable(L, -2); - - // Register the client metatable - luaL_newmetatable(L, SLUIFT_CLIENT); - lua_pushvalue(L, -1); - lua_setfield(L, -2, "__index"); - luaL_register(L, NULL, sluift_client_functions); + // Register global documentation + Lua::registerExtraHelp(L, -1, "sluift"); return 1; diff --git a/Sluift/sluift.h b/Sluift/sluift.h index 27b4eab..b82e1c4 100644 --- a/Sluift/sluift.h +++ b/Sluift/sluift.h @@ -1,4 +1,4 @@ /* - * Copyright (c) 2011 Remko Tronçon + * Copyright (c) 2011-2013 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. @@ -9,9 +9,18 @@ #if defined(SLUIFT_BUILD_DLL) #define SLUIFT_API __declspec(dllexport) +#elif __GNUC__ >= 4 +#define SLUIFT_API __attribute__((visibility("default"))) #else -#define SLUIFT_API extern +#define SLUIFT_API #endif +#if defined(__cplusplus) +#include <lua.hpp> +#else #include <lua.h> +#endif +#if defined(__cplusplus) +extern "C" +#endif SLUIFT_API int (luaopen_sluift)(lua_State *L); diff --git a/Sluift/tokenize.cpp b/Sluift/tokenize.cpp new file mode 100644 index 0000000..b089cdb --- /dev/null +++ b/Sluift/tokenize.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/tokenize.h> + +#include <boost/tokenizer.hpp> +#include <cctype> + +using namespace Swift; + +namespace { + struct LuaTokenizeFunctor { + void reset() { + } + + template<typename InputIterator, typename Token> + bool operator()(InputIterator& next, InputIterator& end, Token& result) { + while (next != end && std::isspace(*next)) { + ++next; + } + if (next == end) { + return false; + } + + std::vector<char> token; + char c = *next++; + token.push_back(c); + + // String literal + if (c == '\'' || c == '"') { + char quote = c; + bool inEscape = false; + for (; next != end; ++next) { + c = *next; + token.push_back(c); + if (inEscape) { + inEscape = false; + } + else if (c == '\\') { + inEscape = true; + } + else if (c == quote) { + break; + } + } + if (next != end) { + ++next; + } + } + // Identifier + else if (std::isalpha(c) || c == '_') { + while (next != end && (std::isalpha(*next) || *next == '_' || std::isdigit(*next))) { + token.push_back(*next); + ++next; + } + } + // Digit + else if (std::isdigit(c)) { + while (next != end && !std::isspace(*next)) { + token.push_back(*next); + ++next; + } + } + // Dots + else if (c == '.') { + while (next != end && *next == '.') { + token.push_back(*next); + ++next; + } + } + + result = Token(&token[0], token.size()); + return true; + } + }; +} + + +std::vector<std::string> Lua::tokenize(const std::string& input) { + boost::tokenizer<LuaTokenizeFunctor> tokenizer(input); + return std::vector<std::string>(tokenizer.begin(), tokenizer.end()); +} + diff --git a/Sluift/tokenize.h b/Sluift/tokenize.h new file mode 100644 index 0000000..6f09345 --- /dev/null +++ b/Sluift/tokenize.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#pragma once + +#include <vector> +#include <string> + +namespace Swift { + namespace Lua { + std::vector<std::string> tokenize(const std::string&); + } +} |