aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/modulemanager.lua156
-rw-r--r--core/stanza_router.lua10
-rw-r--r--main.lua13
-rw-r--r--plugins/mod_dialback.lua8
-rw-r--r--plugins/mod_disco.lua4
-rw-r--r--plugins/mod_legacyauth.lua4
-rw-r--r--plugins/mod_ping.lua2
-rw-r--r--plugins/mod_private.lua2
-rw-r--r--plugins/mod_register.lua4
-rw-r--r--plugins/mod_roster.lua4
-rw-r--r--plugins/mod_saslauth.lua12
-rw-r--r--plugins/mod_selftests.lua2
-rw-r--r--plugins/mod_time.lua4
-rw-r--r--plugins/mod_tls.lua4
-rw-r--r--plugins/mod_uptime.lua2
-rw-r--r--plugins/mod_vcard.lua4
-rw-r--r--plugins/mod_version.lua4
17 files changed, 143 insertions, 96 deletions
diff --git a/core/modulemanager.lua b/core/modulemanager.lua
index d313130c..704bd26f 100644
--- a/core/modulemanager.lua
+++ b/core/modulemanager.lua
@@ -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 @@ local debug = debug;
module "modulemanager"
-local handler_info = {};
-local handlers = {};
-
-local modulehelpers = setmetatable({}, { __index = _G });
+local api = {}; -- Module API container
-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
+local modulemap = {};
-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 handler_info = {};
+local stanza_handlers = {};
-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
+local modulehelpers = setmetatable({}, { __index = _G });
-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 @@ function handle_stanza(origin, stanza)
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 @@ do
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;
diff --git a/core/stanza_router.lua b/core/stanza_router.lua
index 2505fca3..308ae2f4 100644
--- a/core/stanza_router.lua
+++ b/core/stanza_router.lua
@@ -1,10 +1,4 @@
--- 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")
local st = require "util.stanza";
@@ -82,7 +76,7 @@ function core_process_stanza(origin, stanza)
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 @@ end
-- 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;
diff --git a/main.lua b/main.lua
index 053dff74..3ea97ca4 100644
--- a/main.lua
+++ b/main.lua
@@ -63,10 +63,15 @@ require "util.jid"
------------------------------------------------------------------------
-- 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
diff --git a/plugins/mod_dialback.lua b/plugins/mod_dialback.lua
index 87ac303b..e7804962 100644
--- a/plugins/mod_dialback.lua
+++ b/plugins/mod_dialback.lua
@@ -8,7 +8,7 @@ local log = require "util.logger".init("mod_dialback");
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 @@ add_handler({"s2sin_unauthed", "s2sin"}, "verify", xmlns_dialback,
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 @@ add_handler("s2sin_unauthed", "result", xmlns_dialback,
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 @@ add_handler({ "s2sout_unauthed", "s2sout" }, "verify", xmlns_dialback,
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);
diff --git a/plugins/mod_disco.lua b/plugins/mod_disco.lua
index fd93ecf3..fd9f2836 100644
--- a/plugins/mod_disco.lua
+++ b/plugins/mod_disco.lua
@@ -4,9 +4,9 @@ local discomanager_handle = require "core.discomanager".handle;
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);
diff --git a/plugins/mod_legacyauth.lua b/plugins/mod_legacyauth.lua
index 02621af7..81e2ff7f 100644
--- a/plugins/mod_legacyauth.lua
+++ b/plugins/mod_legacyauth.lua
@@ -4,7 +4,7 @@ local t_concat = table.concat;
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 @@ add_iq_handler("c2s_unauthed", "jabber:iq:auth",
end
end
- end); \ No newline at end of file
+ end);
diff --git a/plugins/mod_ping.lua b/plugins/mod_ping.lua
index c13282ff..8306078c 100644
--- a/plugins/mod_ping.lua
+++ b/plugins/mod_ping.lua
@@ -3,7 +3,7 @@ local st = require "util.stanza";
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));
diff --git a/plugins/mod_private.lua b/plugins/mod_private.lua
index f07f8197..dd717d8d 100644
--- a/plugins/mod_private.lua
+++ b/plugins/mod_private.lua
@@ -6,7 +6,7 @@ local datamanager = require "util.datamanager"
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];
diff --git a/plugins/mod_register.lua b/plugins/mod_register.lua
index 0974725d..5769bd80 100644
--- a/plugins/mod_register.lua
+++ b/plugins/mod_register.lua
@@ -6,7 +6,7 @@ local datamanager_store = require "util.datamanager".store;
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 @@ add_iq_handler("c2s", "jabber:iq:register", function (session, stanza)
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
diff --git a/plugins/mod_roster.lua b/plugins/mod_roster.lua
index 23223a65..0b52e43e 100644
--- a/plugins/mod_roster.lua
+++ b/plugins/mod_roster.lua
@@ -11,7 +11,7 @@ local rm_roster_push = require "core.rostermanager".roster_push;
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 @@ add_iq_handler("c2s", "jabber:iq:roster",
return true;
end
end
- end); \ No newline at end of file
+ end);
diff --git a/plugins/mod_saslauth.lua b/plugins/mod_saslauth.lua
index 7ca4308b..f549d0b9 100644
--- a/plugins/mod_saslauth.lua
+++ b/plugins/mod_saslauth.lua
@@ -79,14 +79,14 @@ function sasl_handler(session, stanza)
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 @@ add_event_hook("stream-features",
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 @@ add_iq_handler("c2s", "urn:ietf:params:xml:ns:xmpp-bind",
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));
diff --git a/plugins/mod_selftests.lua b/plugins/mod_selftests.lua
index 8aa70952..4d44d2fd 100644
--- a/plugins/mod_selftests.lua
+++ b/plugins/mod_selftests.lua
@@ -36,7 +36,7 @@ if tests_jid and host then
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' };
diff --git a/plugins/mod_time.lua b/plugins/mod_time.lua
index bf079692..68224421 100644
--- a/plugins/mod_time.lua
+++ b/plugins/mod_time.lua
@@ -7,7 +7,7 @@ local legacy = require "util.datetime".legacy;
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 @@ add_iq_handler({"c2s", "s2sin"}, "urn:xmpp:time",
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"})
diff --git a/plugins/mod_tls.lua b/plugins/mod_tls.lua
index cc46d556..d5f3157b 100644
--- a/plugins/mod_tls.lua
+++ b/plugins/mod_tls.lua
@@ -9,7 +9,7 @@ local log = require "util.logger".init("mod_starttls");
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 @@ add_handler("c2s_unauthed", "starttls", xmlns_starttls,
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();
diff --git a/plugins/mod_uptime.lua b/plugins/mod_uptime.lua
index 9c2cb45d..4b4c28d1 100644
--- a/plugins/mod_uptime.lua
+++ b/plugins/mod_uptime.lua
@@ -8,7 +8,7 @@ local start_time = os.time();
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
diff --git a/plugins/mod_vcard.lua b/plugins/mod_vcard.lua
index 1d9b3d63..844a2406 100644
--- a/plugins/mod_vcard.lua
+++ b/plugins/mod_vcard.lua
@@ -10,7 +10,7 @@ local jid_split = jid.split;
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 @@ add_iq_handler({"c2s", "s2sin"}, "vcard-temp",
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();
diff --git a/plugins/mod_version.lua b/plugins/mod_version.lua
index 2f68fa07..11278a53 100644
--- a/plugins/mod_version.lua
+++ b/plugins/mod_version.lua
@@ -16,5 +16,5 @@ local function handle_version_request(session, stanza)
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);