diff options
-rw-r--r-- | README | 2 | ||||
-rw-r--r-- | core/modulemanager.lua | 12 | ||||
-rw-r--r-- | core/rostermanager.lua | 9 | ||||
-rw-r--r-- | core/sessionmanager.lua | 2 | ||||
-rw-r--r-- | plugins/mod_announce.lua | 37 | ||||
-rw-r--r-- | plugins/mod_groups.lua | 93 | ||||
-rw-r--r-- | plugins/mod_httpserver.lua | 13 | ||||
-rwxr-xr-x | prosody | 7 | ||||
-rwxr-xr-x | prosodyctl | 37 | ||||
-rw-r--r-- | util/datamanager.lua | 37 | ||||
-rw-r--r-- | util/pluginloader.lua | 2 |
11 files changed, 238 insertions, 13 deletions
@@ -15,7 +15,7 @@ Documentation: http://prosody.im/doc/ Jabber/XMPP Chat: Address: - prosody@conference.heavy-horse.co.uk + prosody@conference.prosody.im Web interface: http://prosody.im/webchat diff --git a/core/modulemanager.lua b/core/modulemanager.lua index f160521f..082dcbb8 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -38,7 +38,8 @@ local _G = _G; module "modulemanager" -local api = {}; -- Module API container +api = {}; +local api = api; -- Module API container local modulemap = { ["*"] = {} }; @@ -134,6 +135,13 @@ function load(host, module_name, config) return nil, ret; end + if module_has_method(pluginenv, "load") then + local ok, err = call_module_method(pluginenv, "load"); + if (not ok) and err then + log("warn", "Error loading module '%s' on '%s': %s", module_name, host, err); + end + end + -- Use modified host, if the module set one modulemap[api_instance.host][module_name] = pluginenv; @@ -190,7 +198,7 @@ function reload(host, name, ...) local _mod, err = pluginloader.load_code(name); -- checking for syntax errors if not _mod then - log("error", "Unable to load module '%s': %s", module_name or "nil", err or "nil"); + log("error", "Unable to load module '%s': %s", name or "nil", err or "nil"); return nil, err; end diff --git a/core/rostermanager.lua b/core/rostermanager.lua index 867add2c..514d3d8e 100644 --- a/core/rostermanager.lua +++ b/core/rostermanager.lua @@ -82,19 +82,24 @@ end function load_roster(username, host) log("debug", "load_roster: asked for: "..username.."@"..host); + local roster; if hosts[host] and hosts[host].sessions[username] then - local roster = hosts[host].sessions[username].roster; + roster = hosts[host].sessions[username].roster; if not roster then log("debug", "load_roster: loading for new user: "..username.."@"..host); roster = datamanager.load(username, host, "roster") or {}; if not roster[false] then roster[false] = { }; end hosts[host].sessions[username].roster = roster; + hosts[host].events.fire_event("roster-load", username, host, roster); end return roster; end + -- Attempt to load roster for non-loaded user log("debug", "load_roster: loading for offline user: "..username.."@"..host); - return datamanager.load(username, host, "roster") or {}; + roster = datamanager.load(username, host, "roster") or {}; + hosts[host].events.fire_event("roster-load", username, host, roster); + return roster; end function save_roster(username, host) diff --git a/core/sessionmanager.lua b/core/sessionmanager.lua index 19943db0..103533de 100644 --- a/core/sessionmanager.lua +++ b/core/sessionmanager.lua @@ -149,6 +149,8 @@ function bind_resource(session, resource) session.roster = rm_load_roster(session.username, session.host); + hosts[session.host].events.fire_event("resource-bind", session); + return true; end diff --git a/plugins/mod_announce.lua b/plugins/mod_announce.lua new file mode 100644 index 00000000..992410f3 --- /dev/null +++ b/plugins/mod_announce.lua @@ -0,0 +1,37 @@ +local st, jid, set = require "util.stanza", require "util.jid", require "util.set"; + +local admins = set.new(config.get(module:get_host(), "core", "admins")); + +function handle_announcement(data) + local origin, stanza = data.origin, data.stanza; + local host, resource = select(2, jid.split(stanza.attr.to)); + + if resource ~= "announce/online" then + return; -- Not an announcement + end + + if not admins:contains(jid.bare(origin.full_jid)) then + -- Not an admin? Not allowed! + module:log("warn", "Non-admin %s tried to send server announcement", tostring(jid.bare(origin.full_jid))); + origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); + return; + end + + module:log("info", "Sending server announcement to all online users"); + local host_session = hosts[host]; + local message = st.clone(stanza); + message.attr.type = "headline"; + message.attr.from = host; + + local c = 0; + for user in pairs(host_session.sessions) do + c = c + 1; + message.attr.to = user.."@"..host; + core_post_stanza(host_session, message); + end + + module:log("info", "Announcement sent to %d online users", c); + return true; +end + +module:hook("message/host", handle_announcement); diff --git a/plugins/mod_groups.lua b/plugins/mod_groups.lua new file mode 100644 index 00000000..7a581717 --- /dev/null +++ b/plugins/mod_groups.lua @@ -0,0 +1,93 @@ + +local groups = { default = {} }; +local members = { [false] = {} }; + +local groups_file; + +local jid, datamanager = require "util.jid", require "util.datamanager"; +local jid_bare, jid_prep = jid.bare, jid.prep; + +local module_host = module:get_host(); + +function inject_roster_contacts(username, host, roster) + module:log("warn", "Injecting group members to roster"); + local bare_jid = username.."@"..host; + if not members[bare_jid] then return; end -- Not a member of any groups + + local function import_jids_to_roster(group_name) + for jid in pairs(groups[group_name]) do + -- Add them to roster + --module:log("debug", "processing jid %s in group %s", tostring(jid), tostring(group_name)); + if jid ~= bare_jid then + if not roster[jid] then roster[jid] = {}; end + roster[jid].subscription = "both"; + if not roster[jid].groups then + roster[jid].groups = { [group_name] = true }; + end + roster[jid].groups[group_name] = true; + roster[jid].persist = false; + end + end + end + + -- Find groups this JID is a member of + for _, group_name in ipairs(members[bare_jid]) do + import_jids_to_roster(group_name); + end + + -- Import public groups + for _, group_name in ipairs(members[false]) do + import_jids_to_roster(group_name); + end +end + +function remove_virtual_contacts(username, host, datastore, data) + if host == module_host and datastore == "roster" then + local new_roster = {}; + for jid, contact in pairs(data) do + if contact.persist ~= false then + new_roster[jid] = contact; + end + end + return username, host, datastore, new_roster; + end + + return username, host, datastore, data; +end + +function module.load() + groups_file = config.get(module:get_host(), "core", "groups_file"); + if not groups_file then return; end + + module:hook("roster-load", inject_roster_contacts); + datamanager.add_callback(remove_virtual_contacts); + + groups = { default = {} }; + members = { [false] = {} }; + local curr_group = "default"; + for line in io.lines(groups_file) do + if line:match("^%s*%[.-%]%s*$") then + curr_group = line:match("^%s*%[(.-)%]%s*$"); + if curr_group:match("^%+") then + curr_group = curr_group:gsub("^%+", ""); + members[false][#members[false]+1] = curr_group; -- Is a public group + end + module:log("debug", "New group: %s", tostring(curr_group)); + groups[curr_group] = groups[curr_group] or {}; + else + -- Add JID + local jid = jid_prep(line); + if jid then + module:log("debug", "New member of %s: %s", tostring(curr_group), tostring(jid)); + groups[curr_group][jid] = true; + members[jid] = members[jid] or {}; + members[jid][#members[jid]+1] = curr_group; + end + end + end + module:log("info", "Groups loaded successfully"); +end + +function module.unload() + datamanager.remove_callback(remove_virtual_contacts); +end diff --git a/plugins/mod_httpserver.lua b/plugins/mod_httpserver.lua index 2bcdab43..ec22a4bf 100644 --- a/plugins/mod_httpserver.lua +++ b/plugins/mod_httpserver.lua @@ -19,4 +19,15 @@ local function handle_request(method, body, request) return data; end -httpserver.new{ port = 5280, base = "files", handler = handle_request, ssl = false}
\ No newline at end of file +local ports = config.get(module.host, "core", "http_ports") or { 5280 }; +for _, options in ipairs(ports) do + local port, base, ssl, interface = 5280, "files", false, nil; + if type(options) == "number" then + port = options; + elseif type(options) == "table" then + port, base, ssl, interface = options.port or 5280, options.path or "files", options.ssl or false, options.interface; + elseif type(options) == "string" then + base = options; + end + httpserver.new{ port = port, base = base, handler = handle_request, ssl = ssl } +end @@ -135,8 +135,11 @@ 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"); +require "util.datamanager".add_callback(function(username, host, datastore, data) + if config.get(host, "core", "anonymous_login") then + return false; + end + return username, host, datastore, data; end); ----------- End of out-of-place code -------------- @@ -94,12 +94,14 @@ local error_messages = setmetatable({ ["no-such-user"] = "The given user does not exist on the server"; ["unable-to-save-data"] = "Unable to store, perhaps you don't have permission?"; ["no-pidfile"] = "There is no pidfile option in the configuration file, see http://prosody.im/doc/prosodyctl#pidfile for help"; + ["no-such-method"] = "This module has no commands"; }, { __index = function (t,k) return "Error: "..(tostring(k):gsub("%-", " "):gsub("^.", string.upper)); end }); hosts = {}; require "core.hostmanager" require "core.eventmanager".fire_event("server-starting"); +require "core.modulemanager" require "util.prosodyctl" ----------------------- @@ -404,6 +406,41 @@ end --------------------- +if command:match("^mod_") then -- Is a command in a module + local module_name = command:match("^mod_(.+)"); + local ret, err = modulemanager.load("*", module_name); + if not ret then + show_message("Failed to load module '"..module_name.."': "..err); + os.exit(1); + end + + table.remove(arg, 1); + + local module = modulemanager.get_module("*", module_name); + if not module then + show_message("Failed to load module '"..module_name.."': Unknown error"); + os.exit(1); + end + + if not modulemanager.module_has_method(module, "command") then + show_message("Fail: mod_"..module_name.." does not support any commands"); + os.exit(1); + end + + local ok, ret = modulemanager.call_module_method(module, "command", arg); + if ok then + if type(ret) == "number" then + os.exit(ret); + elseif type(ret) == "string" then + show_message(ret); + end + os.exit(0); -- :) + else + show_message("Failed to execute command: "..error_messages[ret]); + os.exit(1); -- :( + end +end + if not commands[command] then -- Show help for all commands function show_usage(usage, desc) print(" "..usage); diff --git a/util/datamanager.lua b/util/datamanager.lua index 41d09f06..54cf1959 100644 --- a/util/datamanager.lua +++ b/util/datamanager.lua @@ -50,7 +50,7 @@ local function mkdir(path) end local data_path = "data"; -local callback; +local callbacks = {}; ------- API ------------- @@ -58,8 +58,32 @@ function set_data_path(path) log("debug", "Setting data path to: %s", path); data_path = path; end -function set_callback(func) - callback = func; + +local function callback(username, host, datastore, data) + for _, f in ipairs(callbacks) do + username, host, datastore, data = f(username, host, datastore, data); + if not username then break; end + end + + return username, host, datastore, data; +end +function add_callback(func) + if not callbacks[func] then -- Would you really want to set the same callback more than once? + callbacks[func] = true; + callbacks[#callbacks+1] = func; + return true; + end +end +function remove_callback(func) + if callbacks[func] then + for i, f in ipairs(callbacks) do + if f == func then + callbacks[i] = nil; + callbacks[f] = nil; + return true; + end + end + end end function getpath(username, host, datastore, ext, create) @@ -97,7 +121,12 @@ function store(username, host, datastore, data) if not data then data = {}; end - if callback and callback(username, host, datastore) then return true; end + + username, host, datastore, data = callback(username, host, datastore, data); + if not username then + return true; -- Don't save this data at all + end + -- save the datastore local f, msg = io_open(getpath(username, host, datastore, nil, true), "w+"); if not f then diff --git a/util/pluginloader.lua b/util/pluginloader.lua index 1bf22f62..86075c90 100644 --- a/util/pluginloader.lua +++ b/util/pluginloader.lua @@ -27,7 +27,7 @@ end function load_code(plugin, resource) local content, err = load_resource(plugin, resource); if not content then return content, err; end - return loadstring(content, err), err; + return loadstring(content, err); end return _M; |