Diff

mod_delegation/mod_delegation.lua @ 1709:59ba224bf145

mod_delegation: XEP-0355 implementation, first draft (configuration management + delegations advertising)
author Goffi <goffi@goffi.org>
date Fri, 17 Apr 2015 21:02:22 +0200
child 1710:b68eed25b880
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_delegation/mod_delegation.lua	Fri Apr 17 21:02:22 2015 +0200
@@ -0,0 +1,134 @@
+-- XEP-0355 (Namespace Delegation)
+-- Copyright (C) 2015 Jérôme Poisson
+--
+-- This module is MIT/X11 licensed. Please see the
+-- COPYING file in the source package for more information.
+
+
+local jid = require("util/jid")
+local st = require("util/stanza")
+local set = require("util/set")
+
+local delegation_session = module:shared("/*/delegation/session")
+
+if delegation_session.connected_cb == nil then
+	-- set used to have connected event listeners
+	-- which allow a host to react on events from
+	-- other hosts
+	delegation_session.connected_cb = set.new()
+end
+local connected_cb = delegation_session.connected_cb
+
+local _DELEGATION_NS = 'urn:xmpp:delegation:1'
+-- local _FORWARDED_NS = 'urn:xmpp:forward:0'
+
+
+module:log("debug", "Loading namespace delegation module ");
+
+
+--> Configuration management <--
+
+local ns_delegations = module:get_option("delegations", {})
+
+local jid2ns = {}
+for namespace, config in pairs(ns_delegations) do
+	-- "connected" contain the full jid of connected managing entity
+	config.connected = nil
+	if config.jid then
+		if jid2ns[config.jid] == nil then
+			jid2ns[config.jid] = {}
+		end
+		jid2ns[config.jid][namespace] = config
+	else
+		module:log("warn", "Ignoring delegation for %s: no jid specified", tostring(namespace))
+		ns_delegations[namespace] = nil
+	end
+end
+
+
+local function advertise_delegations(session, to_jid)
+	-- send <message/> stanza to advertise delegations
+	-- as expained in § 4.2
+	local message = st.message({from=module.host, to=to_jid})
+					  :tag("delegation", {xmlns=_DELEGATION_NS})
+
+	-- we need to check if a delegation is granted because the configuration
+	-- can be complicated if some delegations are granted to bare jid
+	-- and other to full jids, and several resources are connected.
+	local have_delegation = false
+
+	for namespace, config  in pairs(jid2ns[to_jid]) do
+		if config.connected == to_jid then
+			have_delegation = true
+			message:tag("delegated", {namespace=namespace})
+			if type(config.filtering) == "table" then
+				for _, attribute in pairs(config.filtering) do
+					message:tag("attribute", {name=attribute}):up()
+				end
+				message:up()
+			end
+		end
+	end
+
+	if have_delegation then
+		session.send(message)
+	end
+end
+
+local function set_connected(entity_jid)
+	-- set the "connected" key for all namespace managed by entity_jid
+	-- if the namespace has already a connected entity, ignore the new one
+	local function set_config(jid_)
+		for _, config in pairs(jid2ns[jid_]) do
+			if config.connected == nil then
+				config.connected = entity_jid
+			end
+		end
+	end
+	local bare_jid = jid.bare(entity_jid)
+	set_config(bare_jid)
+	-- We can have a bare jid of a full jid specified in configuration
+	-- so we try our luck with both (first connected resource will
+	-- manage the namespaces in case of bare jid)
+	if bare_jid ~= entity_jid then
+		set_config(entity_jid)
+		jid2ns[entity_jid] = jid2ns[bare_jid]
+	end
+end
+
+local function on_presence(event)
+	local session = event.origin
+	local bare_jid = jid.bare(session.full_jid)
+
+	if jid2ns[bare_jid] or jid2ns[session.full_jid] then
+		set_connected(session.full_jid)
+		advertise_delegations(session, session.full_jid)
+	end
+end
+
+local function on_component_connected(event)
+	-- method called by the module loaded by the component
+	-- /!\ the event come from the component host,
+	-- not from the host of this module
+	local session = event.session
+	local bare_jid = jid.join(session.username, session.host)
+
+	local jid_delegations = jid2ns[bare_jid]
+	if jid_delegations ~= nil then
+		set_connected(bare_jid)
+		advertise_delegations(session, bare_jid)
+	end
+end
+
+local function on_component_auth(event)
+	-- react to component-authenticated event from this host
+	-- and call the on_connected methods from all other hosts
+	-- needed for the component to get delegations advertising
+	for callback in connected_cb:items() do
+		callback(event)
+	end
+end
+
+connected_cb:add(on_component_connected)
+module:hook('component-authenticated', on_component_auth)
+module:hook('presence/initial', on_presence)