local iterators = require "util.iterators";
local keys, values = iterators.keys, iterators.values;
-local jid = require "util.jid";
-local jid_bare, jid_split = jid.bare, jid.split;
+local jid_bare, jid_split = import("util.jid", "bare", "prepped_split");
local set, array = require "util.set", require "util.array";
local cert_verify_identity = require "util.x509".verify_identity;
local envload = require "util.envload".envload;
return session;
end
+function console:process_line(session, line)
+ local useglobalenv;
+
+ if line:match("^>") then
+ line = line:gsub("^>", "");
+ useglobalenv = true;
+ elseif line == "\004" then
+ commands["bye"](session, line);
+ return;
+ else
+ local command = line:match("^%w+") or line:match("%p");
+ if commands[command] then
+ commands[command](session, line);
+ return;
+ end
+ end
+
+ session.env._ = line;
+
+ local chunkname = "=console";
+ local env = (useglobalenv and redirect_output(_G, session)) or session.env or nil
+ local chunk, err = envload("return "..line, chunkname, env);
+ if not chunk then
+ chunk, err = envload(line, chunkname, env);
+ if not chunk then
+ err = err:gsub("^%[string .-%]:%d+: ", "");
+ err = err:gsub("^:%d+: ", "");
+ err = err:gsub("'<eof>'", "the end of the line");
+ session.print("Sorry, I couldn't understand that... "..err);
+ return;
+ end
+ end
+
+ local ranok, taskok, message = pcall(chunk);
+
+ if not (ranok or message or useglobalenv) and commands[line:lower()] then
+ commands[line:lower()](session, line);
+ return;
+ end
+
+ if not ranok then
+ session.print("Fatal error while running command, it did not complete");
+ session.print("Error: "..taskok);
+ return;
+ end
+
+ if not message then
+ session.print("Result: "..tostring(taskok));
+ return;
+ elseif (not taskok) and message then
+ session.print("Command completed with a problem");
+ session.print("Message: "..tostring(message));
+ return;
+ end
+
+ session.print("OK: "..tostring(message));
+end
+
local sessions = {};
function console_listener.onconnect(conn)
end
for line in data:gmatch("[^\n]*[\n\004]") do
- -- Handle data (loop allows us to break to add \0 after response)
- repeat
- local useglobalenv;
-
- if line:match("^>") then
- line = line:gsub("^>", "");
- useglobalenv = true;
- elseif line == "\004" then
- commands["bye"](session, line);
- break;
- else
- local command = line:match("^%w+") or line:match("%p");
- if commands[command] then
- commands[command](session, line);
- break;
- end
- end
-
- session.env._ = line;
-
- local chunkname = "=console";
- local env = (useglobalenv and redirect_output(_G, session)) or session.env or nil
- local chunk, err = envload("return "..line, chunkname, env);
- if not chunk then
- chunk, err = envload(line, chunkname, env);
- if not chunk then
- err = err:gsub("^%[string .-%]:%d+: ", "");
- err = err:gsub("^:%d+: ", "");
- err = err:gsub("'<eof>'", "the end of the line");
- session.print("Sorry, I couldn't understand that... "..err);
- break;
- end
- end
-
- local ranok, taskok, message = pcall(chunk);
-
- if not (ranok or message or useglobalenv) and commands[line:lower()] then
- commands[line:lower()](session, line);
- break;
- end
-
- if not ranok then
- session.print("Fatal error while running command, it did not complete");
- session.print("Error: "..taskok);
- break;
- end
-
- if not message then
- session.print("Result: "..tostring(taskok));
- break;
- elseif (not taskok) and message then
- session.print("Command completed with a problem");
- session.print("Message: "..tostring(message));
- break;
- end
-
- session.print("OK: "..tostring(message));
- until true
-
+ if session.closed then return end
+ console:process_line(session, line);
session.send(string.char(0));
end
session.partial_data = data:match("[^\n]+$");
end
end
+function console_listener.ondetach(conn)
+ sessions[conn] = nil;
+end
+
-- Console commands --
-- These are simple commands, not valid standalone in Lua
function commands.bye(session)
session.print("See you! :)");
+ session.closed = true;
session.disconnect();
end
commands.quit, commands.exit = commands.bye, commands.bye;
print [[host - Commands to activate, deactivate and list virtual hosts]]
print [[user - Commands to create and delete users, and change their passwords]]
print [[server - Uptime, version, shutting down, etc.]]
+ print [[port - Commands to manage ports the server is listening on]]
+ print [[dns - Commands to manage and inspect the internal DNS resolver]]
print [[config - Reloading the configuration, etc.]]
print [[console - Help regarding the console itself]]
elseif section == "c2s" then
elseif section == "server" then
print [[server:version() - Show the server's version number]]
print [[server:uptime() - Show how long the server has been running]]
+ print [[server:memory() - Show details about the server's memory usage]]
print [[server:shutdown(reason) - Shut down the server, with an optional reason to be broadcast to all connections]]
+ elseif section == "port" then
+ print [[port:list() - Lists all network ports prosody currently listens on]]
+ print [[port:close(port, interface) - Close a port]]
+ elseif section == "dns" then
+ print [[dns:lookup(name, type, class) - Do a DNS lookup]]
+ print [[dns:addnameserver(nameserver) - Add a nameserver to the list]]
+ print [[dns:setnameserver(nameserver) - Replace the list of name servers with the supplied one]]
+ print [[dns:purge() - Clear the DNS cache]]
+ print [[dns:cache() - Show cached records]]
elseif section == "config" then
print [[config:reload() - Reload the server configuration. Modules may need to be reloaded for changes to take effect.]]
elseif section == "console" then
return true, "Shutdown initiated";
end
+local function human(kb)
+ local unit = "K";
+ if kb > 1024 then
+ kb, unit = kb/1024, "M";
+ end
+ return ("%0.2f%sB"):format(kb, unit);
+end
+
+function def_env.server:memory()
+ local pposix = require("util.pposix");
+ if not pposix.meminfo then
+ return true, "Lua is using "..collectgarbage("count");
+ end
+ local mem, lua_mem = pposix.meminfo(), collectgarbage("count");
+ local print = self.session.print;
+ print("Process: "..human((mem.allocated+mem.allocated_mmap)/1024));
+ print(" Used: "..human(mem.used/1024).." ("..human(lua_mem).." by Lua)");
+ print(" Free: "..human(mem.unused/1024).." ("..human(mem.returnable/1024).." returnable)");
+ return true, "OK";
+end
+
def_env.module = {};
local function get_hosts_set(hosts, module)
-- I think there's going to be more confusion among operators if we
-- break from that.
local function print_errors(print, errors)
- for depth, t in ipairs(errors) do
+ for depth, t in pairs(errors) do
print(
(" %d: %s"):format(
depth-1,
error("This version of LuaSec does not support certificate viewing");
end
else
- local certs = conn:getpeerchain();
- local cert = certs[1];
+ local cert = conn:getpeercertificate();
if cert then
+ local certs = conn:getpeerchain();
local digest = cert:digest("sha1");
if not cert_set[digest] then
local chain_valid, chain_errors = conn:getpeerverification();
end;
};
-function def_env.muc:room(room_jid)
- local room_name, host = jid_split(room_jid);
+local function check_muc(jid)
+ local room_name, host = jid_split(jid);
if not hosts[host] then
return nil, "No such host: "..host;
elseif not hosts[host].modules.muc then
return nil, "Host '"..host.."' is not a MUC service";
end
+ return room_name, host;
+end
+
+function def_env.muc:create(room_jid)
+ local room, host = check_muc(room_jid);
+ if not room_name then
+ return room_name, host;
+ end
+ if not room then return nil, host end
+ if hosts[host].modules.muc.rooms[room_jid] then return nil, "Room exists already" end
+ return hosts[host].modules.muc.create_room(room_jid);
+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 = hosts[host].modules.muc.rooms[room_jid];
if not room_obj then
return nil, "No such room: "..room_jid;
def_env.user = {};
function def_env.user:create(jid, password)
local username, host = jid_split(jid);
- if um.user_exists(username, host) then
+ if not 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);
function def_env.user:delete(jid)
local username, host = jid_split(jid);
- if not um.user_exists(username, host) then
+ if not 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.delete_user(username, host);
function def_env.user:password(jid, password)
local username, host = jid_split(jid);
- if not um.user_exists(username, host) then
+ if not 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.set_password(username, password, host);
return nil, "No such host";
end
local print = self.session.print;
- local count = 0;
+ local total, matches = 0, 0;
for user in um.users(host) do
if not pat or user:match(pat) then
print(user.."@"..host);
+ matches = matches + 1;
end
- count = count + 1;
+ total = total + 1;
end
- return true, count .. " users total";
+ return true, "Showing "..(pat and (matches.." of ") or "all " )..total.." users";
end
def_env.xmpp = {};
end
end
+def_env.dns = {};
+local adns = require"net.adns";
+local dns = require"net.dns";
+
+function def_env.dns:lookup(name, typ, class)
+ local ret = "Query sent";
+ local print = self.session.print;
+ local function handler(...)
+ ret = "Got response";
+ print(...);
+ end
+ adns.lookup(handler, name, typ, class);
+ return true, ret;
+end
+
+function def_env.dns:addnameserver(...)
+ dns._resolver:addnameserver(...)
+ return true
+end
+
+function def_env.dns:setnameserver(...)
+ dns._resolver:setnameserver(...)
+ return true
+end
+
+function def_env.dns:purge()
+ dns.purge()
+ return true
+end
+
+function def_env.dns:cache()
+ return true, "Cache:\n"..tostring(dns.cache())
+end
+
+def_env.http = {};
+
+function def_env.http:list()
+ local print = self.session.print;
+
+ for host in pairs(prosody.hosts) do
+ local http_apps = modulemanager.get_items("http-provider", host);
+ if #http_apps > 0 then
+ local http_host = module:context(host):get_option("http_host");
+ print("HTTP endpoints on "..host..(http_host and (" (using "..http_host.."):") or ":"));
+ for _, provider in ipairs(http_apps) do
+ local url = module:context(host):http_url(provider.name);
+ print("", url);
+ end
+ print("");
+ end
+ end
+
+ local default_host = module:get_option("http_default_host");
+ if not default_host then
+ print("HTTP requests to unknown hosts will return 404 Not Found");
+ else
+ print("HTTP requests to unknown hosts will be handled by "..default_host);
+ end
+ return true;
+end
+
-------------
function printbanner(session)