diff options
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/mod_bosh.lua | 37 | ||||
-rw-r--r-- | plugins/mod_compression.lua | 122 | ||||
-rw-r--r-- | plugins/mod_console.lua | 29 | ||||
-rw-r--r-- | plugins/mod_disco.lua | 45 | ||||
-rw-r--r-- | plugins/mod_httpserver.lua | 42 | ||||
-rw-r--r-- | plugins/mod_legacyauth.lua | 17 | ||||
-rw-r--r-- | plugins/mod_muc.lua | 3 | ||||
-rw-r--r-- | plugins/mod_pep.lua | 2 | ||||
-rw-r--r-- | plugins/mod_posix.lua | 40 | ||||
-rw-r--r-- | plugins/mod_register.lua | 11 | ||||
-rw-r--r-- | plugins/mod_roster.lua | 2 | ||||
-rw-r--r-- | plugins/mod_saslauth.lua | 64 | ||||
-rw-r--r-- | plugins/mod_selftests.lua | 5 | ||||
-rw-r--r-- | plugins/mod_tls.lua | 7 | ||||
-rw-r--r-- | plugins/mod_version.lua | 24 | ||||
-rw-r--r-- | plugins/mod_watchregistrations.lua | 8 | ||||
-rw-r--r-- | plugins/mod_welcome.lua | 4 | ||||
-rw-r--r-- | plugins/mod_xmlrpc.lua | 30 |
18 files changed, 371 insertions, 121 deletions
diff --git a/plugins/mod_bosh.lua b/plugins/mod_bosh.lua index 743ebdef..e310be28 100644 --- a/plugins/mod_bosh.lua +++ b/plugins/mod_bosh.lua @@ -6,7 +6,6 @@ -- COPYING file in the source package for more information. -- - module.host = "*" -- Global module local hosts = _G.hosts; @@ -22,17 +21,18 @@ local core_process_stanza = core_process_stanza; local st = require "util.stanza"; local logger = require "util.logger"; local log = logger.init("mod_bosh"); -local stream_callbacks = { stream_tag = "http://jabber.org/protocol/httpbind|body" }; -local config = require "core.configmanager"; + local xmlns_bosh = "http://jabber.org/protocol/httpbind"; -- (hard-coded into a literal in session.send) +local stream_callbacks = { stream_tag = "http://jabber.org/protocol/httpbind|body", default_ns = xmlns_bosh }; -local BOSH_DEFAULT_HOLD = tonumber(config.get("*", "core", "bosh_default_hold")) or 1; -local BOSH_DEFAULT_INACTIVITY = tonumber(config.get("*", "core", "bosh_max_inactivity")) or 60; -local BOSH_DEFAULT_POLLING = tonumber(config.get("*", "core", "bosh_max_polling")) or 5; -local BOSH_DEFAULT_REQUESTS = tonumber(config.get("*", "core", "bosh_max_requests")) or 2; -local BOSH_DEFAULT_MAXPAUSE = tonumber(config.get("*", "core", "bosh_max_pause")) or 300; +local BOSH_DEFAULT_HOLD = tonumber(module:get_option("bosh_default_hold")) or 1; +local BOSH_DEFAULT_INACTIVITY = tonumber(module:get_option("bosh_max_inactivity")) or 60; +local BOSH_DEFAULT_POLLING = tonumber(module:get_option("bosh_max_polling")) or 5; +local BOSH_DEFAULT_REQUESTS = tonumber(module:get_option("bosh_max_requests")) or 2; +local BOSH_DEFAULT_MAXPAUSE = tonumber(module:get_option("bosh_max_pause")) or 300; local default_headers = { ["Content-Type"] = "text/xml; charset=utf-8" }; +local session_close_reply = { headers = default_headers, body = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate" }), attr = {} }; local t_insert, t_remove, t_concat = table.insert, table.remove, table.concat; local os_time = os.time; @@ -112,11 +112,9 @@ end local function bosh_reset_stream(session) session.notopen = true; end -local session_close_reply = { headers = default_headers, body = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate" }), attr = {} }; local function bosh_close_stream(session, reason) (session.log or log)("info", "BOSH client disconnected"); session_close_reply.attr.condition = reason; - local session_close_reply = tostring(session_close_reply); for _, held_request in ipairs(session.requests) do held_request:send(session_close_reply); held_request:destroy(); @@ -144,7 +142,7 @@ function stream_callbacks.streamopened(request, attr) -- New session sid = new_uuid(); - local session = { type = "c2s_unauthed", conn = {}, sid = sid, rid = attr.rid, host = attr.to, bosh_version = attr.ver, bosh_wait = attr.wait, streamid = sid, + local session = { type = "c2s_unauthed", conn = {}, sid = sid, rid = tonumber(attr.rid), host = attr.to, bosh_version = attr.ver, bosh_wait = attr.wait, streamid = sid, bosh_hold = BOSH_DEFAULT_HOLD, bosh_max_inactive = BOSH_DEFAULT_INACTIVITY, requests = { }, send_buffer = {}, reset_stream = bosh_reset_stream, close = bosh_close_stream, dispatch_stanza = core_process_stanza, log = logger.init("bosh"..sid), secure = request.secure }; @@ -209,6 +207,21 @@ function stream_callbacks.streamopened(request, attr) return; end + if session.rid then + local rid = tonumber(attr.rid); + local diff = rid - session.rid; + if diff > 1 then + session.log("warn", "rid too large (means a request was lost). Last rid: %d New rid: %s", session.rid, attr.rid); + elseif diff <= 0 then + -- Repeated, ignore + session.log("debug", "rid repeated (on request %s), ignoring: %d", request.id, session.rid); + request.notopen = nil; + t_insert(session.requests, request); + return; + end + session.rid = rid; + end + if attr.type == "terminate" then -- Client wants to end this session session:close(); @@ -275,7 +288,7 @@ function on_timer() end end -local ports = config.get(module.host, "core", "bosh_ports") or { 5280 }; +local ports = module:get_option("bosh_ports") or { 5280 }; httpserver.new_from_config(ports, "http-bind", handle_request); server.addtimer(on_timer); diff --git a/plugins/mod_compression.lua b/plugins/mod_compression.lua new file mode 100644 index 00000000..7e53a5e5 --- /dev/null +++ b/plugins/mod_compression.lua @@ -0,0 +1,122 @@ +-- Prosody IM +-- Copyright (C) 2009 Tobias Markmann +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +local st = require "util.stanza"; +local zlib = require "zlib"; +local pcall = pcall; + +local xmlns_compression_feature = "http://jabber.org/features/compress" +local xmlns_compression_protocol = "http://jabber.org/protocol/compress" +local compression_stream_feature = st.stanza("compression", {xmlns=xmlns_compression_feature}):tag("method"):text("zlib"):up(); + +local compression_level = module:get_option("compression_level"); + +-- if not defined assume admin wants best compression +if compression_level == nil then compression_level = 9 end; + +compression_level = tonumber(compression_level); +if not compression_level or compression_level < 1 or compression_level > 9 then + module:log("warn", "Invalid compression level in config: %s", tostring(compression_level)); + module:log("warn", "Module loading aborted. Compression won't be available."); + return; +end + +module:add_event_hook("stream-features", + function (session, features) + if not session.compressed then + -- FIXME only advertise compression support when TLS layer has no compression enabled + features:add_child(compression_stream_feature); + end + end +); + +-- TODO Support compression on S2S level too. +module:add_handler({"c2s_unauthed", "c2s_authed"}, "compress", xmlns_compression_protocol, + function(session, stanza) + -- fail if we are already compressed + if session.compressed then + local error_st = st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("unsupported-method"); + session.send(error_st); + session:log("warn", "Tried to establish another compression layer."); + end + + -- checking if the compression method is supported + local method = stanza:child_with_name("method")[1]; + if method == "zlib" then + session.log("info", method.." compression selected."); + session.send(st.stanza("compressed", {xmlns=xmlns_compression_protocol})); + session:reset_stream(); + + -- create deflate and inflate streams + local status, deflate_stream = pcall(zlib.deflate, compression_level); + if status == false then + local error_st = st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("setup-failed"); + session.send(error_st); + session:log("error", "Failed to create zlib.deflate filter."); + module:log("error", deflate_stream); + return + end + + local status, inflate_stream = pcall(zlib.inflate); + if status == false then + local error_st = st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("setup-failed"); + session.send(error_st); + session:log("error", "Failed to create zlib.deflate filter."); + module:log("error", inflate_stream); + return + end + + -- setup compression for session.w + local old_send = session.send; + + session.send = function(t) + local status, compressed, eof = pcall(deflate_stream, tostring(t), 'sync'); + if status == false then + session:close({ + condition = "undefined-condition"; + text = compressed; + extra = st.stanza("failure", {xmlns="http://jabber.org/protocol/compress"}):tag("processing-failed"); + }); + module:log("warn", compressed); + return; + end + old_send(compressed); + end; + + -- setup decompression for session.data + local function setup_decompression(session) + local old_data = session.data + session.data = function(conn, data) + local status, decompressed, eof = pcall(inflate_stream, data); + if status == false then + session:close({ + condition = "undefined-condition"; + text = decompressed; + extra = st.stanza("failure", {xmlns="http://jabber.org/protocol/compress"}):tag("processing-failed"); + }); + module:log("warn", decompressed); + return; + end + old_data(conn, decompressed); + end; + end + setup_decompression(session); + + local session_reset_stream = session.reset_stream; + session.reset_stream = function(session) + session_reset_stream(session); + setup_decompression(session); + return true; + end; + session.compressed = true; + else + session.log("info", method.." compression selected. But we don't support it."); + local error_st = st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("unsupported-method"); + session.send(error_st); + end + end +); diff --git a/plugins/mod_console.lua b/plugins/mod_console.lua index a3ed9499..367c46b8 100644 --- a/plugins/mod_console.lua +++ b/plugins/mod_console.lua @@ -70,6 +70,9 @@ function console_listener.listener(conn, data) if data:match("^>") then data = data:gsub("^>", ""); useglobalenv = true; + elseif data == "\004" then + commands["bye"](session, data); + return; else local command = data:lower(); command = data:match("^%w+") or data:match("%p"); @@ -205,7 +208,8 @@ end -- Anything in def_env will be accessible within the session as a global variable def_env.server = {}; -function def_env.server:reload() + +function def_env.server:insane_reload() prosody.unlock_globals(); dofile "prosody" prosody = _G.prosody; @@ -230,6 +234,11 @@ function def_env.server:uptime() minutes, (minutes ~= 1 and "s") or "", os.date("%c", prosody.start_time)); end +function def_env.server:shutdown(reason) + prosody.shutdown(reason); + return true, "Shutdown initiated"; +end + def_env.module = {}; local function get_hosts_set(hosts, module) @@ -333,6 +342,11 @@ function def_env.config:get(host, section, key) return true, tostring(config_get(host, section, key)); end +function def_env.config:reload() + local ok, err = prosody.reload_config(); + return ok, (ok and "Config reloaded (you may need to reload modules to take effect)") or tostring(err); +end + def_env.hosts = {}; function def_env.hosts:list() for host, host_session in pairs(hosts) do @@ -359,10 +373,19 @@ end function def_env.c2s:show(match_jid) local print, count = self.session.print, 0; - show_c2s(function (jid) + show_c2s(function (jid, session) if (not match_jid) or jid:match(match_jid) then count = count + 1; - print(jid); + local status, priority = "unavailable", tostring(session.priority or "-"); + if session.presence then + status = session.presence:child_with_name("show"); + if status then + status = status:get_text() or "[invalid!]"; + else + status = "available"; + end + end + print(jid.." - "..status.."("..priority..")"); end end); return true, "Total: "..count.." clients"; diff --git a/plugins/mod_disco.lua b/plugins/mod_disco.lua index 00ea01d8..06b29f0e 100644 --- a/plugins/mod_disco.lua +++ b/plugins/mod_disco.lua @@ -6,16 +6,47 @@ -- COPYING file in the source package for more information. -- +local componentmanager_get_children = require "core.componentmanager".get_children; +local st = require "util.stanza" - -local discomanager_handle = require "core.discomanager".handle; - +module:add_identity("server", "im", "Prosody"); -- FIXME should be in the non-existing mod_router module:add_feature("http://jabber.org/protocol/disco#info"); module:add_feature("http://jabber.org/protocol/disco#items"); -module:add_iq_handler({"c2s", "s2sin"}, "http://jabber.org/protocol/disco#info", function (session, stanza) - session.send(discomanager_handle(stanza)); +module:hook("iq/host/http://jabber.org/protocol/disco#info:query", function(event) + local origin, stanza = event.origin, event.stanza; + if stanza.attr.type ~= "get" then return; end + local node = stanza.tags[1].attr.node; + if node and node ~= "" then return; end -- TODO fire event? + + local reply = st.reply(stanza):query("http://jabber.org/protocol/disco#info"); + local done = {}; + for _,identity in ipairs(module:get_host_items("identity")) do + local identity_s = identity.category.."\0"..identity.type; + if not done[identity_s] then + reply:tag("identity", identity):up(); + done[identity_s] = true; + end + end + for _,feature in ipairs(module:get_host_items("feature")) do + if not done[feature] then + reply:tag("feature", {var=feature}):up(); + done[feature] = true; + end + end + origin.send(reply); + return true; end); -module:add_iq_handler({"c2s", "s2sin"}, "http://jabber.org/protocol/disco#items", function (session, stanza) - session.send(discomanager_handle(stanza)); +module:hook("iq/host/http://jabber.org/protocol/disco#items:query", function(event) + local origin, stanza = event.origin, event.stanza; + if stanza.attr.type ~= "get" then return; end + local node = stanza.tags[1].attr.node; + if node and node ~= "" then return; end -- TODO fire event? + + local reply = st.reply(stanza):query("http://jabber.org/protocol/disco#items"); + for jid in pairs(componentmanager_get_children(module.host)) do + reply:tag("item", {jid = jid}):up(); + end + origin.send(reply); + return true; end); diff --git a/plugins/mod_httpserver.lua b/plugins/mod_httpserver.lua index a8639281..f1f2150d 100644 --- a/plugins/mod_httpserver.lua +++ b/plugins/mod_httpserver.lua @@ -14,18 +14,48 @@ local t_concat = table.concat; local http_base = "www_files"; +local response_400 = { status = "400 Bad Request", body = "<h1>Bad Request</h1>Sorry, we didn't understand your request :(" }; local response_404 = { status = "404 Not Found", body = "<h1>Page Not Found</h1>Sorry, we couldn't find what you were looking for :(" }; -local http_path = { http_base }; -local function handle_request(method, body, request) - local path = request.url.path:gsub("%.%.%/", ""):gsub("^/[^/]+", ""); - http_path[2] = path; - local f, err = open(t_concat(http_path), "r"); +local function preprocess_path(path) + if path:sub(1,1) ~= "/" then + path = "/"..path; + end + local level = 0; + for component in path:gmatch("([^/]+)/") do + if component == ".." then + level = level - 1; + elseif component ~= "." then + level = level + 1; + end + if level < 0 then + return nil; + end + end + return path; +end + +function serve_file(path) + local f, err = open(http_base..path, "r"); if not f then return response_404; end local data = f:read("*a"); f:close(); return data; end +local function handle_file_request(method, body, request) + local path = preprocess_path(request.url.path); + if not path then return response_400; end + path = path:gsub("^/[^/]+", ""); -- Strip /files/ + return serve_file(path); +end + +local function handle_default_request(method, body, request) + local path = preprocess_path(request.url.path); + if not path then return response_400; end + return serve_file(path); +end + local ports = config.get(module.host, "core", "http_ports") or { 5280 }; -httpserver.new_from_config(ports, "files", handle_request); +httpserver.set_default_handler(handle_default_request); +httpserver.new_from_config(ports, "files", handle_file_request); diff --git a/plugins/mod_legacyauth.lua b/plugins/mod_legacyauth.lua index de94411e..9a9c3902 100644 --- a/plugins/mod_legacyauth.lua +++ b/plugins/mod_legacyauth.lua @@ -11,8 +11,7 @@ local st = require "util.stanza"; local t_concat = table.concat; -local config = require "core.configmanager"; -local secure_auth_only = config.get(module:get_host(), "core", "require_encryption"); +local secure_auth_only = module:get_option("require_encryption"); local sessionmanager = require "core.sessionmanager"; local usermanager = require "core.usermanager"; @@ -43,11 +42,9 @@ module:add_iq_handler("c2s_unauthed", "jabber:iq:auth", :tag("username"):up() :tag("password"):up() :tag("resource"):up()); - return true; else username, password, resource = t_concat(username), t_concat(password), t_concat(resource); local reply = st.reply(stanza); - require "core.usermanager" if usermanager.validate_credentials(session.host, username, password) then -- Authentication successful! local success, err = sessionmanager.make_authenticated(session, username); @@ -56,19 +53,13 @@ module:add_iq_handler("c2s_unauthed", "jabber:iq:auth", success, err_type, err, err_msg = sessionmanager.bind_resource(session, resource); if not success then session.send(st.error_reply(stanza, err_type, err, err_msg)); - return true; + return true; -- FIXME need to unauthenticate here end end session.send(st.reply(stanza)); - return true; else - local reply = st.reply(stanza); - reply.attr.type = "error"; - reply:tag("error", { code = "401", type = "auth" }) - :tag("not-authorized", { xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas" }); - session.send(reply); - return true; + session.send(st.error_reply(stanza, "auth", "not-authorized")); end end - + return true; end); diff --git a/plugins/mod_muc.lua b/plugins/mod_muc.lua index e99ef83c..b38468ea 100644 --- a/plugins/mod_muc.lua +++ b/plugins/mod_muc.lua @@ -76,6 +76,8 @@ component = register_component(muc_host, function(origin, stanza) handle_to_domain(origin, stanza); end); +prosody.hosts[module:get_host()].muc = { rooms = rooms }; + module.unload = function() deregister_component(muc_host); end @@ -84,4 +86,5 @@ module.save = function() end module.restore = function(data) rooms = data.rooms or {}; + prosody.hosts[module:get_host()].muc = { rooms = rooms }; end diff --git a/plugins/mod_pep.lua b/plugins/mod_pep.lua index e07759f0..842f1fce 100644 --- a/plugins/mod_pep.lua +++ b/plugins/mod_pep.lua @@ -25,7 +25,7 @@ local data = {}; local recipients = {}; local hash_map = {}; -module:add_identity("pubsub", "pep"); +module:add_identity("pubsub", "pep", "Prosody"); module:add_feature("http://jabber.org/protocol/pubsub#publish"); local function publish(session, node, item) diff --git a/plugins/mod_posix.lua b/plugins/mod_posix.lua index 0f46888d..5f7dfc5b 100644 --- a/plugins/mod_posix.lua +++ b/plugins/mod_posix.lua @@ -17,19 +17,45 @@ if type(signal) == "string" then module:log("warn", "Couldn't load signal library, won't respond to SIGTERM"); end -local config_get = require "core.configmanager".get; local logger_set = require "util.logger".setwriter; local prosody = _G.prosody; module.host = "*"; -- we're a global module +-- Allow switching away from root, some people like strange ports. +module:add_event_hook("server-started", function () + local uid = module:get_option("setuid"); + local gid = module:get_option("setgid"); + if gid then + local success, msg = pposix.setgid(gid); + if success then + module:log("debug", "Changed group to "..gid.." successfully."); + else + module:log("error", "Failed to change group to "..gid..". Error: "..msg); + prosody.shutdown("Failed to change group to "..gid); + end + end + if uid then + local success, msg = pposix.setuid(uid); + if success then + module:log("debug", "Changed user to "..uid.." successfully."); + else + module:log("error", "Failed to change user to "..uid..". Error: "..msg); + prosody.shutdown("Failed to change user to "..uid); + end + end + end); + -- Don't even think about it! module:add_event_hook("server-starting", function () - if pposix.getuid() == 0 and not config_get("*", "core", "run_as_root") then - module:log("error", "Danger, Will Robinson! Prosody doesn't need to be run as root, so don't do it!"); - module:log("error", "For more information on running Prosody as root, see http://prosody.im/doc/root"); - prosody.shutdown("Refusing to run as root"); + local suid = module:get_option("setuid"); + if not suid or suid == 0 or suid == "root" then + if pposix.getuid() == 0 and not module:get_option("run_as_root") then + module:log("error", "Danger, Will Robinson! Prosody doesn't need to be run as root, so don't do it!"); + module:log("error", "For more information on running Prosody as root, see http://prosody.im/doc/root"); + prosody.shutdown("Refusing to run as root"); + end end end); @@ -46,7 +72,7 @@ local function write_pidfile() if pidfile_written then remove_pidfile(); end - local pidfile = config_get("*", "core", "pidfile"); + local pidfile = module:get_option("pidfile"); if pidfile then local pf, err = io.open(pidfile, "w+"); if not pf then @@ -76,7 +102,7 @@ function syslog_sink_maker(config) end require "core.loggingmanager".register_sink_type("syslog", syslog_sink_maker); -if not config_get("*", "core", "no_daemonize") then +if not module:get_option("no_daemonize") then local function daemonize_server() local ok, ret = pposix.daemonize(); if not ok then diff --git a/plugins/mod_register.lua b/plugins/mod_register.lua index 383ab811..0cb8d771 100644 --- a/plugins/mod_register.lua +++ b/plugins/mod_register.lua @@ -9,7 +9,6 @@ local hosts = _G.hosts; local st = require "util.stanza"; -local config = require "core.configmanager"; local datamanager = require "util.datamanager"; local usermanager_user_exists = require "core.usermanager".user_exists; local usermanager_create_user = require "core.usermanager".create_user; @@ -90,16 +89,16 @@ module:add_iq_handler("c2s", "jabber:iq:register", function (session, stanza) end); local recent_ips = {}; -local min_seconds_between_registrations = config.get(module.host, "core", "min_seconds_between_registrations"); -local whitelist_only = config.get(module.host, "core", "whitelist_registration_only"); -local whitelisted_ips = config.get(module.host, "core", "registration_whitelist") or { "127.0.0.1" }; -local blacklisted_ips = config.get(module.host, "core", "registration_blacklist") or {}; +local min_seconds_between_registrations = module:get_option("min_seconds_between_registrations"); +local whitelist_only = module:get_option("whitelist_registration_only"); +local whitelisted_ips = module:get_option("registration_whitelist") or { "127.0.0.1" }; +local blacklisted_ips = module:get_option("registration_blacklist") or {}; for _, ip in ipairs(whitelisted_ips) do whitelisted_ips[ip] = true; end for _, ip in ipairs(blacklisted_ips) do blacklisted_ips[ip] = true; end module:add_iq_handler("c2s_unauthed", "jabber:iq:register", function (session, stanza) - if config.get(module.host, "core", "allow_registration") == false then + if module:get_option("allow_registration") == false then session.send(st.error_reply(stanza, "cancel", "service-unavailable")); elseif stanza.tags[1].name == "query" then local query = stanza.tags[1]; diff --git a/plugins/mod_roster.lua b/plugins/mod_roster.lua index 8f25ed64..7ca22aa1 100644 --- a/plugins/mod_roster.lua +++ b/plugins/mod_roster.lua @@ -24,7 +24,7 @@ module:add_feature("jabber:iq:roster"); local rosterver_stream_feature = st.stanza("ver", {xmlns="urn:xmpp:features:rosterver"}):tag("optional"):up(); module:add_event_hook("stream-features", - function (session, features) + function (session, features) if session.username then features:add_child(rosterver_stream_feature); end diff --git a/plugins/mod_saslauth.lua b/plugins/mod_saslauth.lua index 32269221..da66717c 100644 --- a/plugins/mod_saslauth.lua +++ b/plugins/mod_saslauth.lua @@ -12,9 +12,13 @@ local st = require "util.stanza"; local sm_bind_resource = require "core.sessionmanager".bind_resource; local sm_make_authenticated = require "core.sessionmanager".make_authenticated; local base64 = require "util.encodings".base64; + local nodeprep = require "util.encodings".stringprep.nodeprep; local datamanager_load = require "util.datamanager".load; local usermanager_validate_credentials = require "core.usermanager".validate_credentials; +local usermanager_get_supported_methods = require "core.usermanager".get_supported_methods; +local usermanager_user_exists = require "core.usermanager".user_exists; +local usermanager_get_password = require "core.usermanager".get_password; local t_concat, t_insert = table.concat, table.insert; local tostring = tostring; local jid_split = require "util.jid".split @@ -57,29 +61,39 @@ local function handle_status(session, status) session.sasl_handler = nil; session:reset_stream(); return; - end + end sm_make_authenticated(session, session.sasl_handler.username); session.sasl_handler = nil; session:reset_stream(); end end -local function password_callback(node, hostname, realm, mechanism, decoder) - local func = function(x) return x; end; - local node = nodeprep(node); - if not node then - return func, nil; - end - local password = (datamanager_load(node, hostname, "accounts") or {}).password; -- FIXME handle hashed passwords - if password then - if mechanism == "PLAIN" then - return func, password; - elseif mechanism == "DIGEST-MD5" then - if decoder then node, realm, password = decoder(node), decoder(realm), decoder(password); end +local function credentials_callback(mechanism, ...) + if mechanism == "PLAIN" then + local username, hostname, password = ...; + username = nodeprep(username); + if not username then + return false; + end + local response = usermanager_validate_credentials(hostname, username, password, mechanism); + if response == nil then + return false; + else + return response; + end + elseif mechanism == "DIGEST-MD5" then + function func(x) return x; end + local node, domain, realm, decoder = ...; + local password = usermanager_get_password(node, domain); + if password then + if decoder then + node, realm, password = decoder(node), decoder(realm), decoder(password); + end return func, md5(node..":"..realm..":"..password); + else + return func, nil; end end - return func, nil; end local function sasl_handler(session, stanza) @@ -92,7 +106,7 @@ local function sasl_handler(session, stanza) 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); + session.sasl_handler = new_sasl(stanza.attr.mechanism, session.host, credentials_callback); if not session.sasl_handler then return session.send(build_reply("failure", "invalid-mechanism")); end @@ -111,7 +125,7 @@ local function sasl_handler(session, stanza) end local status, ret, err_msg = session.sasl_handler:feed(text); handle_status(session, status); - local s = build_reply(status, ret, err_msg); + local s = build_reply(status, ret, err_msg); log("debug", "sasl reply: %s", tostring(s)); session.send(s); end @@ -123,8 +137,8 @@ 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' }; -module:add_event_hook("stream-features", - function (session, features) +module:add_event_hook("stream-features", + function (session, features) if not session.username then if secure_auth_only and not session.secure then return; @@ -134,8 +148,10 @@ module:add_event_hook("stream-features", 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(); + mechanisms = usermanager_get_supported_methods(session.host or "*"); + for k, v in pairs(mechanisms) do + features:tag("mechanism"):text(k):up(); + end end features:up(); else @@ -143,8 +159,8 @@ module:add_event_hook("stream-features", features:tag("session", xmpp_session_attr):up(); end end); - -module: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 requesting a resource bind"); local resource; @@ -166,8 +182,8 @@ module:add_iq_handler("c2s", "urn:ietf:params:xml:ns:xmpp-bind", :tag("jid"):text(session.full_jid)); end end); - -module: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 requesting a session"); session.send(st.reply(stanza)); diff --git a/plugins/mod_selftests.lua b/plugins/mod_selftests.lua index 6a26dfc3..1f413634 100644 --- a/plugins/mod_selftests.lua +++ b/plugins/mod_selftests.lua @@ -6,14 +6,13 @@ -- COPYING file in the source package for more information. -- - +module.host = "*" -- Global module local st = require "util.stanza"; local register_component = require "core.componentmanager".register_component; local core_route_stanza = core_route_stanza; local socket = require "socket"; -local config = require "core.configmanager"; -local ping_hosts = config.get("*", "mod_selftests", "ping_hosts") or { "coversant.interop.xmpp.org", "djabberd.interop.xmpp.org", "djabberd-trunk.interop.xmpp.org", "ejabberd.interop.xmpp.org", "openfire.interop.xmpp.org" }; +local ping_hosts = module:get_option("ping_hosts") or { "coversant.interop.xmpp.org", "djabberd.interop.xmpp.org", "djabberd-trunk.interop.xmpp.org", "ejabberd.interop.xmpp.org", "openfire.interop.xmpp.org" }; local open_pings = {}; diff --git a/plugins/mod_tls.lua b/plugins/mod_tls.lua index 8926edfc..10455559 100644 --- a/plugins/mod_tls.lua +++ b/plugins/mod_tls.lua @@ -6,14 +6,11 @@ -- COPYING file in the source package for more information. -- - - local st = require "util.stanza"; local xmlns_starttls ='urn:ietf:params:xml:ns:xmpp-tls'; -local config = require "core.configmanager"; -local secure_auth_only = config.get("*", "core", "require_encryption"); +local secure_auth_only = module:get_option("require_encryption"); module:add_handler("c2s_unauthed", "starttls", xmlns_starttls, function (session, stanza) @@ -31,7 +28,7 @@ module:add_handler("c2s_unauthed", "starttls", xmlns_starttls, local starttls_attr = { xmlns = xmlns_starttls }; module:add_event_hook("stream-features", - function (session, features) + function (session, features) if session.conn.starttls then features:tag("starttls", starttls_attr); if secure_auth_only then diff --git a/plugins/mod_version.lua b/plugins/mod_version.lua index 87bff5d9..9af830f8 100644 --- a/plugins/mod_version.lua +++ b/plugins/mod_version.lua @@ -6,17 +6,13 @@ -- COPYING file in the source package for more information. -- - -local prosody = prosody; local st = require "util.stanza"; -local xmlns_version = "jabber:iq:version" - -module:add_feature(xmlns_version); +module:add_feature("jabber:iq:version"); local version = "the best operating system ever!"; -if not require "core.configmanager".get("*", "core", "hide_os_type") then +if not module:get_option("hide_os_type") then if os.getenv("WINDIR") then version = "Windows"; else @@ -31,11 +27,15 @@ end version = version:match("^%s*(.-)%s*$") or version; -module:add_iq_handler({"c2s", "s2sin"}, xmlns_version, function(session, stanza) - if stanza.attr.type == "get" then - session.send(st.reply(stanza):query(xmlns_version) - :tag("name"):text("Prosody"):up() - :tag("version"):text(prosody.version):up() - :tag("os"):text(version)); +local query = st.stanza("query", {xmlns = "jabber:iq:version"}) + :tag("name"):text("Prosody"):up() + :tag("version"):text(prosody.version):up() + :tag("os"):text(version); + +module:hook("iq/host/jabber:iq:version:query", function(event) + local stanza = event.stanza; + if stanza.attr.type == "get" and stanza.attr.to == module.host then + event.origin.send(st.reply(stanza):add_child(query)); + return true; end end); diff --git a/plugins/mod_watchregistrations.lua b/plugins/mod_watchregistrations.lua index 9457313f..6a2af853 100644 --- a/plugins/mod_watchregistrations.lua +++ b/plugins/mod_watchregistrations.lua @@ -9,12 +9,10 @@ local host = module:get_host(); -local config = require "core.configmanager"; +local registration_watchers = module:get_option("registration_watchers") + or module:get_option("admins") or {}; -local registration_watchers = config.get(host, "core", "registration_watchers") - or config.get(host, "core", "admins") or {}; - -local registration_alert = config.get(host, "core", "registration_notification") or "User $username just registered on $host from $ip"; +local registration_alert = module:get_option("registration_notification") or "User $username just registered on $host from $ip"; local st = require "util.stanza"; diff --git a/plugins/mod_welcome.lua b/plugins/mod_welcome.lua index 5c0da8b8..cc50cba3 100644 --- a/plugins/mod_welcome.lua +++ b/plugins/mod_welcome.lua @@ -6,10 +6,8 @@ -- COPYING file in the source package for more information. -- -local config = require "core.configmanager"; - local host = module:get_host(); -local welcome_text = config.get("*", "core", "welcome_message") or "Hello $user, welcome to the $host IM server!"; +local welcome_text = module:get_option("welcome_message") or "Hello $user, welcome to the $host IM server!"; local st = require "util.stanza"; diff --git a/plugins/mod_xmlrpc.lua b/plugins/mod_xmlrpc.lua index 46edcaee..7165386a 100644 --- a/plugins/mod_xmlrpc.lua +++ b/plugins/mod_xmlrpc.lua @@ -16,6 +16,7 @@ local unpack = unpack; local tostring = tostring; local is_admin = require "core.usermanager".is_admin; local jid_split = require "util.jid".split; +local jid_bare = require "util.jid".bare; local b64_decode = require "util.encodings".base64.decode; local get_method = require "core.objectmanager".get_object; local validate_credentials = require "core.usermanager".validate_credentials; @@ -65,10 +66,15 @@ local function parse_xml(xml) return stanza.tags[1]; end -local function handle_xmlrpc_request(method, args) +local function handle_xmlrpc_request(jid, method, args) + local is_secure_call = (method:sub(1,7) == "secure/"); + if not is_admin(jid) and not is_secure_call then + return create_error_response(401, "not authorized"); + end method = get_method(method); if not method then return create_error_response(404, "method not found"); end args = args or {}; + if is_secure_call then table.insert(args, 1, jid); end local success, result = pcall(method, unpack(args)); if success then success, result = pcall(create_response, result or "nil"); @@ -77,22 +83,20 @@ local function handle_xmlrpc_request(method, args) end return create_error_response(500, "Error in creating response: "..result); end - return create_error_response(0, result or "nil"); + return create_error_response(0, tostring(result):gsub("^[^:]+:%d+: ", "")); end local function handle_xmpp_request(origin, stanza) local query = stanza.tags[1]; if query.name == "query" then if #query.tags == 1 then - if is_admin(stanza.attr.from) then - local success, method, args = pcall(translate_request, query.tags[1]); - if success then - local result = handle_xmlrpc_request(method, args); - origin.send(st.reply(stanza):tag('query', {xmlns='jabber:iq:rpc'}):add_child(result)); - else - origin.send(st.error_reply(stanza, "modify", "bad-request", method)); - end - else origin.send(st.error_reply(stanza, "auth", "forbidden", "No content in XML-RPC request")); end + local success, method, args = pcall(translate_request, query.tags[1]); + if success then + local result = handle_xmlrpc_request(jid_bare(stanza.attr.from), method, args); + origin.send(st.reply(stanza):tag('query', {xmlns='jabber:iq:rpc'}):add_child(result)); + else + origin.send(st.error_reply(stanza, "modify", "bad-request", method)); + end else origin.send(st.error_reply(stanza, "modify", "bad-request", "No content in XML-RPC request")); end else origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); end end @@ -106,7 +110,7 @@ local function handle_http_request(method, body, request) -- authenticate user local username, password = b64_decode(request['authorization'] or ''):gmatch('([^:]*):(.*)')(); -- TODO digest auth local node, host = jid_split(username); - if not validate_credentials(host, node, password) and is_admin(username) then + if not validate_credentials(host, node, password) then return unauthorized_response; end -- parse request @@ -117,7 +121,7 @@ local function handle_http_request(method, body, request) -- execute request local success, method, args = pcall(translate_request, stanza); if success then - return { headers = default_headers; body = tostring(handle_xmlrpc_request(method, args)) }; + return { headers = default_headers; body = tostring(handle_xmlrpc_request(node.."@"..host, method, args)) }; end return "<html><body>Error parsing XML-RPC request: "..tostring(method).."</body></html>"; end |