summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Sluift/boot.lua')
-rw-r--r--Sluift/boot.lua381
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,
+}