diff options
| author | Remko Tronçon <git@el-tramo.be> | 2013-08-25 16:39:06 (GMT) | 
|---|---|---|
| committer | Remko Tronçon <git@el-tramo.be> | 2013-08-27 19:47:48 (GMT) | 
| commit | 1bb607f96e79845ce30dd5590b0d53cc394ac150 (patch) | |
| tree | 6156622ddd1b3238aec73536e0dc25b632965a71 /Sluift/boot.lua | |
| parent | c4431ee90f3f1daac0a12b35bfa3378d5c570eaa (diff) | |
| download | swift-1bb607f96e79845ce30dd5590b0d53cc394ac150.zip swift-1bb607f96e79845ce30dd5590b0d53cc394ac150.tar.bz2 | |
PubSub implementation & Sluift refactoring.
Change-Id: I04ff7111b73565c00bff6db183451774a633344f
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, +} | 
 Swift
 Swift