From 5a89265623214164fa7ce36721de05183d53058d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Remko=20Tron=C3=A7on?= Date: Thu, 26 Dec 2013 16:56:45 +0100 Subject: Sluift: Refactoring - Rename boot.lua to core.lua - Support Lua 5.2 - Support version prefixes for sluift module - Add Client:process_events Change-Id: I3fa6d06d1dbdf86f65b9f4203bd2ec5b5526b104 diff --git a/BuildTools/SCons/SConstruct b/BuildTools/SCons/SConstruct index 9eeaabe..8c5258e 100644 --- a/BuildTools/SCons/SConstruct +++ b/BuildTools/SCons/SConstruct @@ -422,7 +422,7 @@ if env.get("lua_includedir", None) : lua_flags["CPPPATH"] = [env["lua_includedir"]] lua_conf_env.MergeFlags(lua_flags) conf = Configure(lua_conf_env) -if not env.get("lua_force_bundled", False) and conf.CheckCXXHeader("lua.hpp") and conf.CheckLib(env["lua_libname"]) : +if not env.get("lua_force_bundled", False) and conf.CheckLibWithHeader(env["lua_libname"], "lua.hpp", "cxx", autoadd = 0) : env["HAVE_LUA"] = 1 env["LUA_FLAGS"] = { "LIBS": [env["lua_libname"]] } lua_version = GetVersion(conf, "LUA_VERSION_NUM", "lua.h") diff --git a/Sluift/.gitignore b/Sluift/.gitignore index e5fd1e5..940fc86 100644 --- a/Sluift/.gitignore +++ b/Sluift/.gitignore @@ -2,6 +2,6 @@ lua.c sluift_dll.cpp sluift dll.c -boot.c +core.c dll/ exe/ diff --git a/Sluift/Lua/LuaUtils.cpp b/Sluift/Lua/LuaUtils.cpp index 7052abe..b00ab56 100644 --- a/Sluift/Lua/LuaUtils.cpp +++ b/Sluift/Lua/LuaUtils.cpp @@ -22,7 +22,7 @@ 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.bootIndex); + 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) { @@ -33,7 +33,7 @@ void Swift::Lua::registerTableToString(lua_State* L, int index) { void Swift::Lua::registerGetByTypeIndex(lua_State* L, int index) { index = Lua::absoluteOffset(L, index); - lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.bootIndex); + 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) { diff --git a/Sluift/SConscript b/Sluift/SConscript index 1a29e43..c8f1108 100644 --- a/Sluift/SConscript +++ b/Sluift/SConscript @@ -32,7 +32,7 @@ elif env["SCONS_STAGE"] == "build" : "ElementConvertors/CommandConvertor.cpp", "ClientHelpers.cpp", "SluiftClient.cpp", - "boot.c", + "core.c", "client.cpp", "sluift.cpp" ] @@ -42,6 +42,8 @@ elif env["SCONS_STAGE"] == "build" : 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"]) @@ -62,7 +64,7 @@ elif env["SCONS_STAGE"] == "build" : f.close() sluift_env.Command("lua.c", ["#/3rdParty/Lua/src/lua.c", sluift_env.Value(sluift_env["SLUIFT_VERSION"])], env.Action(patchLua, cmdstr = "$GENCOMSTR")) - # Generate boot.cpp + # Generate core.cpp def generate_embedded_lua(env, target, source) : f = open(source[0].abspath, "r") data = f.read() @@ -70,7 +72,7 @@ elif env["SCONS_STAGE"] == "build" : f = open(target[0].abspath, "w") f.write('const char ' + source[0].name.replace(".", "_") + "[] = \"" + data.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"') + "\";") f.close() - sluift_env.Command("boot.c", ["boot.lua"], env.Action(generate_embedded_lua, cmdstr="$GENCOMSTR")) + sluift_env.Command("core.c", ["core.lua"], env.Action(generate_embedded_lua, cmdstr="$GENCOMSTR")) if sluift_env.get("HAVE_READLINE", False) : sluift_env.Append(CPPDEFINES = ["LUA_USE_READLINE"]) diff --git a/Sluift/SluiftGlobals.h b/Sluift/SluiftGlobals.h index 03c1c1a..18a90c2 100644 --- a/Sluift/SluiftGlobals.h +++ b/Sluift/SluiftGlobals.h @@ -19,6 +19,6 @@ namespace Swift { LuaElementConvertors elementConvertor; SimpleEventLoop eventLoop; BoostNetworkFactories networkFactories; - int bootIndex; + int coreLibIndex; }; } diff --git a/Sluift/boot.lua b/Sluift/boot.lua deleted file mode 100644 index a28a189..0000000 --- a/Sluift/boot.lua +++ /dev/null @@ -1,454 +0,0 @@ ---[[ - Copyright (c) 2013 Remko Tronçon - Licensed under the GNU General Public License. - See the COPYING file for more information. ---]] - -local Client = {} -local PubSub = {} -local PubSubNode = {} - --------------------------------------------------------------------------------- --- Utility methods --------------------------------------------------------------------------------- - -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 clone_table(table) - return merge_tables(table) -end - -local function parse_options(unnamed_parameters, arg1, arg2) - local options = {} - if type(arg1) == 'table' then - options = arg1 - f = arg2 - elseif type(arg1) == 'function' then - f = arg1 - end - options.f = f or options.f - return clone_table(options) -end - - -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 tprint(table) - print(table_tostring(table, true)) -end - -local function register_table_tostring(table) - if type(table) == 'table' then - local metatable = getmetatable(table) - if not metatable then - metatable = {} - setmetatable(table, metatable) - end - metatable.__tostring = table_tostring - end - return table -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 - --------------------------------------------------------------------------------- --- Client --------------------------------------------------------------------------------- - -function Client:connect (...) - local options = parse_options({}, ...) - local f = options.f - self:async_connect(options) - self:wait_connected() - if f then - local result = { xpcall(function() return f(self) end, debug.traceback) } - self:disconnect() - if result[1] then - table.remove(result, 1) - return unpack(result) - else - error(result[2]) - end - end - return true -end - -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 - -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 - -for method, event_type in pairs({message = 'message', presence = 'presence', pubsub_event = 'pubsub'}) do - Client['for_each_' .. method] = function (client, ...) - local options = parse_options({}, ...) - options['type'] = event_type - return client:for_each_event (options) - end - - Client['get_next_' .. method] = function (client, ...) - local options = parse_options({}, ...) - options['type'] = event_type - return client:get_next_event(options) - end -end - -for method, event_type in pairs({messages = 'message', pubsub_events = 'pubsub'}) do - Client[method] = function (client, ...) - local options = parse_options({}, ...) - options['type'] = event_type - return client:events (options) - end -end - --- --- Register get_* and set_* convenience methods for some type of queries --- --- Example usages: --- client:get_software_version{to = 'alice@wonderland.lit'} --- client:set_command{to = 'alice@wonderland.lit', command = { type = 'execute', node = 'uptime' }} --- -local get_set_shortcuts = { - get = {'software_version', 'disco_items', 'xml', 'dom', 'vcard'}, - set = {'command'} -} -for query_action, query_types in pairs(get_set_shortcuts) do - for _, query_type in ipairs(query_types) do - Client[query_action .. '_' .. query_type] = function (client, options) - options = options or {} - if type(options) ~= 'table' then error('Invalid options: ' .. options) end - options['query'] = merge_tables({_type = query_type}, options[query_type] or {}) - return client[query_action](client, options) - end - end -end - -function Client:pubsub (jid) - local result = { client = self, jid = jid } - setmetatable(result, PubSub) - return result -end - --------------------------------------------------------------------------------- --- PubSub --------------------------------------------------------------------------------- - -PubSub.__index = 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 --------------------------------------------------------------------------------- - -PubSubNode.__index = PubSubNode - -local function pubsub_node_configuration_to_form(configuration) - if not configuration then - return - end - local fields = { {name = 'form_type', value = 'http://jabber.org/protocol/pubsub#node_config'} } - for var, value in pairs(configuration) do - fields[#fields+1] = { name = var, value = value } - end - return { type = "submit", fields = fields } -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'] } - }, 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' - } -} - --------------------------------------------------------------------------------- - -return { - Client = Client, - register_table_tostring = register_table_tostring, - register_get_by_type_index = register_get_by_type_index, - process_pubsub_event = process_pubsub_event, - tprint = tprint, - disco = disco, -} diff --git a/Sluift/client.cpp b/Sluift/client.cpp index 97f9106..9eac84b 100644 --- a/Sluift/client.cpp +++ b/Sluift/client.cpp @@ -419,7 +419,7 @@ static void pushEvent(lua_State* L, const SluiftClient::Event& event) { lua_pushstring(L, event.from.toString().c_str()); lua_setfield(L, -2, "from"); - lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.bootIndex); + lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.coreLibIndex); lua_getfield(L, -1, "process_pubsub_event"); lua_pushvalue(L, -3); lua_call(L, 1, 0); diff --git a/Sluift/core.lua b/Sluift/core.lua new file mode 100644 index 0000000..e2d4f8e --- /dev/null +++ b/Sluift/core.lua @@ -0,0 +1,473 @@ +--[[ + Copyright (c) 2013 Remko Tronçon + Licensed under the GNU General Public License. + See the COPYING file for more information. +--]] + +local _G = _G +local pairs, ipairs, print, tostring, type, error = pairs, ipairs, print, tostring, type, error +local setmetatable, getmetatable = setmetatable, getmetatable +local string = string +_ENV = nil + +local Client = {} +local PubSub = {} +local PubSubNode = {} + +-------------------------------------------------------------------------------- +-- Utility methods +-------------------------------------------------------------------------------- + +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 clone_table(table) + return merge_tables(table) +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 clone_table(options) +end + + +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 tprint(table) + print(table_tostring(table, true)) +end + +local function register_table_tostring(table) + if type(table) == 'table' then + local metatable = getmetatable(table) + if not metatable then + metatable = {} + setmetatable(table, metatable) + end + metatable.__tostring = table_tostring + end + return table +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 + +-------------------------------------------------------------------------------- +-- Client +-------------------------------------------------------------------------------- + +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 + +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 + +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 + +for method, event_type in pairs({message = 'message', presence = 'presence', pubsub_event = 'pubsub'}) do + Client['for_each_' .. method] = function (client, ...) + local options = parse_options({}, ...) + options['type'] = event_type + return client:for_each_event (options) + end + + Client['get_next_' .. method] = function (client, ...) + local options = parse_options({}, ...) + options['type'] = event_type + return client:get_next_event(options) + end +end + +for method, event_type in pairs({messages = 'message', pubsub_events = 'pubsub'}) do + Client[method] = function (client, ...) + local options = parse_options({}, ...) + options['type'] = event_type + return client:events (options) + end +end + +-- Process all pending events +function Client:process_events () + for event in self:events{timeout=0} do end +end + +-- +-- Register get_* and set_* convenience methods for some type of queries +-- +-- Example usages: +-- client:get_software_version{to = 'alice@wonderland.lit'} +-- client:set_command{to = 'alice@wonderland.lit', command = { type = 'execute', node = 'uptime' }} +-- +local get_set_shortcuts = { + get = {'software_version', 'disco_items', 'xml', 'dom', 'vcard'}, + set = {'command'} +} +for query_action, query_types in pairs(get_set_shortcuts) do + for _, query_type in ipairs(query_types) do + Client[query_action .. '_' .. query_type] = function (client, options) + options = options or {} + if type(options) ~= 'table' then error('Invalid options: ' .. options) end + options['query'] = merge_tables({_type = query_type}, options[query_type] or {}) + return client[query_action](client, options) + end + end +end + +function Client:pubsub (jid) + local result = { client = self, jid = jid } + setmetatable(result, PubSub) + return result +end + +-------------------------------------------------------------------------------- +-- PubSub +-------------------------------------------------------------------------------- + +PubSub.__index = 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 +-------------------------------------------------------------------------------- + +PubSubNode.__index = PubSubNode + +local function pubsub_node_configuration_to_form(configuration) + if not configuration then + return + end + local fields = { {name = 'form_type', value = 'http://jabber.org/protocol/pubsub#node_config'} } + for var, value in pairs(configuration) do + fields[#fields+1] = { name = var, value = value } + end + return { type = "submit", fields = fields } +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'] } + }, 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' + } +} + +-------------------------------------------------------------------------------- + +return { + Client = Client, + register_table_tostring = register_table_tostring, + register_get_by_type_index = register_get_by_type_index, + process_pubsub_event = process_pubsub_event, + tprint = tprint, + disco = disco, +} diff --git a/Sluift/linit.c b/Sluift/linit.c index 8f0614b..ad27b37 100644 --- a/Sluift/linit.c +++ b/Sluift/linit.c @@ -4,33 +4,34 @@ #include "sluift.h" 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} + {"", 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); - } + const luaL_Reg *lib = lualibs; + for (; lib->func; lib++) { + lua_pushcfunction(L, lib->func); + lua_pushstring(L, lib->name); + lua_call(L, 1, 0); + } /* Import sluift into global namespace */ - lua_getfield(L, LUA_GLOBALSINDEX, "sluift"); + lua_pushglobaltable(L); + lua_getfield(L, -1, "sluift"); for (lua_pushnil(L); lua_next(L, -2); ) { lua_pushvalue(L, -2); lua_pushvalue(L, -2); - lua_settable(L, LUA_GLOBALSINDEX); + lua_settable(L, -6); lua_pop(L, 1); } } diff --git a/Sluift/sluift.cpp b/Sluift/sluift.cpp index 95e7101..e6b2bb6 100644 --- a/Sluift/sluift.cpp +++ b/Sluift/sluift.cpp @@ -40,7 +40,7 @@ namespace Swift { } } -extern "C" const char boot_lua[]; +extern "C" const char core_lua[]; /******************************************************************************* * Module functions @@ -263,21 +263,21 @@ SLUIFT_LUA_FUNCTION(IDN, stringprep) { * Module registration ******************************************************************************/ -static const luaL_reg sluift_functions[] = { {NULL, NULL} }; +static const luaL_Reg sluift_functions[] = { {NULL, NULL} }; SLUIFT_API int luaopen_sluift(lua_State* L) { // Initialize globals Sluift::globals.debug = false; Sluift::globals.timeout = -1; - luaL_register(L, "sluift", sluift_functions); + luaL_register(L, lua_tostring(L, 1), sluift_functions); - // Load bootstrap code - if (luaL_loadbuffer(L, boot_lua, strlen(boot_lua), "boot.lua") != 0) { + // Load core lib code + if (luaL_loadbuffer(L, core_lua, strlen(core_lua), "core.lua") != 0) { lua_error(L); } lua_call(L, 0, 1); - Sluift::globals.bootIndex = luaL_ref(L, LUA_REGISTRYINDEX); + Sluift::globals.coreLibIndex = luaL_ref(L, LUA_REGISTRYINDEX); // Register functions Lua::FunctionRegistry::getInstance().addFunctionsToTable(L, "Sluift"); @@ -289,14 +289,13 @@ SLUIFT_API int luaopen_sluift(lua_State* L) { lua_setfield(L, -2, "idn"); // Register convenience functions - lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.bootIndex); - lua_getfield(L, -1, "tprint"); - lua_setfield(L, -3, "tprint"); - lua_pop(L, 1); - - lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.bootIndex); - lua_getfield(L, -1, "disco"); - lua_setfield(L, -3, "disco"); + lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.coreLibIndex); + std::vector coreLibExports = boost::assign::list_of + ("tprint")("disco"); + foreach (const std::string& coreLibExport, coreLibExports) { + lua_getfield(L, -1, coreLibExport.c_str()); + lua_setfield(L, -3, coreLibExport.c_str()); + } lua_pop(L, 1); // Set read only @@ -312,7 +311,7 @@ SLUIFT_API int luaopen_sluift(lua_State* L) { foreach (const std::string& table, tables) { Lua::FunctionRegistry::getInstance().registerTypeMetaTable(L, table); luaL_getmetatable(L, Lua::FunctionRegistry::getMetaTableNameForType(table).c_str()); - lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.bootIndex); + lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.coreLibIndex); lua_getfield(L, -1, table.c_str()); if (!lua_isnil(L, -1)) { for (lua_pushnil(L); lua_next(L, -2); ) { diff --git a/Sluift/sluift.h b/Sluift/sluift.h index b82e1c4..2613370 100644 --- a/Sluift/sluift.h +++ b/Sluift/sluift.h @@ -20,6 +20,10 @@ #include #endif +#if LUA_VERSION_NUM < 502 +#define lua_pushglobaltable(L) lua_pushvalue(L, LUA_GLOBALSINDEX) +#endif + #if defined(__cplusplus) extern "C" #endif -- cgit v0.10.2-6-g49f6