diff options
Diffstat (limited to 'Sluift/boot.lua')
-rw-r--r-- | Sluift/boot.lua | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/Sluift/boot.lua b/Sluift/boot.lua new file mode 100644 index 0000000..ae8cc41 --- /dev/null +++ b/Sluift/boot.lua @@ -0,0 +1,381 @@ +--[[ + 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, indent, accumulator) + local INDENT = ' ' + local accumulator = accumulator or '' + local indent = indent or '' + accumulator = accumulator .. '{' + local is_first = true + for key, value in pairs(table) do + if 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 + accumulator = table_tostring(value, indent .. INDENT, accumulator) + else + accumulator = accumulator .. table_value_tostring(value) + end + end + end + if not is_first then + accumulator = accumulator .. '\n' .. indent + end + accumulator = accumulator .. '}' + return accumulator +end + +local function tprint(table) + print(table_tostring(table)) +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 (client, ...) + local options = parse_options({}, ...) + local f = options.f + client:async_connect(options) + client:wait_connected() + if f then + local result = { xpcall(function() return f(client) end, debug.traceback) } + client:disconnect() + if result[1] then + table.remove(result, 1) + return unpack(result) + else + error(result[2]) + end + end + return true +end + +function Client.events (client, options) + local function client_events_iterator(s) + return s['client']:get_next_event(s['options']) + end + return client_events_iterator, {client = client, options = options} +end + +function Client.for_each_event (client, ...) + local options = parse_options({}, ...) + if not type(options.f) == 'function' then error('Expected function') end + for event in client: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 +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_* convenience methods for some type of queries +for _, query_type in ipairs({'software_version', 'disco_items', 'xml', 'dom', 'vcard'}) do + Client['get_' .. query_type] = function (client, options) + options = options or {} + if type(options) ~= 'table' then error('Invalid options: ' .. options) end + options['query'] = merge_tables({_type = query_type}, options['query'] or {}) + return client:get(options) + end +end + +function Client.pubsub (client, jid) + local result = { client = client, 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 (service, options) + return service.client:get_disco_items(merge_tables({to = service.jid}, options)) +end + +function PubSub.node (service, node) + local result = { client = service.client, jid = service.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 + +-------------------------------------------------------------------------------- +-- 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 (node, options) + return node.client:get_disco_items(merge_tables({to = node.jid, query = { node = node.node }}, options)) +end + +local simple_pubsub_node_queries = { + get_configuration = 'pubsub_owner_configure', + get_subscriptions = 'pubsub_subscriptions', + get_affiliations = 'pubsub_affiliations', + get_items = 'pubsub_items', + 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.create (node, options) + options = options or {} + local configure + if options['configuration'] then + configure = { data = pubsub_node_configuration_to_form(options['configuration']) } + end + return node.client:query_pubsub(merge_tables( + { type = 'set', to = node.jid, query = { + _type = 'pubsub_create', node = node.node, configure = configure } + }, options)) +end + +function PubSubNode.delete (node, options) + options = options or {} + local redirect + if options['redirect'] then + redirect = {uri = options['redirect']} + end + return node.client:query_pubsub(merge_tables({ type = 'set', to = node.jid, query = { + _type = 'pubsub_owner_delete', node = node.node, redirect = redirect + }}, options)) +end + +function PubSubNode.set_configuration(node, options) + options = options or {} + local configuration = pubsub_node_configuration_to_form(options['configuration']) + return node.client:query_pubsub(merge_tables( + { type = 'set', to = node.jid, query = { + _type = 'pubsub_owner_configure', node = node.node, data = configuration } + }, options)) +end + +function PubSubNode.subscribe(node, options) + options = options or {} + return node.client:query_pubsub(merge_tables( + { type = 'set', to = node.jid, query = { + _type = 'pubsub_subscribe', node = node.node, jid = options['jid'] } + }, options)) +end + +function PubSubNode.unsubscribe(node, options) + options = options or {} + return node.client:query_pubsub(merge_tables( + { type = 'set', to = node.jid, query = { + _type = 'pubsub_unsubscribe', node = node.node, jid = options['jid'] } + }, options)) +end + +function PubSubNode.get_subscription_options (node, options) + return node.client:query_pubsub(merge_tables( + { type = 'get', to = node.jid, query = { + _type = 'pubsub_options', node = node.node, jid = options['jid'] } + }, options)) +end + +function PubSubNode.publish(node, ...) + 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 node.client:query_pubsub(merge_tables( + { type = 'set', to = node.jid, query = { + _type = 'pubsub_publish', node = node.node, items = items } + }, options)) +end + +function PubSubNode.retract(node, options) + options = options or {} + local item_ids = options['items'] + item_ids = item_ids or { options['item'] } + local items = {} + for _, item_id in ipairs(item_ids) do + items[#items+1] = { id = item_id } + end + return node.client:query_pubsub(merge_tables( + { type = 'set', to = node.jid, query = { + _type = 'pubsub_retract', node = node.node, items = items, notify = options['notify'] + }}, options)) +end + +function PubSubNode.purge(node, options) + options = options or {} + return node.client:query_pubsub(merge_tables( + { type = 'set', to = node.jid, query = { + _type = 'pubsub_owner_purge', node = node.node + }}, options)) +end + +-------------------------------------------------------------------------------- +-- Service discovery +-------------------------------------------------------------------------------- + +local disco = { + features = { + DISCO_INFO = 'http://jabber.org/protocol/disco#info', + 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, +} |