Bumper commit for the new modulemanager API \o/ Updates all the modules, though some more changes may be in store.

Thu, 27 Nov 2008 03:12:12 +0000

author
Matthew Wild <mwild1@gmail.com>
date
Thu, 27 Nov 2008 03:12:12 +0000
changeset 438
193f9dd64f17
parent 437
c1a720db2157
child 439
6608ad3a72f3
child 441
4089b62b510c

Bumper commit for the new modulemanager API \o/ Updates all the modules, though some more changes may be in store.

core/modulemanager.lua file | annotate | diff | comparison | revisions
core/stanza_router.lua file | annotate | diff | comparison | revisions
main.lua file | annotate | diff | comparison | revisions
plugins/mod_dialback.lua file | annotate | diff | comparison | revisions
plugins/mod_disco.lua file | annotate | diff | comparison | revisions
plugins/mod_legacyauth.lua file | annotate | diff | comparison | revisions
plugins/mod_ping.lua file | annotate | diff | comparison | revisions
plugins/mod_private.lua file | annotate | diff | comparison | revisions
plugins/mod_register.lua file | annotate | diff | comparison | revisions
plugins/mod_roster.lua file | annotate | diff | comparison | revisions
plugins/mod_saslauth.lua file | annotate | diff | comparison | revisions
plugins/mod_selftests.lua file | annotate | diff | comparison | revisions
plugins/mod_time.lua file | annotate | diff | comparison | revisions
plugins/mod_tls.lua file | annotate | diff | comparison | revisions
plugins/mod_uptime.lua file | annotate | diff | comparison | revisions
plugins/mod_vcard.lua file | annotate | diff | comparison | revisions
plugins/mod_version.lua file | annotate | diff | comparison | revisions
--- a/core/modulemanager.lua	Thu Nov 27 03:06:29 2008 +0000
+++ b/core/modulemanager.lua	Thu Nov 27 03:12:12 2008 +0000
@@ -1,5 +1,7 @@
 
-local log = require "util.logger".init("modulemanager")
+
+local logger = require "util.logger";
+local log = logger.init("modulemanager")
 
 local loadfile, pcall = loadfile, pcall;
 local setmetatable, setfenv, getfenv = setmetatable, setfenv, getfenv;
@@ -14,80 +16,59 @@
 
 module "modulemanager"
 
+local api = {}; -- Module API container
+
+local modulemap = {};
+
 local handler_info = {};
-local handlers = {};
-					
+local stanza_handlers = {};
+
 local modulehelpers = setmetatable({}, { __index = _G });
 
-local function _add_iq_handler(module, origin_type, xmlns, handler)
-	handlers[origin_type] = handlers[origin_type] or {};
-	handlers[origin_type].iq = handlers[origin_type].iq or {};
-	if not handlers[origin_type].iq[xmlns] then
-		handlers[origin_type].iq[xmlns]= handler;
-		handler_info[handler] = module;
-		log("debug", "mod_%s now handles tag 'iq' with query namespace '%s'", module.name, xmlns);
-	else
-		log("warning", "mod_%s wants to handle tag 'iq' with query namespace '%s' but mod_%s already handles that", module.name, xmlns, handler_info[handlers[origin_type].iq[xmlns]].module.name);
-	end
-end
 
-function modulehelpers.add_iq_handler(origin_type, xmlns, handler)
-	if not (origin_type and handler and xmlns) then return false; end
-	if type(origin_type) == "table" then
-		for _, origin_type in ipairs(origin_type) do
-			_add_iq_handler(getfenv(2).module, origin_type, xmlns, handler);
-		end
-		return;
-	end
-	_add_iq_handler(getfenv(2).module, origin_type, xmlns, handler);
-end
-
-local function _add_handler(module, origin_type, tag, xmlns, handler)
-	handlers[origin_type] = handlers[origin_type] or {};
-	if not handlers[origin_type][tag] then
-		handlers[origin_type][tag] = handlers[origin_type][tag] or {};
-		handlers[origin_type][tag][xmlns]= handler;
-		handler_info[handler] = module;
-		log("debug", "mod_%s now handles tag '%s'", module.name, tag);
-	elseif handler_info[handlers[origin_type][tag]] then
-		log("warning", "mod_%s wants to handle tag '%s' but mod_%s already handles that", module.name, tag, handler_info[handlers[origin_type][tag]].module.name);
-	end
-end
-
-function modulehelpers.add_handler(origin_type, tag, xmlns, handler)
-	if not (origin_type and tag and xmlns and handler) then return false; end
-	if type(origin_type) == "table" then
-		for _, origin_type in ipairs(origin_type) do
-			_add_handler(getfenv(2).module, origin_type, tag, xmlns, handler);
-		end
-		return;
-	end
-	_add_handler(getfenv(2).module, origin_type, tag, xmlns, handler);
-end
-
-function load(name)
-	local mod, err = loadfile("plugins/mod_"..name..".lua");
+function load(host, module_name, config)
+	local mod, err = loadfile("plugins/mod_"..module_name..".lua");
 	if not mod then
-		log("error", "Unable to load module '%s': %s", name or "nil", err or "nil");
+		log("error", "Unable to load module '%s': %s", module_name or "nil", err or "nil");
 		return nil, err;
 	end
 	
-	local pluginenv = setmetatable({ module = { name = name } }, { __index = modulehelpers });
+	if not modulemap[host] then
+		modulemap[host] = {};
+		stanza_handlers[host] = {};
+	elseif modulemap[host][module_name] then
+		log("warn", "%s is already loaded for %s, so not loading again", module_name, host);
+		return nil, "module-already-loaded";
+	end
+	
+	local _log = logger.init(host..":"..module_name);
+	local api_instance = setmetatable({ name = module_name, host = host, config = config,  _log = _log, log = function (self, ...) return _log(...); end }, { __index = api });
+
+	local pluginenv = setmetatable({ module = api_instance }, { __index = _G });
 	
 	setfenv(mod, pluginenv);
+	
 	local success, ret = pcall(mod);
 	if not success then
 		log("error", "Error initialising module '%s': %s", name or "nil", ret or "nil");
 		return nil, ret;
 	end
+	
+	modulemap[host][module_name] = mod;
+	
 	return true;
 end
 
-function handle_stanza(origin, stanza)
+function handle_stanza(host, origin, stanza)
 	local name, xmlns, origin_type = stanza.name, stanza.attr.xmlns, origin.type;
 	
+	local handlers = stanza_handlers[host];
+	if not handlers then
+		log("warn", "No handlers for %s", host);
+		return false;
+	end
+	
 	if name == "iq" and xmlns == "jabber:client" and handlers[origin_type] then
-		log("debug", "Stanza is an <iq/>");
 		local child = stanza.tags[1];
 		if child then
 			local xmlns = child.attr.xmlns or xmlns;
@@ -112,14 +93,54 @@
 	return false; -- we didn't handle it
 end
 
+----- API functions exposed to modules -----------
+-- Must all be in api.* 
+
+-- Returns the name of the current module
+function api:get_name()
+	return self.name;
+end
+
+-- Returns the host that the current module is serving
+function api:get_host()
+	return self.host;
+end
+
+
+local function _add_iq_handler(module, origin_type, xmlns, handler)
+	local handlers = stanza_handlers[module.host];
+	handlers[origin_type] = handlers[origin_type] or {};
+	handlers[origin_type].iq = handlers[origin_type].iq or {};
+	if not handlers[origin_type].iq[xmlns] then
+		handlers[origin_type].iq[xmlns]= handler;
+		handler_info[handler] = module;
+		module:log("debug", "I now handle tag 'iq' [%s] with payload namespace '%s'", origin_type, xmlns);
+	else
+		module:log("warn", "I wanted to handle tag 'iq' [%s] with payload namespace '%s' but mod_%s already handles that", origin_type, xmlns, handler_info[handlers[origin_type].iq[xmlns]].name);
+	end
+end
+
+function api:add_iq_handler(origin_type, xmlns, handler)
+	if not (origin_type and handler and xmlns) then return false; end
+	if type(origin_type) == "table" then
+		for _, origin_type in ipairs(origin_type) do
+			_add_iq_handler(self, origin_type, xmlns, handler);
+		end
+		return;
+	end
+	_add_iq_handler(self, origin_type, xmlns, handler);
+end
+
+
 do
 	local event_handlers = {};
 	
-	function modulehelpers.add_event_hook(name, handler)
+	function api:add_event_hook(name, handler)
 		if not event_handlers[name] then
 			event_handlers[name] = {};
 		end
 		t_insert(event_handlers[name] , handler);
+		self:log("debug", "Subscribed to %s", name);
 	end
 	
 	function fire_event(name, ...)
@@ -132,4 +153,31 @@
 	end
 end
 
+
+local function _add_handler(module, origin_type, tag, xmlns, handler)
+	local handlers = stanza_handlers[module.host];
+	handlers[origin_type] = handlers[origin_type] or {};
+	if not handlers[origin_type][tag] then
+		handlers[origin_type][tag] = handlers[origin_type][tag] or {};
+		handlers[origin_type][tag][xmlns]= handler;
+		handler_info[handler] = module;
+		module:log("debug", "I now handle tag '%s' [%s] with xmlns '%s'", tag, origin_type, xmlns);
+	elseif handler_info[handlers[origin_type][tag]] then
+		log("warning", "I wanted to handle tag '%s' [%s] but mod_%s already handles that", tag, origin_type, handler_info[handlers[origin_type][tag]].module.name);
+	end
+end
+
+function api:add_handler(origin_type, tag, xmlns, handler)
+	if not (origin_type and tag and xmlns and handler) then return false; end
+	if type(origin_type) == "table" then
+		for _, origin_type in ipairs(origin_type) do
+			_add_handler(self, origin_type, tag, xmlns, handler);
+		end
+		return;
+	end
+	_add_handler(self, origin_type, tag, xmlns, handler);
+end
+
+--------------------------------------------------------------------
+
 return _M;
--- a/core/stanza_router.lua	Thu Nov 27 03:06:29 2008 +0000
+++ b/core/stanza_router.lua	Thu Nov 27 03:12:12 2008 +0000
@@ -1,9 +1,3 @@
-
--- The code in this file should be self-explanatory, though the logic is horrible
--- for more info on that, see doc/stanza_routing.txt, which attempts to condense
--- the rules from the RFCs (mainly 3921)
-
-require "core.servermanager"
 
 local log = require "util.logger".init("stanzarouter")
 
@@ -82,7 +76,7 @@
 		elseif hosts[to] and hosts[to].type == "local" then -- directed at a local server
 			core_handle_stanza(origin, stanza);
 		elseif stanza.attr.xmlns and stanza.attr.xmlns ~= "jabber:client" and stanza.attr.xmlns ~= "jabber:server" then
-			modules_handle_stanza(origin, stanza);
+			modules_handle_stanza(host or origin.host or origin.to_host, origin, stanza);
 		elseif hosts[to_bare] and hosts[to_bare].type == "component" then -- hack to allow components to handle node@server
 			component_handle_stanza(origin, stanza);
 		elseif hosts[to] and hosts[to].type == "component" then -- hack to allow components to handle node@server/resource and server/resource
@@ -105,7 +99,7 @@
 -- that is, they are handled by this server
 function core_handle_stanza(origin, stanza)
 	-- Handlers
-	if modules_handle_stanza(origin, stanza) then return; end
+	if modules_handle_stanza(stanza.attr.to or origin.host, origin, stanza) then return; end
 	if origin.type == "c2s" or origin.type == "c2s_unauthed" then
 		local session = origin;
 
--- a/main.lua	Thu Nov 27 03:06:29 2008 +0000
+++ b/main.lua	Thu Nov 27 03:12:12 2008 +0000
@@ -63,10 +63,15 @@
 ------------------------------------------------------------------------
 
 -- Initialise modules
-local modules_enabled = config.get("*", "core", "modules_enabled");
-if modules_enabled then
-	for _, module in pairs(modules_enabled) do
-		modulemanager.load(module);
+
+for host in pairs(hosts) do
+	if host ~= "*" then
+		local modules_enabled = config.get(host, "core", "modules_enabled");
+		if modules_enabled then
+			for _, module in pairs(modules_enabled) do
+				modulemanager.load(host, module);
+			end
+		end
 	end
 end
 
--- a/plugins/mod_dialback.lua	Thu Nov 27 03:06:29 2008 +0000
+++ b/plugins/mod_dialback.lua	Thu Nov 27 03:12:12 2008 +0000
@@ -8,7 +8,7 @@
 
 local xmlns_dialback = "jabber:server:dialback";
 
-add_handler({"s2sin_unauthed", "s2sin"}, "verify", xmlns_dialback,
+module:add_handler({"s2sin_unauthed", "s2sin"}, "verify", xmlns_dialback,
 	function (origin, stanza)
 		-- We are being asked to verify the key, to ensure it was generated by us
 		log("debug", "verifying dialback key...");
@@ -26,7 +26,7 @@
 		origin.sends2s(format("<db:verify from='%s' to='%s' id='%s' type='%s'>%s</db:verify>", attr.to, attr.from, attr.id, type, stanza[1]));
 	end);
 
-add_handler("s2sin_unauthed", "result", xmlns_dialback,
+module:add_handler("s2sin_unauthed", "result", xmlns_dialback,
 	function (origin, stanza)
 		-- he wants to be identified through dialback
 		-- We need to check the key with the Authoritative server
@@ -42,7 +42,7 @@
 		hosts[origin.to_host].s2sout[origin.from_host].dialback_verifying = origin;
 	end);
 
-add_handler({ "s2sout_unauthed", "s2sout" }, "verify", xmlns_dialback,
+module:add_handler({ "s2sout_unauthed", "s2sout" }, "verify", xmlns_dialback,
 	function (origin, stanza)
 		if origin.dialback_verifying then
 			local valid;
@@ -64,7 +64,7 @@
 		end
 	end);
 
-add_handler({ "s2sout_unauthed", "s2sout" }, "result", xmlns_dialback,
+module:add_handler({ "s2sout_unauthed", "s2sout" }, "result", xmlns_dialback,
 	function (origin, stanza)
 		if stanza.attr.type == "valid" then
 			s2s_make_authenticated(origin);
--- a/plugins/mod_disco.lua	Thu Nov 27 03:06:29 2008 +0000
+++ b/plugins/mod_disco.lua	Thu Nov 27 03:12:12 2008 +0000
@@ -4,9 +4,9 @@
 require "core.discomanager".set("disco", "http://jabber.org/protocol/disco#info");
 require "core.discomanager".set("disco", "http://jabber.org/protocol/disco#items");
 
-add_iq_handler({"c2s", "s2sin"}, "http://jabber.org/protocol/disco#info", function (session, stanza)
+module:add_iq_handler({"c2s", "s2sin"}, "http://jabber.org/protocol/disco#info", function (session, stanza)
 	session.send(discomanager_handle(stanza));
 end);
-add_iq_handler({"c2s", "s2sin"}, "http://jabber.org/protocol/disco#items", function (session, stanza)
+module:add_iq_handler({"c2s", "s2sin"}, "http://jabber.org/protocol/disco#items", function (session, stanza)
 	session.send(discomanager_handle(stanza));
 end);
--- a/plugins/mod_legacyauth.lua	Thu Nov 27 03:06:29 2008 +0000
+++ b/plugins/mod_legacyauth.lua	Thu Nov 27 03:12:12 2008 +0000
@@ -4,7 +4,7 @@
 
 require "core.discomanager".set("legacyauth", "jabber:iq:auth");
 
-add_iq_handler("c2s_unauthed", "jabber:iq:auth", 
+module:add_iq_handler("c2s_unauthed", "jabber:iq:auth", 
 		function (session, stanza)
 			local username = stanza.tags[1]:child_with_name("username");
 			local password = stanza.tags[1]:child_with_name("password");
@@ -43,4 +43,4 @@
 				end
 			end
 			
-		end);
\ No newline at end of file
+		end);
--- a/plugins/mod_ping.lua	Thu Nov 27 03:06:29 2008 +0000
+++ b/plugins/mod_ping.lua	Thu Nov 27 03:12:12 2008 +0000
@@ -3,7 +3,7 @@
 
 require "core.discomanager".set("ping", "urn:xmpp:ping");
 
-add_iq_handler({"c2s", "s2sin"}, "urn:xmpp:ping",
+module:add_iq_handler({"c2s", "s2sin"}, "urn:xmpp:ping",
 	function(session, stanza)
 		if stanza.attr.type == "get" then
 			session.send(st.reply(stanza));
--- a/plugins/mod_private.lua	Thu Nov 27 03:06:29 2008 +0000
+++ b/plugins/mod_private.lua	Thu Nov 27 03:12:12 2008 +0000
@@ -6,7 +6,7 @@
 
 require "core.discomanager".set("private", "jabber:iq:private");
 
-add_iq_handler("c2s", "jabber:iq:private",
+module:add_iq_handler("c2s", "jabber:iq:private",
 	function (session, stanza)
 		local type = stanza.attr.type;
 		local query = stanza.tags[1];
--- a/plugins/mod_register.lua	Thu Nov 27 03:06:29 2008 +0000
+++ b/plugins/mod_register.lua	Thu Nov 27 03:12:12 2008 +0000
@@ -6,7 +6,7 @@
 
 require "core.discomanager".set("register", "jabber:iq:register");
 
-add_iq_handler("c2s", "jabber:iq:register", function (session, stanza)
+module:add_iq_handler("c2s", "jabber:iq:register", function (session, stanza)
 	if stanza.tags[1].name == "query" then
 		local query = stanza.tags[1];
 		if stanza.attr.type == "get" then
@@ -73,7 +73,7 @@
 	end;
 end);
 
-add_iq_handler("c2s_unauthed", "jabber:iq:register", function (session, stanza)
+module:add_iq_handler("c2s_unauthed", "jabber:iq:register", function (session, stanza)
 	if stanza.tags[1].name == "query" then
 		local query = stanza.tags[1];
 		if stanza.attr.type == "get" then
--- a/plugins/mod_roster.lua	Thu Nov 27 03:06:29 2008 +0000
+++ b/plugins/mod_roster.lua	Thu Nov 27 03:12:12 2008 +0000
@@ -11,7 +11,7 @@
 
 require "core.discomanager".set("roster", "jabber:iq:roster");
 
-add_iq_handler("c2s", "jabber:iq:roster", 
+module:add_iq_handler("c2s", "jabber:iq:roster", 
 		function (session, stanza)
 			if stanza.tags[1].name == "query" then
 				if stanza.attr.type == "get" then
@@ -103,4 +103,4 @@
 					return true;
 				end
 			end
-		end);
\ No newline at end of file
+		end);
--- a/plugins/mod_saslauth.lua	Thu Nov 27 03:06:29 2008 +0000
+++ b/plugins/mod_saslauth.lua	Thu Nov 27 03:12:12 2008 +0000
@@ -79,14 +79,14 @@
 	session.send(s);
 end
 
-add_handler("c2s_unauthed", "auth", xmlns_sasl, sasl_handler);
-add_handler("c2s_unauthed", "abort", xmlns_sasl, sasl_handler);
-add_handler("c2s_unauthed", "response", xmlns_sasl, sasl_handler);
+module:add_handler("c2s_unauthed", "auth", xmlns_sasl, sasl_handler);
+module:add_handler("c2s_unauthed", "abort", xmlns_sasl, sasl_handler);
+module:add_handler("c2s_unauthed", "response", xmlns_sasl, sasl_handler);
 
 local mechanisms_attr = { xmlns='urn:ietf:params:xml:ns:xmpp-sasl' };
 local bind_attr = { xmlns='urn:ietf:params:xml:ns:xmpp-bind' };
 local xmpp_session_attr = { xmlns='urn:ietf:params:xml:ns:xmpp-session' };
-add_event_hook("stream-features", 
+module:add_event_hook("stream-features", 
 					function (session, features)												
 						if not session.username then
 							features:tag("mechanisms", mechanisms_attr);
@@ -100,7 +100,7 @@
 						end
 					end);
 					
-add_iq_handler("c2s", "urn:ietf:params:xml:ns:xmpp-bind", 
+module:add_iq_handler("c2s", "urn:ietf:params:xml:ns:xmpp-bind", 
 		function (session, stanza)
 			log("debug", "Client tried to bind to a resource");
 			local resource;
@@ -123,7 +123,7 @@
 			end
 		end);
 		
-add_iq_handler("c2s", "urn:ietf:params:xml:ns:xmpp-session", 
+module:add_iq_handler("c2s", "urn:ietf:params:xml:ns:xmpp-session", 
 		function (session, stanza)
 			log("debug", "Client tried to bind to a resource");
 			session.send(st.reply(stanza));
--- a/plugins/mod_selftests.lua	Thu Nov 27 03:06:29 2008 +0000
+++ b/plugins/mod_selftests.lua	Thu Nov 27 03:12:12 2008 +0000
@@ -36,7 +36,7 @@
 
 
 	local our_origin = hosts[host];
-	add_event_hook("server-started", 
+	module:add_event_hook("server-started", 
 					function ()
 						local id = st.new_id();
 						local ping_attr = { xmlns = 'urn:xmpp:ping' };
--- a/plugins/mod_time.lua	Thu Nov 27 03:06:29 2008 +0000
+++ b/plugins/mod_time.lua	Thu Nov 27 03:12:12 2008 +0000
@@ -7,7 +7,7 @@
 
 require "core.discomanager".set("time", "urn:xmpp:time");
 
-add_iq_handler({"c2s", "s2sin"}, "urn:xmpp:time",
+module:add_iq_handler({"c2s", "s2sin"}, "urn:xmpp:time",
 	function(session, stanza)
 		if stanza.attr.type == "get" then
 			session.send(st.reply(stanza):tag("time", {xmlns="urn:xmpp:time"})
@@ -20,7 +20,7 @@
 
 require "core.discomanager".set("time", "jabber:iq:time");
 
-add_iq_handler({"c2s", "s2sin"}, "jabber:iq:time",
+module:add_iq_handler({"c2s", "s2sin"}, "jabber:iq:time",
 	function(session, stanza)
 		if stanza.attr.type == "get" then
 			session.send(st.reply(stanza):tag("query", {xmlns="jabber:iq:time"})
--- a/plugins/mod_tls.lua	Thu Nov 27 03:06:29 2008 +0000
+++ b/plugins/mod_tls.lua	Thu Nov 27 03:12:12 2008 +0000
@@ -9,7 +9,7 @@
 
 local xmlns_starttls ='urn:ietf:params:xml:ns:xmpp-tls';
 
-add_handler("c2s_unauthed", "starttls", xmlns_starttls,
+module:add_handler("c2s_unauthed", "starttls", xmlns_starttls,
 		function (session, stanza)
 			if session.conn.starttls then
 				session.send(st.stanza("proceed", { xmlns = xmlns_starttls }));
@@ -25,7 +25,7 @@
 		end);
 		
 local starttls_attr = { xmlns = xmlns_starttls };
-add_event_hook("stream-features", 
+module:add_event_hook("stream-features", 
 					function (session, features)												
 						if session.conn.starttls then
 							features:tag("starttls", starttls_attr):up();
--- a/plugins/mod_uptime.lua	Thu Nov 27 03:06:29 2008 +0000
+++ b/plugins/mod_uptime.lua	Thu Nov 27 03:12:12 2008 +0000
@@ -8,7 +8,7 @@
 
 require "core.discomanager".set("uptime", "jabber:iq:last");
 
-add_iq_handler({"c2s", "s2sin"}, "jabber:iq:last", 
+module:add_iq_handler({"c2s", "s2sin"}, "jabber:iq:last", 
 	function (origin, stanza)
 		if stanza.tags[1].name == "query" then
 			if stanza.attr.type == "get" then
--- a/plugins/mod_vcard.lua	Thu Nov 27 03:06:29 2008 +0000
+++ b/plugins/mod_vcard.lua	Thu Nov 27 03:12:12 2008 +0000
@@ -10,7 +10,7 @@
 
 require "core.discomanager".set("vcard", "vcard-temp");
 
-add_iq_handler({"c2s", "s2sin"}, "vcard-temp", 
+module:add_iq_handler({"c2s", "s2sin"}, "vcard-temp", 
 		function (session, stanza)
 			if stanza.tags[1].name == "vCard" then
 				local to = stanza.attr.to;
@@ -46,7 +46,7 @@
 		end);
 
 local feature_vcard_attr = { var='vcard-temp' };
-add_event_hook("stream-features", 
+module:add_event_hook("stream-features", 
 					function (session, features)												
 						if session.type == "c2s" then
 							features:tag("feature", feature_vcard_attr):up();
--- a/plugins/mod_version.lua	Thu Nov 27 03:06:29 2008 +0000
+++ b/plugins/mod_version.lua	Thu Nov 27 03:12:12 2008 +0000
@@ -16,5 +16,5 @@
 	end
 end
 
-add_iq_handler("c2s", xmlns_version, handle_version_request);
-add_iq_handler("s2sin", xmlns_version, handle_version_request);
+module:add_iq_handler("c2s", xmlns_version, handle_version_request);
+module:add_iq_handler("s2sin", xmlns_version, handle_version_request);

mercurial