aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/configmanager.lua121
-rw-r--r--core/discomanager.lua39
-rw-r--r--core/modulemanager.lua6
-rw-r--r--core/presencemanager.lua121
-rw-r--r--core/s2smanager.lua83
-rw-r--r--core/servermanager.lua2
-rw-r--r--core/sessionmanager.lua42
-rw-r--r--core/stanza_router.lua132
-rw-r--r--core/xmlhandlers.lua19
9 files changed, 394 insertions, 171 deletions
diff --git a/core/configmanager.lua b/core/configmanager.lua
new file mode 100644
index 00000000..5f5648b9
--- /dev/null
+++ b/core/configmanager.lua
@@ -0,0 +1,121 @@
+
+local _G = _G;
+local setmetatable, loadfile, pcall, rawget, rawset, io =
+ setmetatable, loadfile, pcall, rawget, rawset, io;
+module "configmanager"
+
+local parsers = {};
+
+local config = { ["*"] = { core = {} } };
+
+local global_config = config["*"];
+
+-- When host not found, use global
+setmetatable(config, { __index = function () return global_config; end});
+local host_mt = { __index = global_config };
+
+-- When key not found in section, check key in global's section
+function section_mt(section_name)
+ return { __index = function (t, k)
+ local section = rawget(global_config, section_name);
+ if not section then return nil; end
+ return section[k];
+ end };
+end
+
+function getconfig()
+ return config;
+end
+
+function get(host, section, key)
+ local sec = config[host][section];
+ if sec then
+ return sec[key];
+ end
+ return nil;
+end
+
+function set(host, section, key, value)
+ if host and section and key then
+ local hostconfig = rawget(config, host);
+ if not hostconfig then
+ hostconfig = rawset(config, host, setmetatable({}, host_mt))[host];
+ end
+ if not rawget(hostconfig, section) then
+ hostconfig[section] = setmetatable({}, section_mt(section));
+ end
+ hostconfig[section][key] = value;
+ return true;
+ end
+ return false;
+end
+
+function load(filename, format)
+ format = format or filename:match("%w+$");
+ if parsers[format] and parsers[format].load then
+ local f = io.open(filename);
+ if f then
+ local ok, err = parsers[format].load(f:read("*a"));
+ f:close();
+ return ok, err;
+ end
+ end
+ if not format then
+ return nil, "no parser specified";
+ else
+ return false, "no parser";
+ end
+end
+
+function save(filename, format)
+end
+
+function addparser(format, parser)
+ if format and parser then
+ parsers[format] = parser;
+ end
+end
+
+-- Built-in Lua parser
+do
+ local loadstring, pcall, setmetatable = _G.loadstring, _G.pcall, _G.setmetatable;
+ local setfenv, rawget, tostring = _G.setfenv, _G.rawget, _G.tostring;
+ parsers.lua = {};
+ function parsers.lua.load(data)
+ local env;
+ env = setmetatable({ Host = true; host = true; }, { __index = function (t, k)
+ return rawget(_G, k) or
+ function (settings_table)
+ config[__currenthost or "*"][k] = settings_table;
+ end;
+ end,
+ __newindex = function (t, k, v)
+ set(env.__currenthost or "*", "core", k, v);
+ end});
+
+ function env.Host(name)
+ rawset(env, "__currenthost", name);
+ set(name or "*", "core", "defined", true);
+ end
+ env.host = env.Host;
+
+ local chunk, err = loadstring(data);
+
+ if not chunk then
+ return nil, err;
+ end
+
+ setfenv(chunk, env);
+
+ local ok, err = pcall(chunk);
+
+ if not ok then
+ return nil, err;
+ end
+
+ return true;
+ end
+
+end
+
+return _M; \ No newline at end of file
diff --git a/core/discomanager.lua b/core/discomanager.lua
new file mode 100644
index 00000000..5f7b3c78
--- /dev/null
+++ b/core/discomanager.lua
@@ -0,0 +1,39 @@
+
+local helper = require "util.discohelper".new();
+local hosts = hosts;
+local jid_split = require "util.jid".split;
+local jid_bare = require "util.jid".bare;
+local usermanager_user_exists = require "core.usermanager".user_exists;
+local rostermanager_is_contact_subscribed = require "core.rostermanager".is_contact_subscribed;
+
+do
+ helper:addDiscoInfoHandler("*host", function(reply, to, from, node)
+ if hosts[to] then
+ reply:tag("identity", {category="server", type="im", name="lxmppd"}):up();
+ return true;
+ end
+ end);
+ helper:addDiscoInfoHandler("*node", function(reply, to, from, node)
+ local node, host = jid_split(to);
+ if hosts[host] and rostermanager_is_contact_subscribed(node, host, jid_bare(from)) then
+ reply:tag("identity", {category="account", type="registered"}):up();
+ return true;
+ end
+ end);
+end
+
+module "discomanager"
+
+function handle(stanza)
+ return helper:handle(stanza);
+end
+
+function addDiscoItemsHandler(jid, func)
+ return helper:addDiscoItemsHandler(jid, func);
+end
+
+function addDiscoInfoHandler(jid, func)
+ return helper:addDiscoInfoHandler(jid, func);
+end
+
+return _M;
diff --git a/core/modulemanager.lua b/core/modulemanager.lua
index 783fea55..d313130c 100644
--- a/core/modulemanager.lua
+++ b/core/modulemanager.lua
@@ -78,7 +78,7 @@ function load(name)
local success, ret = pcall(mod);
if not success then
log("error", "Error initialising module '%s': %s", name or "nil", ret or "nil");
- return nil, err;
+ return nil, ret;
end
return true;
end
@@ -92,8 +92,8 @@ function handle_stanza(origin, stanza)
if child then
local xmlns = child.attr.xmlns or xmlns;
log("debug", "Stanza of type %s from %s has xmlns: %s", name, origin_type, xmlns);
- local handler = handlers[origin_type][name][xmlns];
- if handler then
+ local handler = handlers[origin_type][name] and handlers[origin_type][name][xmlns];
+ if handler then
log("debug", "Passing stanza to mod_%s", handler_info[handler].name);
return handler(origin, stanza) or true;
end
diff --git a/core/presencemanager.lua b/core/presencemanager.lua
new file mode 100644
index 00000000..c6619fea
--- /dev/null
+++ b/core/presencemanager.lua
@@ -0,0 +1,121 @@
+
+local log = require "util.logger".init("presencemanager")
+
+local require = require;
+local pairs = pairs;
+
+local st = require "util.stanza";
+local jid_split = require "util.jid".split;
+local hosts = hosts;
+
+local rostermanager = require "core.rostermanager";
+local sessionmanager = require "core.sessionmanager";
+
+module "presencemanager"
+
+function send_presence_of_available_resources(user, host, jid, recipient_session, core_route_stanza)
+ local h = hosts[host];
+ local count = 0;
+ if h and h.type == "local" then
+ local u = h.sessions[user];
+ if u then
+ for k, session in pairs(u.sessions) do
+ local pres = session.presence;
+ if pres then
+ pres.attr.to = jid;
+ pres.attr.from = session.full_jid;
+ core_route_stanza(session, pres);
+ pres.attr.to = nil;
+ pres.attr.from = nil;
+ count = count + 1;
+ end
+ end
+ end
+ end
+ log("info", "broadcasted presence of "..count.." resources from "..user.."@"..host.." to "..jid);
+ return count;
+end
+
+function handle_outbound_presence_subscriptions_and_probes(origin, stanza, from_bare, to_bare, core_route_stanza)
+ local node, host = jid_split(from_bare);
+ local st_from, st_to = stanza.attr.from, stanza.attr.to;
+ stanza.attr.from, stanza.attr.to = from_bare, to_bare;
+ log("debug", "outbound presence "..stanza.attr.type.." from "..from_bare.." for "..to_bare);
+ if stanza.attr.type == "subscribe" then
+ -- 1. route stanza
+ -- 2. roster push (subscription = none, ask = subscribe)
+ if rostermanager.set_contact_pending_out(node, host, to_bare) then
+ rostermanager.roster_push(node, host, to_bare);
+ end -- else file error
+ core_route_stanza(origin, stanza);
+ elseif stanza.attr.type == "unsubscribe" then
+ -- 1. route stanza
+ -- 2. roster push (subscription = none or from)
+ if rostermanager.unsubscribe(node, host, to_bare) then
+ rostermanager.roster_push(node, host, to_bare); -- FIXME do roster push when roster has in fact not changed?
+ end -- else file error
+ core_route_stanza(origin, stanza);
+ elseif stanza.attr.type == "subscribed" then
+ -- 1. route stanza
+ -- 2. roster_push ()
+ -- 3. send_presence_of_available_resources
+ if rostermanager.subscribed(node, host, to_bare) then
+ rostermanager.roster_push(node, host, to_bare);
+ end
+ core_route_stanza(origin, stanza);
+ send_presence_of_available_resources(node, host, to_bare, origin, core_route_stanza);
+ elseif stanza.attr.type == "unsubscribed" then
+ -- 1. route stanza
+ -- 2. roster push (subscription = none or to)
+ if rostermanager.unsubscribed(node, host, to_bare) then
+ rostermanager.roster_push(node, host, to_bare);
+ end
+ core_route_stanza(origin, stanza);
+ end
+ stanza.attr.from, stanza.attr.to = st_from, st_to;
+end
+
+function handle_inbound_presence_subscriptions_and_probes(origin, stanza, from_bare, to_bare, core_route_stanza)
+ local node, host = jid_split(to_bare);
+ local st_from, st_to = stanza.attr.from, stanza.attr.to;
+ stanza.attr.from, stanza.attr.to = from_bare, to_bare;
+ log("debug", "inbound presence "..stanza.attr.type.." from "..from_bare.." for "..to_bare);
+ if stanza.attr.type == "probe" then
+ if rostermanager.is_contact_subscribed(node, host, from_bare) then
+ if 0 == send_presence_of_available_resources(node, host, from_bare, origin, core_route_stanza) then
+ -- TODO send last recieved unavailable presence (or we MAY do nothing, which is fine too)
+ end
+ else
+ core_route_stanza(origin, st.presence({from=to_bare, to=from_bare, type="unsubscribed"}));
+ end
+ elseif stanza.attr.type == "subscribe" then
+ if rostermanager.is_contact_subscribed(node, host, from_bare) then
+ core_route_stanza(origin, st.presence({from=to_bare, to=from_bare, type="subscribed"})); -- already subscribed
+ -- Sending presence is not clearly stated in the RFC, but it seems appropriate
+ if 0 == send_presence_of_available_resources(node, host, from_bare, origin, core_route_stanza) then
+ -- TODO send last recieved unavailable presence (or we MAY do nothing, which is fine too)
+ end
+ else
+ if not rostermanager.is_contact_pending_in(node, host, from_bare) then
+ if rostermanager.set_contact_pending_in(node, host, from_bare) then
+ sessionmanager.send_to_available_resources(node, host, stanza);
+ end -- TODO else return error, unable to save
+ end
+ end
+ elseif stanza.attr.type == "unsubscribe" then
+ if rostermanager.process_inbound_unsubscribe(node, host, from_bare) then
+ rostermanager.roster_push(node, host, from_bare);
+ end
+ elseif stanza.attr.type == "subscribed" then
+ if rostermanager.process_inbound_subscription_approval(node, host, from_bare) then
+ rostermanager.roster_push(node, host, from_bare);
+ end
+ elseif stanza.attr.type == "unsubscribed" then
+ if rostermanager.process_inbound_subscription_cancellation(node, host, from_bare) then
+ rostermanager.roster_push(node, host, from_bare);
+ end
+ end -- discard any other type
+ stanza.attr.from, stanza.attr.to = st_from, st_to;
+end
+
+return _M;
diff --git a/core/s2smanager.lua b/core/s2smanager.lua
index c3d9bdb4..6d8f3a00 100644
--- a/core/s2smanager.lua
+++ b/core/s2smanager.lua
@@ -3,7 +3,7 @@ local hosts = hosts;
local sessions = sessions;
local socket = require "socket";
local format = string.format;
-local t_insert = table.insert;
+local t_insert, t_sort = table.insert, table.sort;
local get_traceback = debug.traceback;
local tostring, pairs, ipairs, getmetatable, print, newproxy, error, tonumber
= tostring, pairs, ipairs, getmetatable, print, newproxy, error, tonumber;
@@ -24,16 +24,19 @@ local md5_hash = require "util.hashes".md5;
local dialback_secret = "This is very secret!!! Ha!";
-local srvmap = { ["gmail.com"] = "talk.google.com", ["identi.ca"] = "hampton.controlezvous.ca", ["cdr.se"] = "jabber.cdr.se" };
+local dns = require "net.dns";
module "s2smanager"
+local function compare_srv_priorities(a,b) return a.priority < b.priority or a.weight < b.weight; end
+
function send_to_host(from_host, to_host, data)
+ if data.name then data = tostring(data); end
local host = hosts[from_host].s2sout[to_host];
if host then
-- We have a connection to this host already
- if host.type == "s2sout_unauthed" then
- host.log("debug", "trying to send over unauthed s2sout to "..to_host..", authing it now...");
+ if host.type == "s2sout_unauthed" and ((not data.xmlns) or data.xmlns == "jabber:client" or data.xmlns == "jabber:server") then
+ (host.log or log)("debug", "trying to send over unauthed s2sout to "..to_host..", authing it now...");
if not host.notopen and not host.dialback_key then
host.log("debug", "dialback had not been initiated");
initiate_dialback(host);
@@ -51,7 +54,7 @@ function send_to_host(from_host, to_host, data)
-- FIXME
if host.from_host ~= from_host then
log("error", "WARNING! This might, possibly, be a bug, but it might not...");
- log("error", "We are going to send from %s instead of %s", host.from_host, from_host);
+ log("error", "We are going to send from %s instead of %s", tostring(host.from_host), tostring(from_host));
end
host.sends2s(data);
host.log("debug", "stanza sent over "..host.type);
@@ -73,8 +76,8 @@ function new_incoming(conn)
getmetatable(session.trace).__gc = function () open_sessions = open_sessions - 1; print("s2s session got collected, now "..open_sessions.." s2s sessions are allocated") end;
end
open_sessions = open_sessions + 1;
- local w = conn.write;
- session.sends2s = function (t) w(tostring(t)); end
+ local w, log = conn.write, logger_init("s2sin"..tostring(conn):match("[a-f0-9]+$"));
+ session.sends2s = function (t) log("debug", "sending: %s", tostring(t)); w(tostring(t)); end
return session;
end
@@ -84,30 +87,49 @@ function new_outgoing(from_host, to_host)
local cl = connlisteners_get("xmppserver");
local conn, handler = socket.tcp()
+
+ local connect_host, connect_port = to_host, 5269;
- --FIXME: Below parameters (ports/ip) are incorrect (use SRV)
- to_host = srvmap[to_host] or to_host;
+ local answer = dns.lookup("_xmpp-server._tcp."..to_host..".", "SRV");
+
+ if answer then
+ log("debug", to_host.." has SRV records, handling...");
+ local srv_hosts = {};
+ host_session.srv_hosts = srv_hosts;
+ for _, record in ipairs(answer) do
+ t_insert(srv_hosts, record.srv);
+ end
+ t_sort(srv_hosts, compare_srv_priorities);
+
+ local srv_choice = srv_hosts[1];
+ if srv_choice then
+ connect_host, connect_port = srv_choice.target or to_host, srv_choice.port or connect_port;
+ log("debug", "Best record found, will connect to %s:%d", connect_host, connect_port);
+ end
+ end
conn:settimeout(0);
- local success, err = conn:connect(to_host, 5269);
- if not success then
+ local success, err = conn:connect(connect_host, connect_port);
+ if not success and err ~= "timeout" then
log("warn", "s2s connect() failed: %s", err);
end
- conn = wraptlsclient(cl, conn, to_host, 5269, 0, 1, hosts[from_host].ssl_ctx );
+ conn = wraptlsclient(cl, conn, connect_host, connect_port, 0, 1, hosts[from_host].ssl_ctx );
host_session.conn = conn;
-- Register this outgoing connection so that xmppserver_listener knows about it
-- otherwise it will assume it is a new incoming connection
cl.register_outgoing(conn, host_session);
+ local log;
do
local conn_name = "s2sout"..tostring(conn):match("[a-f0-9]*$");
- host_session.log = logger_init(conn_name);
+ log = logger_init(conn_name);
+ host_session.log = log;
end
local w = conn.write;
- host_session.sends2s = function (t) w(tostring(t)); end
+ host_session.sends2s = function (t) log("debug", "sending: %s", tostring(t)); w(tostring(t)); end
conn.write(format([[<stream:stream xmlns='jabber:server' xmlns:db='jabber:server:dialback' xmlns:stream='http://etherx.jabber.org/streams' from='%s' to='%s' version='1.0'>]], from_host, to_host));
@@ -119,23 +141,31 @@ function streamopened(session, attr)
session.version = tonumber(attr.version) or 0;
if session.version >= 1.0 and not (attr.to and attr.from) then
- print("to: "..tostring(attr.to).." from: "..tostring(attr.from));
- --error(session.to_host.." failed to specify 'to' or 'from' hostname as per RFC");
+ --print("to: "..tostring(attr.to).." from: "..tostring(attr.from));
log("warn", (session.to_host or "(unknown)").." failed to specify 'to' or 'from' hostname as per RFC");
end
if session.direction == "incoming" then
-- Send a reply stream header
- for k,v in pairs(attr) do print("", tostring(k), ":::", tostring(v)); end
+ --for k,v in pairs(attr) do print("", tostring(k), ":::", tostring(v)); end
session.to_host = attr.to;
session.from_host = attr.from;
session.streamid = uuid_gen();
- print(session, session.from_host, "incoming s2s stream opened");
+ (session.log or log)("debug", "incoming s2s received <stream:stream>");
send("<?xml version='1.0'?>");
send(stanza("stream:stream", { xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback', ["xmlns:stream"]='http://etherx.jabber.org/streams', id=session.streamid, from=session.to_host }):top_tag());
+ if session.to_host and not hosts[session.to_host] then
+ -- Attempting to connect to a host we don't serve
+ session:close("host-unknown");
+ return;
+ end
+ if session.version >= 1.0 then
+ send(st.stanza("stream:features")
+ :tag("dialback", { xmlns='urn:xmpp:features:dialback' }):tag("optional"):up():up());
+ end
elseif session.direction == "outgoing" then
-- If we are just using the connection for verifying dialback keys, we won't try and auth it
if not attr.id then error("stream response did not give us a streamid!!!"); end
@@ -147,17 +177,6 @@ function streamopened(session, attr)
mark_connected(session);
end
end
- --[[
- local features = {};
- modulemanager.fire_event("stream-features-s2s", session, features);
-
- send("<stream:features>");
-
- for _, feature in ipairs(features) do
- send(tostring(feature));
- end
-
- send("</stream:features>");]]
session.notopen = nil;
end
@@ -217,11 +236,13 @@ end
function destroy_session(session)
(session.log or log)("info", "Destroying "..tostring(session.direction).." session "..tostring(session.from_host).."->"..tostring(session.to_host));
+
+ -- FIXME: Flush sendq here/report errors to originators
+
if session.direction == "outgoing" then
hosts[session.from_host].s2sout[session.to_host] = nil;
end
- session.conn = nil;
- session.disconnect = nil;
+
for k in pairs(session) do
if k ~= "trace" then
session[k] = nil;
diff --git a/core/servermanager.lua b/core/servermanager.lua
index 99eb4c23..8cbf2f12 100644
--- a/core/servermanager.lua
+++ b/core/servermanager.lua
@@ -2,7 +2,7 @@
local st = require "util.stanza";
local xmlns_stanzas ='urn:ietf:params:xml:ns:xmpp-stanzas';
-require "modulemanager"
+local modulemanager = require "core.modulemanager";
-- Handle stanzas that were addressed to the server (whether they came from c2s, s2s, etc.)
function handle_stanza(origin, stanza)
diff --git a/core/sessionmanager.lua b/core/sessionmanager.lua
index 0d65f6d6..e83b7c23 100644
--- a/core/sessionmanager.lua
+++ b/core/sessionmanager.lua
@@ -14,6 +14,8 @@ local error = error;
local uuid_generate = require "util.uuid".generate;
local rm_load_roster = require "core.rostermanager".load_roster;
+local st = require "util.stanza";
+
local newproxy = newproxy;
local getmetatable = getmetatable;
@@ -28,13 +30,24 @@ function new_session(conn)
getmetatable(session.trace).__gc = function () open_sessions = open_sessions - 1; print("Session got collected, now "..open_sessions.." sessions are allocated") end;
end
open_sessions = open_sessions + 1;
+ log("info", "open sessions now: ".. open_sessions);
local w = conn.write;
session.send = function (t) w(tostring(t)); end
return session;
end
-function destroy_session(session)
+function destroy_session(session, err)
(session.log or log)("info", "Destroying session");
+
+ -- Send unavailable presence
+ if session.presence then
+ local pres = st.presence{ type = "unavailable" };
+ if (not err) or err == "closed" then err = "connection closed"; end
+ pres:tag("status"):text("Disconnected: "..err);
+ session.stanza_dispatch(pres);
+ end
+
+ -- Remove session/resource from user's session list
if session.host and session.username then
if session.resource then
hosts[session.host].sessions[session.username].sessions[session.resource] = nil;
@@ -46,8 +59,7 @@ function destroy_session(session)
end
end
end
- session.conn = nil;
- session.disconnect = nil;
+
for k in pairs(session) do
if k ~= "trace" then
session[k] = nil;
@@ -96,21 +108,25 @@ function streamopened(session, attr)
session.host = attr.to or error("Client failed to specify destination hostname");
session.version = tonumber(attr.version) or 0;
session.streamid = m_random(1000000, 99999999);
- print(session, session.host, "Client opened stream");
- send("<?xml version='1.0'?>");
+ (session.log or session)("debug", "Client sent opening <stream:stream> to %s", session.host);
+
+
+ send("<?xml version='1.0'?>");
send(format("<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='%s' from='%s' version='1.0'>", session.streamid, session.host));
- local features = {};
+ if not hosts[session.host] then
+ -- We don't serve this host...
+ session:close{ condition = "host-unknown", text = "This server does not serve "..tostring(session.host)};
+ return;
+ end
+
+
+ local features = st.stanza("stream:features");
modulemanager.fire_event("stream-features", session, features);
- send("<stream:features>");
+ send(features);
- for _, feature in ipairs(features) do
- send(tostring(feature));
- end
-
- send("</stream:features>");
- log("info", "Stream opened successfully");
+ (session.log or log)("info", "Sent reply <stream:stream> to client");
session.notopen = nil;
end
diff --git a/core/stanza_router.lua b/core/stanza_router.lua
index 2b0e1f4b..2505fca3 100644
--- a/core/stanza_router.lua
+++ b/core/stanza_router.lua
@@ -21,6 +21,9 @@ local s2s_make_authenticated = require "core.s2smanager".make_authenticated;
local modules_handle_stanza = require "core.modulemanager".handle_stanza;
local component_handle_stanza = require "core.componentmanager".handle_stanza;
+local handle_outbound_presence_subscriptions_and_probes = require "core.presencemanager".handle_outbound_presence_subscriptions_and_probes;
+local handle_inbound_presence_subscriptions_and_probes = require "core.presencemanager".handle_inbound_presence_subscriptions_and_probes;
+
local format = string.format;
local tostring = tostring;
local t_concat = table.concat;
@@ -32,7 +35,7 @@ local jid_split = require "util.jid".split;
local print = print;
function core_process_stanza(origin, stanza)
- log("debug", "Received[%s]: %s", origin.type, stanza:pretty_top_tag())
+ (origin.log or log)("debug", "Received[%s]: %s", origin.type, stanza:pretty_print()) --top_tag())
if not stanza.attr.xmlns then stanza.attr.xmlns = "jabber:client"; end -- FIXME Hack. This should be removed when we fix namespace handling.
-- TODO verify validity of stanza (as well as JID validity)
@@ -87,7 +90,7 @@ function core_process_stanza(origin, stanza)
elseif hosts[host] and hosts[host].type == "component" then -- directed at a component
component_handle_stanza(origin, stanza);
elseif origin.type == "c2s" and stanza.name == "presence" and stanza.attr.type ~= nil and stanza.attr.type ~= "unavailable" then
- handle_outbound_presence_subscriptions_and_probes(origin, stanza, from_bare, to_bare);
+ handle_outbound_presence_subscriptions_and_probes(origin, stanza, from_bare, to_bare, core_route_stanza);
elseif origin.type ~= "c2s" and stanza.name == "iq" and not resource then -- directed at bare JID
core_handle_stanza(origin, stanza);
else
@@ -174,130 +177,23 @@ function core_handle_stanza(origin, stanza)
stanza.attr.to = nil; -- reset it
else
log("warn", "Unhandled c2s presence: %s", tostring(stanza));
- if stanza.attr.type ~= "error" then
+ if (stanza.attr.xmlns == "jabber:client" or stanza.attr.xmlns == "jabber:server") and stanza.attr.type ~= "error" then
origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); -- FIXME correct error?
end
end
else
log("warn", "Unhandled c2s stanza: %s", tostring(stanza));
- if stanza.attr.type ~= "error" and stanza.attr.type ~= "result" then
+ if (stanza.attr.xmlns == "jabber:client" or stanza.attr.xmlns == "jabber:server") and stanza.attr.type ~= "error" and stanza.attr.type ~= "result" then
origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); -- FIXME correct error?
end
end -- TODO handle other stanzas
else
log("warn", "Unhandled origin: %s", origin.type);
- if stanza.attr.type ~= "error" and stanza.attr.type ~= "result" then
+ if (stanza.attr.xmlns == "jabber:client" or stanza.attr.xmlns == "jabber:server") and stanza.attr.type ~= "error" and stanza.attr.type ~= "result" then
-- s2s stanzas can get here
- (origin.sends2s or origin.send)(st.error_reply(stanza, "cancel", "service-unavailable")); -- FIXME correct error?
- end
- end
-end
-
-function send_presence_of_available_resources(user, host, jid, recipient_session)
- local h = hosts[host];
- local count = 0;
- if h and h.type == "local" then
- local u = h.sessions[user];
- if u then
- for k, session in pairs(u.sessions) do
- local pres = session.presence;
- if pres then
- pres.attr.to = jid;
- pres.attr.from = session.full_jid;
- recipient_session.send(pres);
- pres.attr.to = nil;
- pres.attr.from = nil;
- count = count + 1;
- end
- end
+ origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); -- FIXME correct error?
end
end
- return count;
-end
-
-function handle_outbound_presence_subscriptions_and_probes(origin, stanza, from_bare, to_bare)
- local node, host = jid_split(from_bare);
- local st_from, st_to = stanza.attr.from, stanza.attr.to;
- stanza.attr.from, stanza.attr.to = from_bare, to_bare;
- if stanza.attr.type == "subscribe" then
- log("debug", "outbound subscribe from "..from_bare.." for "..to_bare);
- -- 1. route stanza
- -- 2. roster push (subscription = none, ask = subscribe)
- if rostermanager.set_contact_pending_out(node, host, to_bare) then
- rostermanager.roster_push(node, host, to_bare);
- end -- else file error
- core_route_stanza(origin, stanza);
- elseif stanza.attr.type == "unsubscribe" then
- log("debug", "outbound unsubscribe from "..from_bare.." for "..to_bare);
- -- 1. route stanza
- -- 2. roster push (subscription = none or from)
- if rostermanager.unsubscribe(node, host, to_bare) then
- rostermanager.roster_push(node, host, to_bare); -- FIXME do roster push when roster has in fact not changed?
- end -- else file error
- core_route_stanza(origin, stanza);
- elseif stanza.attr.type == "subscribed" then
- log("debug", "outbound subscribed from "..from_bare.." for "..to_bare);
- -- 1. route stanza
- -- 2. roster_push ()
- -- 3. send_presence_of_available_resources
- if rostermanager.subscribed(node, host, to_bare) then
- rostermanager.roster_push(node, host, to_bare);
- core_route_stanza(origin, stanza);
- send_presence_of_available_resources(node, host, to_bare, origin);
- end
- elseif stanza.attr.type == "unsubscribed" then
- log("debug", "outbound unsubscribed from "..from_bare.." for "..to_bare);
- -- 1. route stanza
- -- 2. roster push (subscription = none or to)
- if rostermanager.unsubscribed(node, host, to_bare) then
- rostermanager.roster_push(node, host, to_bare);
- core_route_stanza(origin, stanza);
- end
- end
- stanza.attr.from, stanza.attr.to = st_from, st_to;
-end
-
-function handle_inbound_presence_subscriptions_and_probes(origin, stanza, from_bare, to_bare)
- local node, host = jid_split(to_bare);
- local st_from, st_to = stanza.attr.from, stanza.attr.to;
- stanza.attr.from, stanza.attr.to = from_bare, to_bare;
- if stanza.attr.type == "probe" then
- log("debug", "inbound probe from "..from_bare.." for "..to_bare);
- if rostermanager.is_contact_subscribed(node, host, from_bare) then
- if 0 == send_presence_of_available_resources(node, host, from_bare, origin) then
- -- TODO send last recieved unavailable presence (or we MAY do nothing, which is fine too)
- end
- else
- core_route_stanza(origin, st.presence({from=to_bare, to=from_bare, type="unsubscribed"}));
- end
- elseif stanza.attr.type == "subscribe" then
- log("debug", "inbound subscribe from "..from_bare.." for "..to_bare);
- if rostermanager.is_contact_subscribed(node, host, from_bare) then
- core_route_stanza(origin, st.presence({from=to_bare, to=from_bare, type="subscribed"})); -- already subscribed
- else
- if not rostermanager.is_contact_pending_in(node, host, from_bare) then
- if rostermanager.set_contact_pending_in(node, host, from_bare) then
- sessionmanager.send_to_available_resources(node, host, stanza);
- end -- TODO else return error, unable to save
- end
- end
- elseif stanza.attr.type == "unsubscribe" then
- log("debug", "inbound unsubscribe from "..from_bare.." for "..to_bare);
- if rostermanager.process_inbound_unsubscribe(node, host, from_bare) then
- rostermanager.roster_push(node, host, from_bare);
- end
- elseif stanza.attr.type == "subscribed" then
- log("debug", "inbound subscribed from "..from_bare.." for "..to_bare);
- if rostermanager.process_inbound_subscription_approval(node, host, from_bare) then
- rostermanager.roster_push(node, host, from_bare);
- end
- elseif stanza.attr.type == "unsubscribed" then
- log("debug", "inbound unsubscribed from "..from_bare.." for "..to_bare);
- if rostermanager.process_inbound_subscription_approval(node, host, from_bare) then
- rostermanager.roster_push(node, host, from_bare);
- end
- end -- discard any other type
- stanza.attr.from, stanza.attr.to = st_from, st_to;
end
function core_route_stanza(origin, stanza)
@@ -312,6 +208,10 @@ function core_route_stanza(origin, stanza)
local from_node, from_host, from_resource = jid_split(from);
local from_bare = from_node and (from_node.."@"..from_host) or from_host; -- bare JID
+ -- Auto-detect origin if not specified
+ origin = origin or hosts[from_host];
+ if not origin then return false; end
+
if stanza.name == "presence" and (stanza.attr.type ~= nil and stanza.attr.type ~= "unavailable") then resource = nil; end
local host_session = hosts[host]
@@ -324,7 +224,7 @@ function core_route_stanza(origin, stanza)
-- if we get here, resource was not specified or was unavailable
if stanza.name == "presence" then
if stanza.attr.type ~= nil and stanza.attr.type ~= "unavailable" then
- handle_inbound_presence_subscriptions_and_probes(origin, stanza, from_bare, to_bare);
+ handle_inbound_presence_subscriptions_and_probes(origin, stanza, from_bare, to_bare, core_route_stanza);
else -- sender is available or unavailable
for _, session in pairs(user.sessions) do -- presence broadcast to all user resources.
if session.full_jid then -- FIXME should this be just for available resources? Do we need to check subscription?
@@ -367,7 +267,7 @@ function core_route_stanza(origin, stanza)
if user_exists(node, host) then
if stanza.name == "presence" then
if stanza.attr.type ~= nil and stanza.attr.type ~= "unavailable" then
- handle_inbound_presence_subscriptions_and_probes(origin, stanza, from_bare, to_bare);
+ handle_inbound_presence_subscriptions_and_probes(origin, stanza, from_bare, to_bare, core_route_stanza);
else
-- TODO send unavailable presence or unsubscribed
end
@@ -404,8 +304,6 @@ function core_route_stanza(origin, stanza)
elseif origin.type == "component" or origin.type == "local" then
-- Route via s2s for components and modules
log("debug", "Routing outgoing stanza for %s to %s", origin.host, host);
- for k,v in pairs(origin) do print("origin:", tostring(k), tostring(v)); end
- print(tostring(host), tostring(from_host))
send_s2s(origin.host, host, stanza);
else
log("warn", "received stanza from unhandled connection type: %s", origin.type);
diff --git a/core/xmlhandlers.lua b/core/xmlhandlers.lua
index 3037a848..a97db8e9 100644
--- a/core/xmlhandlers.lua
+++ b/core/xmlhandlers.lua
@@ -25,7 +25,7 @@ local ns_prefixes = {
["http://www.w3.org/XML/1998/namespace"] = "xml";
}
-function init_xmlhandlers(session, streamopened)
+function init_xmlhandlers(session, stream_callbacks)
local ns_stack = { "" };
local curr_ns = "";
local curr_tag;
@@ -36,6 +36,9 @@ function init_xmlhandlers(session, streamopened)
local send = session.send;
+ local cb_streamopened = stream_callbacks.streamopened;
+ local cb_streamclosed = stream_callbacks.streamclosed;
+
local stanza
function xml_handlers:StartElement(name, attr)
if stanza and #chardata > 0 then
@@ -66,7 +69,9 @@ function init_xmlhandlers(session, streamopened)
if not stanza then --if we are not currently inside a stanza
if session.notopen then
if name == "stream" then
- streamopened(session, attr);
+ if cb_streamopened then
+ cb_streamopened(session, attr);
+ end
return;
end
error("Client failed to open stream successfully");
@@ -75,7 +80,7 @@ function init_xmlhandlers(session, streamopened)
error("Client sent invalid top-level stanza");
end
- stanza = st.stanza(name, attr); --{ to = attr.to, type = attr.type, id = attr.id, xmlns = curr_ns });
+ stanza = st.stanza(name, attr);
curr_tag = stanza;
else -- we are inside a stanza, so add a tag
attr.xmlns = nil;
@@ -92,15 +97,17 @@ function init_xmlhandlers(session, streamopened)
end
function xml_handlers:EndElement(name)
curr_ns,name = name:match("^(.+)|([%w%-]+)$");
- if (not stanza) or #stanza.last_add < 0 or (#stanza.last_add > 0 and name ~= stanza.last_add[#stanza.last_add].name) then
+ if (not stanza) or (#stanza.last_add > 0 and name ~= stanza.last_add[#stanza.last_add].name) then
if name == "stream" then
log("debug", "Stream closed");
- sm_destroy_session(session);
+ if cb_streamclosed then
+ cb_streamclosed(session);
+ end
return;
elseif name == "error" then
error("Stream error: "..tostring(name)..": "..tostring(stanza));
else
- error("XML parse error in client stream");
+ error("XML parse error in client stream with element: "..name);
end
end
if stanza and #chardata > 0 then