aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/componentmanager.lua21
-rw-r--r--core/modulemanager.lua2
-rw-r--r--core/rostermanager.lua4
-rw-r--r--core/s2smanager.lua5
-rw-r--r--core/sessionmanager.lua2
-rw-r--r--core/stanza_router.lua8
-rw-r--r--net/http.lua1
-rw-r--r--plugins/mod_muc.lua10
-rw-r--r--plugins/mod_register.lua22
-rw-r--r--plugins/mod_roster.lua24
-rw-r--r--plugins/mod_saslauth.lua15
-rwxr-xr-xprosody3
-rw-r--r--util/datamanager.lua7
-rw-r--r--util/events.lua96
14 files changed, 189 insertions, 31 deletions
diff --git a/core/componentmanager.lua b/core/componentmanager.lua
index da079e83..e7b5e4dc 100644
--- a/core/componentmanager.lua
+++ b/core/componentmanager.lua
@@ -20,6 +20,18 @@ local pairs, type, tostring = pairs, type, tostring;
local components = {};
+local disco_items = require "util.multitable".new();
+local NULL = {};
+require "core.discomanager".addDiscoItemsHandler("*host", function(reply, to, from, node)
+ if #node == 0 and hosts[to] then
+ for jid in pairs(disco_items:get(to) or NULL) do
+ reply:tag("item", {jid = jid}):up();
+ end
+ return true;
+ end
+end);
+
+
module "componentmanager"
function load_enabled_components(config)
@@ -64,7 +76,10 @@ function register_component(host, component, session)
if not hosts[host] or (hosts[host].type == 'component' and not hosts[host].connected) then
components[host] = component;
hosts[host] = session or create_component(host, component);
-
+ -- add to disco_items
+ if not(host:find("@", 1, true) or host:find("/", 1, true)) and host:find(".", 1, true) then
+ disco_items:set(host:sub(host:find(".", 1, true)+1), host, true);
+ end
-- FIXME only load for a.b.c if b.c has dialback, and/or check in config
modulemanager.load(host, "dialback");
log("debug", "component added: "..host);
@@ -79,6 +94,10 @@ function deregister_component(host)
modulemanager.unload(host, "dialback");
components[host] = nil;
hosts[host] = nil;
+ -- remove from disco_items
+ if not(host:find("@", 1, true) or host:find("/", 1, true)) and host:find(".", 1, true) then
+ disco_items:remove(host:sub(host:find(".", 1, true)+1), host);
+ end
log("debug", "component removed: "..host);
return true;
else
diff --git a/core/modulemanager.lua b/core/modulemanager.lua
index 4391fc7f..a1d3bef3 100644
--- a/core/modulemanager.lua
+++ b/core/modulemanager.lua
@@ -204,7 +204,7 @@ function handle_stanza(host, origin, stanza)
local name, xmlns, origin_type = stanza.name, stanza.attr.xmlns, origin.type;
if name == "iq" and xmlns == "jabber:client" then
if stanza.attr.type == "get" or stanza.attr.type == "set" then
- xmlns = stanza.tags[1].attr.xmlns;
+ xmlns = stanza.tags[1].attr.xmlns or "jabber:client";
log("debug", "Stanza of type %s from %s has xmlns: %s", name, origin_type, xmlns);
else
log("debug", "Discarding %s from %s of type: %s", name, origin_type, stanza.attr.type);
diff --git a/core/rostermanager.lua b/core/rostermanager.lua
index fdaf64a7..867add2c 100644
--- a/core/rostermanager.lua
+++ b/core/rostermanager.lua
@@ -224,6 +224,10 @@ function subscribed(username, host, jid)
if is_contact_pending_in(username, host, jid) then
local roster = load_roster(username, host);
local item = roster[jid];
+ if not item then -- FIXME should roster item be auto-created?
+ item = {subscription = "none", groups = {}};
+ roster[jid] = item;
+ end
if item.subscription == "none" then
item.subscription = "from";
else -- subscription == to
diff --git a/core/s2smanager.lua b/core/s2smanager.lua
index 19b7f047..f4f7ad36 100644
--- a/core/s2smanager.lua
+++ b/core/s2smanager.lua
@@ -24,6 +24,7 @@ local wrapclient = require "net.server".wrapclient;
local modulemanager = require "core.modulemanager";
local st = require "stanza";
local stanza = st.stanza;
+local nameprep = require "util.encodings".stringprep.nameprep;
local uuid_gen = require "util.uuid".generate;
@@ -211,8 +212,8 @@ function streamopened(session, attr)
if session.direction == "incoming" then
-- Send a reply stream header
- session.to_host = attr.to;
- session.from_host = attr.from;
+ session.to_host = attr.to and nameprep(attr.to);
+ session.from_host = attr.from and nameprep(attr.from);
session.streamid = uuid_gen();
(session.log or log)("debug", "incoming s2s received <stream:stream>");
diff --git a/core/sessionmanager.lua b/core/sessionmanager.lua
index 762982c1..efc99366 100644
--- a/core/sessionmanager.lua
+++ b/core/sessionmanager.lua
@@ -23,6 +23,7 @@ local error = error;
local uuid_generate = require "util.uuid".generate;
local rm_load_roster = require "core.rostermanager".load_roster;
local config_get = require "core.configmanager".get;
+local nameprep = require "util.encodings".stringprep.nameprep;
local fire_event = require "core.eventmanager".fire_event;
@@ -156,6 +157,7 @@ end
function streamopened(session, attr)
local send = session.send;
session.host = attr.to or error("Client failed to specify destination hostname");
+ session.host = nameprep(session.host);
session.version = tonumber(attr.version) or 0;
session.streamid = m_random(1000000, 99999999);
(session.log or session)("debug", "Client sent opening <stream:stream> to %s", session.host);
diff --git a/core/stanza_router.lua b/core/stanza_router.lua
index ef973ba2..bbbb6385 100644
--- a/core/stanza_router.lua
+++ b/core/stanza_router.lua
@@ -51,9 +51,11 @@ function core_process_stanza(origin, stanza)
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)
- if stanza.name == "iq" and #stanza.tags > 1 then
- if stanza.attr.type == "set" or stanza.attr.type == "get" then
- error("Invalid IQ");
+ if stanza.attr.xmlns == "error" and #stanza.tags == 0 then return; end -- TODO invalid stanza, log
+ if stanza.name == "iq" then
+ if (stanza.attr.type == "set" or stanza.attr.type == "get") and #stanza.tags ~= 1 then
+ origin.send(st.error_reply(stanza, "modify", "bad-request"));
+ return;
end
end
diff --git a/net/http.lua b/net/http.lua
index 1d22d3e5..29da41ea 100644
--- a/net/http.lua
+++ b/net/http.lua
@@ -115,6 +115,7 @@ function request(u, ex, callback)
local req = url.parse(u);
if not (req and req.host) then
+ callback(nil, 0, req);
return nil, "invalid-url";
end
diff --git a/plugins/mod_muc.lua b/plugins/mod_muc.lua
index f4c17546..117a044b 100644
--- a/plugins/mod_muc.lua
+++ b/plugins/mod_muc.lua
@@ -369,6 +369,16 @@ function handle_to_room(origin, stanza) -- presence changes and groupchat messag
elseif type ~= "error" and type ~= "result" then
origin.send(st.error_reply(stanza, "cancel", "service-unavailable"));
end
+ elseif stanza.name == "message" and not stanza.attr.type and #stanza.tags == 1 and jid_nick:get(stanza.attr.from, stanza.attr.to)
+ and stanza.tags[1].name == "x" and stanza.tags[1].attr.xmlns == "http://jabber.org/protocol/muc#user" and #stanza.tags[1].tags == 1
+ and stanza.tags[1].tags[1].name == "invite" and stanza.tags[1].tags[1].attr.to then
+ local _from, _to = stanza.attr.from, stanza.attr.to;
+ local _invitee = stanza.tags[1].tags[1].attr.to;
+ stanza.attr.from, stanza.attr.to = _to, _invitee;
+ stanza.tags[1].tags[1].attr.from, stanza.tags[1].tags[1].attr.to = _from, nil;
+ core_route_stanza(component, stanza);
+ stanza.tags[1].tags[1].attr.from, stanza.tags[1].tags[1].attr.to = nil, _invitee;
+ stanza.attr.from, stanza.attr.to = _from, _to;
else
if type == "error" or type == "result" then return; end
origin.send(st.error_reply(stanza, "cancel", "service-unavailable"));
diff --git a/plugins/mod_register.lua b/plugins/mod_register.lua
index eaeb8867..c04eca0a 100644
--- a/plugins/mod_register.lua
+++ b/plugins/mod_register.lua
@@ -13,6 +13,7 @@ local usermanager_user_exists = require "core.usermanager".user_exists;
local usermanager_create_user = require "core.usermanager".create_user;
local datamanager_store = require "util.datamanager".store;
local os_time = os.time;
+local nodeprep = require "util.encodings".stringprep.nodeprep;
module:add_feature("jabber:iq:register");
@@ -29,22 +30,23 @@ module:add_iq_handler("c2s", "jabber:iq:register", function (session, stanza)
elseif stanza.attr.type == "set" then
if query.tags[1] and query.tags[1].name == "remove" then
-- TODO delete user auth data, send iq response, kick all user resources with a <not-authorized/>, delete all user data
+ local username, host = session.username, session.host;
--session.send(st.error_reply(stanza, "cancel", "not-allowed"));
--return;
- usermanager_create_user(session.username, nil, session.host); -- Disable account
+ usermanager_create_user(username, nil, host); -- Disable account
-- FIXME the disabling currently allows a different user to recreate the account
-- we should add an in-memory account block mode when we have threading
session.send(st.reply(stanza));
local roster = session.roster;
- for _, session in pairs(hosts[session.host].sessions[session.username].sessions) do -- disconnect all resources
+ for _, session in pairs(hosts[host].sessions[username].sessions) do -- disconnect all resources
session:close({condition = "not-authorized", text = "Account deleted"});
end
-- TODO datamanager should be able to delete all user data itself
- datamanager.store(session.username, session.host, "roster", nil);
- datamanager.store(session.username, session.host, "vcard", nil);
- datamanager.store(session.username, session.host, "private", nil);
- datamanager.store(session.username, session.host, "offline", nil);
- --local bare = session.username.."@"..session.host;
+ datamanager.store(username, host, "roster", nil);
+ datamanager.store(username, host, "vcard", nil);
+ datamanager.store(username, host, "private", nil);
+ datamanager.store(username, host, "offline", nil);
+ --local bare = username.."@"..host;
for jid, item in pairs(roster) do
if jid ~= "pending" then
if item.subscription == "both" or item.subscription == "to" then
@@ -55,13 +57,13 @@ module:add_iq_handler("c2s", "jabber:iq:register", function (session, stanza)
end
end
end
- datamanager.store(session.username, session.host, "accounts", nil); -- delete accounts datastore at the end
+ datamanager.store(username, host, "accounts", nil); -- delete accounts datastore at the end
else
local username = query:child_with_name("username");
local password = query:child_with_name("password");
if username and password then
-- FIXME shouldn't use table.concat
- username = table.concat(username);
+ username = nodeprep(table.concat(username));
password = table.concat(password);
if username == session.username then
if usermanager_create_user(username, password, session.host) then -- password change -- TODO is this the right way?
@@ -132,7 +134,7 @@ module:add_iq_handler("c2s_unauthed", "jabber:iq:register", function (session, s
end
end
-- FIXME shouldn't use table.concat
- username = table.concat(username);
+ username = nodeprep(table.concat(username));
password = table.concat(password);
if usermanager_user_exists(username, session.host) then
session.send(st.error_reply(stanza, "cancel", "conflict"));
diff --git a/plugins/mod_roster.lua b/plugins/mod_roster.lua
index da375e5d..e30bc1f9 100644
--- a/plugins/mod_roster.lua
+++ b/plugins/mod_roster.lua
@@ -11,6 +11,7 @@
local st = require "util.stanza"
local jid_split = require "util.jid".split;
+local jid_prep = require "util.jid".prep;
local t_concat = table.concat;
local tostring = tostring;
@@ -61,17 +62,18 @@ module:add_iq_handler("c2s", "jabber:iq:roster",
local item = query.tags[1];
local from_node, from_host = jid_split(stanza.attr.from);
local from_bare = from_node and (from_node.."@"..from_host) or from_host; -- bare JID
- local node, host, resource = jid_split(item.attr.jid);
- local to_bare = node and (node.."@"..host) or host; -- bare JID
+ local jid = jid_prep(item.attr.jid);
+ local node, host, resource = jid_split(jid);
if not resource and host then
- if item.attr.jid ~= from_node.."@"..from_host then
+ if jid ~= from_node.."@"..from_host then
if item.attr.subscription == "remove" then
- local r_item = session.roster[item.attr.jid];
+ local r_item = session.roster[jid];
if r_item then
- local success, err_type, err_cond, err_msg = rm_remove_from_roster(session, item.attr.jid);
+ local success, err_type, err_cond, err_msg = rm_remove_from_roster(session, jid);
if success then
session.send(st.reply(stanza));
- rm_roster_push(from_node, from_host, item.attr.jid);
+ rm_roster_push(from_node, from_host, jid);
+ local to_bare = node and (node.."@"..host) or host; -- bare JID
if r_item.subscription == "both" or r_item.subscription == "from" then
handle_presence(session, st.presence({type="unsubscribed"}), from_bare, to_bare,
core_route_stanza, false);
@@ -88,9 +90,9 @@ module:add_iq_handler("c2s", "jabber:iq:roster",
else
local r_item = {name = item.attr.name, groups = {}};
if r_item.name == "" then r_item.name = nil; end
- if session.roster[item.attr.jid] then
- r_item.subscription = session.roster[item.attr.jid].subscription;
- r_item.ask = session.roster[item.attr.jid].ask;
+ if session.roster[jid] then
+ r_item.subscription = session.roster[jid].subscription;
+ r_item.ask = session.roster[jid].ask;
else
r_item.subscription = "none";
end
@@ -102,10 +104,10 @@ module:add_iq_handler("c2s", "jabber:iq:roster",
end
end
end
- local success, err_type, err_cond, err_msg = rm_add_to_roster(session, item.attr.jid, r_item);
+ local success, err_type, err_cond, err_msg = rm_add_to_roster(session, jid, r_item);
if success then
session.send(st.reply(stanza));
- rm_roster_push(from_node, from_host, item.attr.jid);
+ rm_roster_push(from_node, from_host, jid);
else
session.send(st.error_reply(stanza, err_type, err_cond, err_msg));
end
diff --git a/plugins/mod_saslauth.lua b/plugins/mod_saslauth.lua
index d3f32be3..31d62325 100644
--- a/plugins/mod_saslauth.lua
+++ b/plugins/mod_saslauth.lua
@@ -72,7 +72,15 @@ end
local function sasl_handler(session, stanza)
if stanza.name == "auth" then
-- FIXME ignoring duplicates because ejabberd does
+ if config.get(session.host or "*", "core", "anonymous_login") and stanza.attr.mechanism ~= "ANONYMOUS" then
+ return session.send(build_reply("failure", "invalid-mechanism"));
+ elseif stanza.attr.mechanism == "ANONYMOUS" then
+ return session.send(build_reply("failure", "mechanism-too-weak"));
+ end
session.sasl_handler = new_sasl(stanza.attr.mechanism, session.host, password_callback);
+ if not session.sasl_handler then
+ return session.send(build_reply("failure", "invalid-mechanism"));
+ end
elseif not session.sasl_handler then
return; -- FIXME ignoring out of order stanzas because ejabberd does
end
@@ -105,10 +113,11 @@ module:add_event_hook("stream-features",
if not session.username then
features:tag("mechanisms", mechanisms_attr);
-- TODO: Provide PLAIN only if TLS is active, this is a SHOULD from the introduction of RFC 4616. This behavior could be overridden via configuration but will issuing a warning or so.
- features:tag("mechanism"):text("PLAIN"):up();
- features:tag("mechanism"):text("DIGEST-MD5"):up();
- if config.get(session.host or "*", "core", "sasl_anonymous") then
+ if config.get(session.host or "*", "core", "anonymous_login") then
features:tag("mechanism"):text("ANONYMOUS"):up();
+ else
+ features:tag("mechanism"):text("DIGEST-MD5"):up();
+ features:tag("mechanism"):text("PLAIN"):up();
end
features:up();
else
diff --git a/prosody b/prosody
index 59ff904f..57e705ce 100755
--- a/prosody
+++ b/prosody
@@ -104,6 +104,9 @@ require "util.jid"
local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data";
require "util.datamanager".set_data_path(data_path);
+require "util.datamanager".set_callback(function(username, host, datastore)
+ return config.get(host, "core", "anonymous_login");
+end);
----------- End of out-of-place code --------------
diff --git a/util/datamanager.lua b/util/datamanager.lua
index f67c8864..614f0c80 100644
--- a/util/datamanager.lua
+++ b/util/datamanager.lua
@@ -50,6 +50,7 @@ local function mkdir(path)
end
local data_path = "data";
+local callback;
------- API -------------
@@ -57,6 +58,9 @@ function set_data_path(path)
log("info", "Setting data path to: %s", path);
data_path = path;
end
+function set_callback(func)
+ callback = func;
+end
function getpath(username, host, datastore, ext, create)
ext = ext or "dat";
@@ -93,6 +97,7 @@ function store(username, host, datastore, data)
if not data then
data = {};
end
+ if callback and callback(username, host, datastore) then return true; end
-- save the datastore
local f, msg = io_open(getpath(username, host, datastore, nil, true), "w+");
if not f then
@@ -113,6 +118,7 @@ end
function list_append(username, host, datastore, data)
if not data then return; end
+ if callback and callback(username, host, datastore) then return true; end
-- save the datastore
local f, msg = io_open(getpath(username, host, datastore, "list", true), "a+");
if not f then
@@ -130,6 +136,7 @@ function list_store(username, host, datastore, data)
if not data then
data = {};
end
+ if callback and callback(username, host, datastore) then return true; end
-- save the datastore
local f, msg = io_open(getpath(username, host, datastore, "list", true), "w+");
if not f then
diff --git a/util/events.lua b/util/events.lua
new file mode 100644
index 00000000..b1f3811c
--- /dev/null
+++ b/util/events.lua
@@ -0,0 +1,96 @@
+
+local ipairs = ipairs;
+local pairs = pairs;
+local t_insert = table.insert;
+local select = select;
+
+module "events"
+
+function new()
+ local dispatchers = {};
+ local handlers = {};
+ local event_map = {};
+ local function _rebuild_index() -- TODO optimize index rebuilding
+ for event, _handlers in pairs(event_map) do
+ local index = handlers[event];
+ if index then
+ for i=#index,1,-1 do index[i] = nil; end
+ else index = {}; handlers[event] = index; end
+ for handler in pairs(_handlers) do
+ t_insert(index, handler);
+ end
+ end
+ end;
+ local function add_handler(event, handler)
+ local map = event_map[event];
+ if map then
+ map[handler] = true;
+ else
+ map = {[handler] = true};
+ event_map[event] = map;
+ end
+ _rebuild_index();
+ end;
+ local function remove_handler(event, handler)
+ local map = event_map[event];
+ if map then
+ map[handler] = nil;
+ _rebuild_index();
+ end
+ end;
+ local function add_plugin(plugin)
+ for event, handler in pairs(plugin) do
+ add_handler(event, handler);
+ end
+ end;
+ local function remove_plugin(plugin)
+ for event, handler in pairs(plugin) do
+ remove_handler(event, handler);
+ end
+ end;
+ local function _create_dispatcher(event) -- FIXME duplicate code in fire_event
+ local h = handlers[event];
+ if not h then h = {}; handlers[event] = h; end
+ local dispatcher = function(data)
+ for _, handler in ipairs(h) do
+ handler(data);
+ end
+ end;
+ dispatchers[event] = dispatcher;
+ return dispatcher;
+ end;
+ local function get_dispatcher(event)
+ return dispatchers[event] or _create_dispatcher(event);
+ end;
+ local function fire_event(event, data) -- FIXME duplicates dispatcher code
+ local h = handlers[event];
+ if h then
+ for _, handler in ipairs(h) do
+ handler(data);
+ end
+ end
+ end;
+ local function get_named_arg_dispatcher(event, ...)
+ local dispatcher = get_dispatcher(event);
+ local keys = {...};
+ local data = {};
+ return function(...)
+ for i, key in ipairs(keys) do data[key] = select(i, ...); end
+ dispatcher(data);
+ end;
+ end;
+ return {
+ add_handler = add_handler;
+ remove_handler = remove_handler;
+ add_plugin = add_plugin;
+ remove_plugin = remove_plugin;
+ get_dispatcher = get_dispatcher;
+ fire_event = fire_event;
+ get_named_arg_dispatcher = get_named_arg_dispatcher;
+ _dispatchers = dispatchers;
+ _handlers = handlers;
+ _event_map = event_map;
+ };
+end
+
+return _M;