diff options
-rw-r--r-- | core/componentmanager.lua | 21 | ||||
-rw-r--r-- | core/modulemanager.lua | 2 | ||||
-rw-r--r-- | core/rostermanager.lua | 4 | ||||
-rw-r--r-- | core/s2smanager.lua | 5 | ||||
-rw-r--r-- | core/sessionmanager.lua | 2 | ||||
-rw-r--r-- | core/stanza_router.lua | 8 | ||||
-rw-r--r-- | net/http.lua | 1 | ||||
-rw-r--r-- | plugins/mod_muc.lua | 10 | ||||
-rw-r--r-- | plugins/mod_register.lua | 22 | ||||
-rw-r--r-- | plugins/mod_roster.lua | 24 | ||||
-rw-r--r-- | plugins/mod_saslauth.lua | 15 | ||||
-rwxr-xr-x | prosody | 3 | ||||
-rw-r--r-- | util/datamanager.lua | 7 | ||||
-rw-r--r-- | util/events.lua | 96 |
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 @@ -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;
|