aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/mod_admin_shell.lua
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/mod_admin_shell.lua')
-rw-r--r--plugins/mod_admin_shell.lua494
1 files changed, 390 insertions, 104 deletions
diff --git a/plugins/mod_admin_shell.lua b/plugins/mod_admin_shell.lua
index e1016425..5c9d3df7 100644
--- a/plugins/mod_admin_shell.lua
+++ b/plugins/mod_admin_shell.lua
@@ -10,38 +10,39 @@
module:set_global();
module:depends("admin_socket");
-local hostmanager = require "core.hostmanager";
-local modulemanager = require "core.modulemanager";
-local s2smanager = require "core.s2smanager";
-local portmanager = require "core.portmanager";
-local helpers = require "util.helpers";
-local server = require "net.server";
-local st = require "util.stanza";
+local hostmanager = require "prosody.core.hostmanager";
+local modulemanager = require "prosody.core.modulemanager";
+local s2smanager = require "prosody.core.s2smanager";
+local portmanager = require "prosody.core.portmanager";
+local helpers = require "prosody.util.helpers";
+local server = require "prosody.net.server";
+local st = require "prosody.util.stanza";
local _G = _G;
local prosody = _G.prosody;
-local unpack = table.unpack or unpack; -- luacheck: ignore 113
-local iterators = require "util.iterators";
+local unpack = table.unpack;
+local iterators = require "prosody.util.iterators";
local keys, values = iterators.keys, iterators.values;
-local jid_bare, jid_split, jid_join = import("util.jid", "bare", "prepped_split", "join");
-local set, array = require "util.set", require "util.array";
-local cert_verify_identity = require "util.x509".verify_identity;
-local envload = require "util.envload".envload;
-local envloadfile = require "util.envload".envloadfile;
-local has_pposix, pposix = pcall(require, "util.pposix");
-local async = require "util.async";
-local serialization = require "util.serialization";
+local jid_bare, jid_split, jid_join, jid_resource, jid_compare = import("prosody.util.jid", "bare", "prepped_split", "join", "resource", "compare");
+local set, array = require "prosody.util.set", require "prosody.util.array";
+local cert_verify_identity = require "prosody.util.x509".verify_identity;
+local envload = require "prosody.util.envload".envload;
+local envloadfile = require "prosody.util.envload".envloadfile;
+local has_pposix, pposix = pcall(require, "prosody.util.pposix");
+local async = require "prosody.util.async";
+local serialization = require "prosody.util.serialization";
local serialize_config = serialization.new ({ fatal = false, unquoted = true});
-local time = require "util.time";
-local promise = require "util.promise";
+local time = require "prosody.util.time";
+local promise = require "prosody.util.promise";
+local logger = require "prosody.util.logger";
local t_insert = table.insert;
local t_concat = table.concat;
-local format_number = require "util.human.units".format;
-local format_table = require "util.human.io".table;
+local format_number = require "prosody.util.human.units".format;
+local format_table = require "prosody.util.human.io".table;
local function capitalize(s)
if not s then return end
@@ -83,8 +84,8 @@ function runner_callbacks:error(err)
self.data.print("Error: "..tostring(err));
end
-local function send_repl_output(session, line)
- return session.send(st.stanza("repl-output"):text(tostring(line)));
+local function send_repl_output(session, line, attr)
+ return session.send(st.stanza("repl-output", attr):text(tostring(line)));
end
function console:new_session(admin_session)
@@ -99,8 +100,14 @@ function console:new_session(admin_session)
end
return send_repl_output(admin_session, table.concat(t, "\t"));
end;
+ write = function (t)
+ return send_repl_output(admin_session, t, { eol = "0" });
+ end;
serialize = tostring;
disconnect = function () admin_session:close(); end;
+ is_connected = function ()
+ return not not admin_session.conn;
+ end
};
session.env = setmetatable({}, default_env_mt);
@@ -126,6 +133,11 @@ local function handle_line(event)
session = console:new_session(event.origin);
event.origin.shell_session = session;
end
+
+ local default_width = 132; -- The common default of 80 is a bit too narrow for e.g. s2s:show(), 132 was another common width for hardware terminals
+ local margin = 2; -- To account for '| ' when lines are printed
+ session.width = (tonumber(event.stanza.attr.width) or default_width)-margin;
+
local line = event.stanza:get_text();
local useglobalenv;
@@ -213,7 +225,7 @@ function commands.help(session, data)
print [[Commands are divided into multiple sections. For help on a particular section, ]]
print [[type: help SECTION (for example, 'help c2s'). Sections are: ]]
print [[]]
- local row = format_table({ { title = "Section"; width = 7 }; { title = "Description"; width = "100%" } })
+ local row = format_table({ { title = "Section", width = 7 }, { title = "Description", width = "100%" } }, session.width)
print(row())
print(row { "c2s"; "Commands to manage local client-to-server sessions" })
print(row { "s2s"; "Commands to manage sessions between this server and others" })
@@ -229,6 +241,7 @@ function commands.help(session, data)
print(row { "dns"; "Commands to manage and inspect the internal DNS resolver" })
print(row { "xmpp"; "Commands for sending XMPP stanzas" })
print(row { "debug"; "Commands for debugging the server" })
+ print(row { "watch"; "Commands for watching live logs from the server" })
print(row { "config"; "Reloading the configuration, etc." })
print(row { "columns"; "Information about customizing session listings" })
print(row { "console"; "Help regarding the console itself" })
@@ -256,28 +269,31 @@ function commands.help(session, data)
print [[host:deactivate(hostname) - Disconnects all clients on this host and deactivates]]
print [[host:list() - List the currently-activated hosts]]
elseif section == "user" then
- print [[user:create(jid, password, roles) - Create the specified user account]]
+ print [[user:create(jid, password, role) - Create the specified user account]]
print [[user:password(jid, password) - Set the password for the specified user account]]
print [[user:roles(jid, host) - Show current roles for an user]]
- print [[user:setroles(jid, host, roles) - Set roles for an user (see 'help roles')]]
+ print [[user:setrole(jid, host, role) - Set primary role of a user (see 'help roles')]]
+ print [[user:addrole(jid, host, role) - Add a secondary role to a user]]
+ print [[user:delrole(jid, host, role) - Remove a secondary role from a user]]
+ print [[user:disable(jid) - Disable the specified user account, preventing login]]
+ print [[user:enable(jid) - Enable the specified user account, restoring login access]]
print [[user:delete(jid) - Permanently remove the specified user account]]
print [[user:list(hostname, pattern) - List users on the specified host, optionally filtering with a pattern]]
elseif section == "roles" then
print [[Roles may grant access or restrict users from certain operations]]
print [[Built-in roles are:]]
- print [[ prosody:admin - Administrator]]
- print [[ (empty set) - Normal user]]
+ print [[ prosody:user - Normal user (default)]]
+ print [[ prosody:admin - Host administrator]]
+ print [[ prosody:operator - Server administrator]]
print [[]]
- print [[The canonical role format looks like: { ["example:role"] = true }]]
- print [[For convenience, the following formats are also accepted:]]
- print [["admin" - short for "prosody:admin", the normal admin status (like the admins config option)]]
- print [["example:role" - short for {["example:role"]=true}]]
- print [[{"example:role"} - short for {["example:role"]=true}]]
+ print [[Roles can be assigned using the user management commands (see 'help user').]]
elseif section == "muc" then
-- TODO `muc:room():foo()` commands
print [[muc:create(roomjid, { config }) - Create the specified MUC room with the given config]]
print [[muc:list(host) - List rooms on the specified MUC component]]
print [[muc:room(roomjid) - Reference the specified MUC room to access MUC API methods]]
+ print [[muc:occupants(roomjid, filter) - List room occupants, optionally filtered on substring or role]]
+ print [[muc:affiliations(roomjid, filter) - List affiliated members of the room, optionally filtered on substring or affiliation]]
elseif section == "server" then
print [[server:version() - Show the server's version number]]
print [[server:uptime() - Show how long the server has been running]]
@@ -305,6 +321,9 @@ function commands.help(session, data)
print [[debug:logevents(host) - Enable logging of fired events on host]]
print [[debug:events(host, event) - Show registered event handlers]]
print [[debug:timers() - Show information about scheduled timers]]
+ elseif section == "watch" then
+ print [[watch:log() - Follow debug logs]]
+ print [[watch:stanzas(target, filter) - Watch live stanzas matching the specified target and filter]]
elseif section == "console" then
print [[Hey! Welcome to Prosody's admin console.]]
print [[First thing, if you're ever wondering how to get out, simply type 'quit'.]]
@@ -335,7 +354,7 @@ function commands.help(session, data)
meta_columns[2].width = math.max(meta_columns[2].width or 0, #(spec.title or ""));
meta_columns[3].width = math.max(meta_columns[3].width or 0, #(spec.description or ""));
end
- local row = format_table(meta_columns, 120)
+ local row = format_table(meta_columns, session.width)
print(row());
for column, spec in iterators.sorted_pairs(available_columns) do
print(row({ column, spec.title, spec.description }));
@@ -481,6 +500,16 @@ function def_env.module:info(name, hosts)
local function item_name(item) return item.name; end
+ local function task_timefmt(t)
+ if not t then
+ return "no last run time"
+ elseif os.difftime(os.time(), t) < 86400 then
+ return os.date("last run today at %H:%M", t);
+ else
+ return os.date("last run %A at %H:%M", t);
+ end
+ end
+
local friendly_descriptions = {
["adhoc-provider"] = "Ad-hoc commands",
["auth-provider"] = "Authentication provider",
@@ -498,12 +527,22 @@ function def_env.module:info(name, hosts)
["auth-provider"] = item_name,
["storage-provider"] = item_name,
["http-provider"] = function(item, mod) return mod:http_url(item.name, item.default_path); end,
- ["net-provider"] = item_name,
+ ["net-provider"] = function(item)
+ local service_name = item.name;
+ local ports_list = {};
+ for _, interface, port in portmanager.get_active_services():iter(service_name, nil, nil) do
+ table.insert(ports_list, "["..interface.."]:"..port);
+ end
+ if not ports_list[1] then
+ return service_name..": not listening on any ports";
+ end
+ return service_name..": "..table.concat(ports_list, ", ");
+ end,
["measure"] = function(item) return item.name .. " (" .. suf(item.conf and item.conf.unit, " ") .. item.type .. ")"; end,
["metric"] = function(item)
return ("%s (%s%s)%s"):format(item.name, suf(item.mf.unit, " "), item.mf.type_, pre(": ", item.mf.description));
end,
- ["task"] = function (item) return string.format("%s (%s)", item.name or item.id, item.when); end
+ ["task"] = function (item) return string.format("%s (%s, %s)", item.name or item.id, item.when, task_timefmt(item.last)); end
};
for host in hosts do
@@ -533,21 +572,34 @@ function def_env.module:info(name, hosts)
if mod.module.dependencies and next(mod.module.dependencies) ~= nil then
print(" dependencies:");
for dep in pairs(mod.module.dependencies) do
- print(" - mod_" .. dep);
+ -- Dependencies are per module instance, not per host, so dependencies
+ -- of/on global modules may list modules not actually loaded on the
+ -- current host.
+ if modulemanager.is_loaded(host, dep) then
+ print(" - mod_" .. dep);
+ end
+ end
+ end
+ if mod.module.reverse_dependencies and next(mod.module.reverse_dependencies) ~= nil then
+ print(" reverse dependencies:");
+ for dep in pairs(mod.module.reverse_dependencies) do
+ if modulemanager.is_loaded(host, dep) then
+ print(" - mod_" .. dep);
+ end
end
end
end
return true;
end
-function def_env.module:load(name, hosts, config)
+function def_env.module:load(name, hosts)
hosts = get_hosts_with_module(hosts);
-- Load the module for each host
local ok, err, count, mod = true, nil, 0;
for host in hosts do
if (not modulemanager.is_loaded(host, name)) then
- mod, err = modulemanager.load(host, name, config);
+ mod, err = modulemanager.load(host, name);
if not mod then
ok = false;
if err == "global-module-already-loaded" then
@@ -644,7 +696,7 @@ end
def_env.config = {};
function def_env.config:load(filename, format)
- local config_load = require "core.configmanager".load;
+ local config_load = require "prosody.core.configmanager".load;
local ok, err = config_load(filename, format);
if not ok then
return false, err or "Unknown error loading config";
@@ -656,7 +708,7 @@ function def_env.config:get(host, key)
if key == nil then
host, key = "*", host;
end
- local config_get = require "core.configmanager".get
+ local config_get = require "prosody.core.configmanager".get
return true, serialize_config(config_get(host, key));
end
@@ -805,9 +857,7 @@ available_columns = {
mapper = function(conn, session)
if not session.secure then return "insecure"; end
if not conn or not conn:ssl() then return "secure" end
- local sock = conn and conn:socket();
- if not sock then return "secure"; end
- local tls_info = sock.info and sock:info();
+ local tls_info = conn.ssl_info and conn:ssl_info();
return tls_info and tls_info.protocol or "secure";
end;
};
@@ -817,8 +867,7 @@ available_columns = {
width = 30;
key = "conn";
mapper = function(conn)
- local sock = conn and conn:socket();
- local info = sock and sock.info and sock:info();
+ local info = conn and conn.ssl_info and conn:ssl_info();
if info then return info.cipher end
end;
};
@@ -915,6 +964,15 @@ available_columns = {
end
end
};
+ role = {
+ title = "Role";
+ description = "Session role";
+ width = 20;
+ key = "role";
+ mapper = function(role)
+ return role and role.name;
+ end;
+ }
};
local function get_colspec(colspec, default)
@@ -935,12 +993,12 @@ end
function def_env.c2s:show(match_jid, colspec)
local print = self.session.print;
- local columns = get_colspec(colspec, { "id"; "jid"; "ipv"; "status"; "secure"; "smacks"; "csi" });
- local row = format_table(columns, 120);
+ local columns = get_colspec(colspec, { "id"; "jid"; "role"; "ipv"; "status"; "secure"; "smacks"; "csi" });
+ local row = format_table(columns, self.session.width);
local function match(session)
local jid = get_jid(session)
- return (not match_jid) or jid == match_jid;
+ return (not match_jid) or jid_compare(jid, match_jid);
end
local group_by_host = true;
@@ -1019,7 +1077,7 @@ end
function def_env.s2s:show(match_jid, colspec)
local print = self.session.print;
local columns = get_colspec(colspec, { "id"; "host"; "dir"; "remote"; "ipv"; "secure"; "s2s_sasl"; "dialback" });
- local row = format_table(columns, 132);
+ local row = format_table(columns, self.session.width);
local function match(session)
local host, remote = get_s2s_hosts(session);
@@ -1229,18 +1287,18 @@ end
function def_env.host:list()
local print = self.session.print;
local i = 0;
- local type;
+ local host_type;
for host, host_session in iterators.sorted_pairs(prosody.hosts, _sort_hosts) do
i = i + 1;
- type = host_session.type;
- if type == "local" then
+ host_type = host_session.type;
+ if host_type == "local" then
print(host);
else
- type = module:context(host):get_option_string("component_module", type);
- if type ~= "component" then
- type = type .. " component";
+ host_type = module:context(host):get_option_string("component_module", host_type);
+ if host_type ~= "component" then
+ host_type = host_type .. " component";
end
- print(("%s (%s)"):format(host, type));
+ print(("%s (%s)"):format(host, host_type));
end
end
return true, i.." hosts";
@@ -1307,6 +1365,20 @@ local function check_muc(jid)
return room_name, host;
end
+local function get_muc(room_jid)
+ local room_name, host = check_muc(room_jid);
+ if not room_name then
+ return room_name, host;
+ end
+ local room_obj = prosody.hosts[host].modules.muc.get_room_from_jid(room_jid);
+ if not room_obj then
+ return nil, "No such room: "..room_jid;
+ end
+ return room_obj;
+end
+
+local muc_util = module:require"muc/util";
+
function def_env.muc:create(room_jid, config)
local room_name, host = check_muc(room_jid);
if not room_name then
@@ -1319,13 +1391,9 @@ function def_env.muc:create(room_jid, config)
end
function def_env.muc:room(room_jid)
- local room_name, host = check_muc(room_jid);
- if not room_name then
- return room_name, host;
- end
- local room_obj = prosody.hosts[host].modules.muc.get_room_from_jid(room_jid);
+ local room_obj, err = get_muc(room_jid);
if not room_obj then
- return nil, "No such room: "..room_jid;
+ return room_obj, err;
end
return setmetatable({ room = room_obj }, console_room_mt);
end
@@ -1344,33 +1412,170 @@ function def_env.muc:list(host)
return true, c.." rooms";
end
-local um = require"core.usermanager";
+function def_env.muc:occupants(room_jid, filter)
+ local room_obj, err = get_muc(room_jid);
+ if not room_obj then
+ return room_obj, err;
+ end
-local function coerce_roles(roles)
- if roles == "admin" then roles = "prosody:admin"; end
- if type(roles) == "string" then roles = { [roles] = true }; end
- if roles[1] then for i, role in ipairs(roles) do roles[role], roles[i] = true, nil; end end
- return roles;
+ local print = self.session.print;
+ local row = format_table({
+ { title = "Role"; width = 12; key = "role" }; -- longest role name
+ { title = "JID"; width = "75%"; key = "bare_jid" };
+ { title = "Nickname"; width = "25%"; key = "nick"; mapper = jid_resource };
+ }, self.session.width);
+ local occupants = array.collect(iterators.select(2, room_obj:each_occupant()));
+ local total = #occupants;
+ if filter then
+ occupants:filter(function(occupant)
+ return occupant.role == filter or jid_resource(occupant.nick):find(filter, 1, true);
+ end);
+ end
+ local displayed = #occupants;
+ occupants:sort(function(a, b)
+ if a.role ~= b.role then
+ return muc_util.valid_roles[a.role] > muc_util.valid_roles[b.role];
+ else
+ return a.bare_jid < b.bare_jid;
+ end
+ end);
+
+ if displayed == 0 then
+ return true, ("%d out of %d occupant%s listed"):format(displayed, total, total ~= 1 and "s" or "")
+ end
+
+ print(row());
+ for _, occupant in ipairs(occupants) do
+ print(row(occupant));
+ end
+
+ if total == displayed then
+ return true, ("%d occupant%s listed"):format(total, total ~= 1 and "s" or "")
+ else
+ return true, ("%d out of %d occupant%s listed"):format(displayed, total, total ~= 1 and "s" or "")
+ end
+end
+
+function def_env.muc:affiliations(room_jid, filter)
+ local room_obj, err = get_muc(room_jid);
+ if not room_obj then
+ return room_obj, err;
+ end
+
+ local print = self.session.print;
+ local row = format_table({
+ { title = "Affiliation"; width = 12 }; -- longest affiliation name
+ { title = "JID"; width = "75%" };
+ { title = "Nickname"; width = "25%"; key = "reserved_nickname" };
+ }, self.session.width);
+ local affiliated = array();
+ for affiliated_jid, affiliation, affiliation_data in room_obj:each_affiliation() do
+ affiliated:push(setmetatable({ affiliation; affiliated_jid }, { __index = affiliation_data }));
+ end
+
+ local total = #affiliated;
+ if filter then
+ affiliated:filter(function(affiliation)
+ return filter == affiliation[1] or affiliation[2]:find(filter, 1, true);
+ end);
+ end
+ local displayed = #affiliated;
+ local aff_ranking = muc_util.valid_affiliations;
+ affiliated:sort(function(a, b)
+ if a[1] ~= b[1] then
+ return aff_ranking[a[1]] > aff_ranking[b[1]];
+ else
+ return a[2] < b[2];
+ end
+ end);
+
+ if displayed == 0 then
+ return true, ("%d out of %d affiliations%s listed"):format(displayed, total, total ~= 1 and "s" or "")
+ end
+
+ print(row());
+ for _, affiliation in ipairs(affiliated) do
+ print(row(affiliation));
+ end
+
+
+ if total == displayed then
+ return true, ("%d affiliation%s listed"):format(total, total ~= 1 and "s" or "")
+ else
+ return true, ("%d out of %d affiliation%s listed"):format(displayed, total, total ~= 1 and "s" or "")
+ end
end
+local um = require"prosody.core.usermanager";
+
def_env.user = {};
-function def_env.user:create(jid, password, roles)
+function def_env.user:create(jid, password, role)
local username, host = jid_split(jid);
if not prosody.hosts[host] then
return nil, "No such host: "..host;
elseif um.user_exists(username, host) then
return nil, "User exists";
end
- local ok, err = um.create_user(username, password, host);
- if ok then
- if ok and roles then
- roles = coerce_roles(roles);
- local roles_ok, rerr = um.set_roles(jid, host, roles);
- if not roles_ok then return nil, "User created, but could not set roles: " .. tostring(rerr); end
+
+ if role then
+ local ok, err = um.create_user(username, nil, host);
+ if not ok then
+ return nil, "Could not create user: "..err;
+ end
+
+ local role_ok, rerr = um.set_user_role(jid, host, role);
+ if not role_ok then
+ return nil, "Could not set role: " .. tostring(rerr);
+ end
+
+ if password then
+ local ok, err = um.set_password(username, password, host, nil);
+ if not ok then
+ return nil, "Could not set password for user: "..err;
+ end
+
+ local ok, err = um.enable_user(username, host);
+ if not ok and err ~= "method not implemented" then
+ return nil, "Could not enable user: "..err;
+ end
end
- return true, "User created";
else
- return nil, "Could not create user: "..err;
+ local ok, err = um.create_user(username, password, host);
+ if not ok then
+ return nil, "Could not create user: "..err;
+ end
+ end
+
+ return true, "User created";
+end
+
+function def_env.user:disable(jid)
+ local username, host = jid_split(jid);
+ if not prosody.hosts[host] then
+ return nil, "No such host: "..host;
+ elseif not um.user_exists(username, host) then
+ return nil, "No such user";
+ end
+ local ok, err = um.disable_user(username, host);
+ if ok then
+ return true, "User disabled";
+ else
+ return nil, "Could not disable user: "..err;
+ end
+end
+
+function def_env.user:enable(jid)
+ local username, host = jid_split(jid);
+ if not prosody.hosts[host] then
+ return nil, "No such host: "..host;
+ elseif not um.user_exists(username, host) then
+ return nil, "No such user";
+ end
+ local ok, err = um.enable_user(username, host);
+ if ok then
+ return true, "User enabled";
+ else
+ return nil, "Could not enable user: "..err;
end
end
@@ -1404,41 +1609,64 @@ function def_env.user:password(jid, password)
end
end
-function def_env.user:roles(jid, host, new_roles)
- if new_roles or type(host) == "table" then
- return nil, "Use user:setroles(jid, host, roles) to change user roles";
- end
+function def_env.user:role(jid, host)
+ local print = self.session.print;
local username, userhost = jid_split(jid);
if host == nil then host = userhost; end
- if host ~= "*" and not prosody.hosts[host] then
+ if not prosody.hosts[host] then
return nil, "No such host: "..host;
elseif prosody.hosts[userhost] and not um.user_exists(username, userhost) then
return nil, "No such user";
end
- local roles = um.get_roles(jid, host);
- if not roles then return true, "No roles"; end
- local count = 0;
- local print = self.session.print;
- for role in pairs(roles) do
+
+ local primary_role = um.get_user_role(username, host);
+ local secondary_roles = um.get_user_secondary_roles(username, host);
+
+ print(primary_role and primary_role.name or "<none>");
+
+ local count = primary_role and 1 or 0;
+ for role_name in pairs(secondary_roles or {}) do
count = count + 1;
- print(role);
+ print(role_name.." (secondary)");
end
+
return true, count == 1 and "1 role" or count.." roles";
end
-def_env.user.showroles = def_env.user.roles; -- COMPAT
+def_env.user.roles = def_env.user.role;
--- user:roles("someone@example.com", "example.com", {"prosody:admin"})
--- user:roles("someone@example.com", {"prosody:admin"})
-function def_env.user:setroles(jid, host, new_roles)
+-- user:setrole("someone@example.com", "example.com", "prosody:admin")
+-- user:setrole("someone@example.com", "prosody:admin")
+function def_env.user:setrole(jid, host, new_role)
local username, userhost = jid_split(jid);
- if new_roles == nil then host, new_roles = userhost, host; end
- if host ~= "*" and not prosody.hosts[host] then
+ if new_role == nil then host, new_role = userhost, host; end
+ if not prosody.hosts[host] then
return nil, "No such host: "..host;
elseif prosody.hosts[userhost] and not um.user_exists(username, userhost) then
return nil, "No such user";
end
- if host == "*" then host = nil; end
- return um.set_roles(jid, host, coerce_roles(new_roles));
+ return um.set_user_role(username, host, new_role);
+end
+
+function def_env.user:addrole(jid, host, new_role)
+ local username, userhost = jid_split(jid);
+ if new_role == nil then host, new_role = userhost, host; end
+ if not prosody.hosts[host] then
+ return nil, "No such host: "..host;
+ elseif prosody.hosts[userhost] and not um.user_exists(username, userhost) then
+ return nil, "No such user";
+ end
+ return um.add_user_secondary_role(username, host, new_role);
+end
+
+function def_env.user:delrole(jid, host, role_name)
+ local username, userhost = jid_split(jid);
+ if role_name == nil then host, role_name = userhost, host; end
+ if not prosody.hosts[host] then
+ return nil, "No such host: "..host;
+ elseif prosody.hosts[userhost] and not um.user_exists(username, userhost) then
+ return nil, "No such user";
+ end
+ return um.remove_user_secondary_role(username, host, role_name);
end
-- TODO switch to table view, include roles
@@ -1462,7 +1690,7 @@ end
def_env.xmpp = {};
-local new_id = require "util.id".medium;
+local new_id = require "prosody.util.id".medium;
function def_env.xmpp:ping(localhost, remotehost, timeout)
localhost = select(2, jid_split(localhost));
remotehost = select(2, jid_split(remotehost));
@@ -1509,12 +1737,12 @@ function def_env.xmpp:ping(localhost, remotehost, timeout)
module:unhook("s2sin-established", onestablished);
module:unhook("s2s-destroyed", ondestroyed);
end):next(function(pong)
- return ("pong from %s in %gs"):format(pong.stanza.attr.from, time.now() - time_start);
+ return ("pong from %s on %s in %gs"):format(pong.stanza.attr.from, pong.origin.id, time.now() - time_start);
end);
end
def_env.dns = {};
-local adns = require"net.adns";
+local adns = require"prosody.net.adns";
local function get_resolver(session)
local resolver = session.dns_resolver;
@@ -1561,7 +1789,7 @@ function def_env.http:list(hosts)
local output = format_table({
{ title = "Module", width = "20%" },
{ title = "URL", width = "80%" },
- }, 132);
+ }, self.session.width);
for _, host in ipairs(hosts) do
local http_apps = modulemanager.get_items("http-provider", host);
@@ -1592,6 +1820,60 @@ function def_env.http:list(hosts)
return true;
end
+def_env.watch = {};
+
+function def_env.watch:log()
+ local writing = false;
+ local sink = logger.add_simple_sink(function (source, level, message)
+ if writing then return; end
+ writing = true;
+ self.session.print(source, level, message);
+ writing = false;
+ end);
+
+ while self.session.is_connected() do
+ async.sleep(3);
+ end
+ if not logger.remove_sink(sink) then
+ module:log("warn", "Unable to remove watch:log() sink");
+ end
+end
+
+local stanza_watchers = module:require("mod_debug_stanzas/watcher");
+function def_env.watch:stanzas(target_spec, filter_spec)
+ local function handler(event_type, stanza, session)
+ if stanza then
+ if event_type == "sent" then
+ self.session.print(("\n<!-- sent to %s -->"):format(session.id));
+ elseif event_type == "received" then
+ self.session.print(("\n<!-- received from %s -->"):format(session.id));
+ else
+ self.session.print(("\n<!-- %s (%s) -->"):format(event_type, session.id));
+ end
+ self.session.print(stanza);
+ elseif session then
+ self.session.print("\n<!-- session "..session.id.." "..event_type.." -->");
+ elseif event_type then
+ self.session.print("\n<!-- "..event_type.." -->");
+ end
+ end
+
+ stanza_watchers.add({
+ target_spec = {
+ jid = target_spec;
+ };
+ filter_spec = filter_spec and {
+ with_jid = filter_spec;
+ };
+ }, handler);
+
+ while self.session.is_connected() do
+ async.sleep(3);
+ end
+
+ stanza_watchers.remove(handler);
+end
+
def_env.debug = {};
function def_env.debug:logevents(host)
@@ -1603,7 +1885,7 @@ function def_env.debug:events(host, event)
local events_obj;
if host and host ~= "*" then
if host == "http" then
- events_obj = require "net.http.server"._events;
+ events_obj = require "prosody.net.http.server"._events;
elseif not prosody.hosts[host] then
return false, "Unknown host: "..host;
else
@@ -1617,7 +1899,7 @@ end
function def_env.debug:timers()
local print = self.session.print;
- local add_task = require"util.timer".add_task;
+ local add_task = require"prosody.util.timer".add_task;
local h, params = add_task.h, add_task.params;
local function normalize_time(t)
return t;
@@ -1914,7 +2196,7 @@ local function new_stats_context(self)
end
function def_env.stats:show(name_filter)
- local statsman = require "core.statsmanager"
+ local statsman = require "prosody.core.statsmanager"
local collect = statsman.collect
if collect then
-- force collection if in manual mode
@@ -1935,6 +2217,10 @@ function def_env.stats:show(name_filter)
end
+function module.unload()
+ stanza_watchers.cleanup();
+end
+
-------------