Merge 0.9->trunk
[prosody.git] / plugins / mod_admin_telnet.lua
index 3d149623344be871187824eb836bad493cc0094c..6fc378bb6253ad936187b3689fba24c67fed1899 100644 (file)
@@ -17,7 +17,6 @@ local _G = _G;
 
 local prosody = _G.prosody;
 local hosts = prosody.hosts;
-local incoming_s2s = prosody.incoming_s2s;
 
 local console_listener = { default_port = 5582; default_mode = "*a"; interface = "127.0.0.1" };
 
@@ -236,6 +235,7 @@ function commands.help(session, data)
        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]]
@@ -300,6 +300,26 @@ function def_env.server:shutdown(reason)
        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()
+       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)
@@ -463,6 +483,25 @@ end
 function def_env.hosts:add(name)
 end
 
+local function session_flags(session, line)
+       line = line or {};
+       if session.cert_identity_status == "valid" then
+               line[#line+1] = "(secure)";
+       elseif session.secure then
+               line[#line+1] = "(encrypted)";
+       end
+       if session.compressed then
+               line[#line+1] = "(compressed)";
+       end
+       if session.smacks then
+               line[#line+1] = "(sm)";
+       end
+       if session.ip and session.ip:match(":") then
+               line[#line+1] = "(IPv6)";
+       end
+       return table.concat(line, " ");
+end
+
 def_env.c2s = {};
 
 local function show_c2s(callback)
@@ -498,14 +537,9 @@ function def_env.c2s:show(match_jid)
                        count = count + 1;
                        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
+                               status = session.presence:get_child_text("show") or "available";
                        end
-                       print("   "..jid.." - "..status.."("..priority..")");
+                       print(session_flags(session, { "   "..jid.." - "..status.."("..priority..")" }));
                end             
        end);
        return true, "Total: "..count.." clients";
@@ -544,96 +578,80 @@ function def_env.c2s:close(match_jid)
        return true, "Total: "..count.." sessions closed";
 end
 
-local function session_flags(session, line)
-       if session.cert_identity_status == "valid" then
-               line[#line+1] = "(secure)";
-       elseif session.secure then
-               line[#line+1] = "(encrypted)";
-       end
-       if session.compressed then
-               line[#line+1] = "(compressed)";
-       end
-       if session.smacks then
-               line[#line+1] = "(sm)";
-       end
-       if session.conn and session.conn:ip():match(":") then
-               line[#line+1] = "(IPv6)";
-       end
-       return table.concat(line, " ");
-end
 
 def_env.s2s = {};
 function def_env.s2s:show(match_jid)
-       local _print = self.session.print;
        local print = self.session.print;
        
        local count_in, count_out = 0,0;
+       local s2s_list = { };
        
-       for host, host_session in pairs(hosts) do
-               print = function (...) _print(host); _print(...); print = _print; end
-               for remotehost, session in pairs(host_session.s2sout) do
-                       if (not match_jid) or remotehost:match(match_jid) or host:match(match_jid) then
-                               count_out = count_out + 1;
-                               print(session_flags(session, {"   ", host, "->", remotehost}));
-                               if session.sendq then
-                                       print("        There are "..#session.sendq.." queued outgoing stanzas for this connection");
-                               end
-                               if session.type == "s2sout_unauthed" then
-                                       if session.connecting then
-                                               print("        Connection not yet established");
-                                               if not session.srv_hosts then
-                                                       if not session.conn then
-                                                               print("        We do not yet have a DNS answer for this host's SRV records");
-                                                       else
-                                                               print("        This host has no SRV records, using A record instead");
-                                                       end
-                                               elseif session.srv_choice then
-                                                       print("        We are on SRV record "..session.srv_choice.." of "..#session.srv_hosts);
-                                                       local srv_choice = session.srv_hosts[session.srv_choice];
-                                                       print("        Using "..(srv_choice.target or ".")..":"..(srv_choice.port or 5269));
+       local s2s_sessions = module:shared"/*/s2s/sessions";
+       for _, session in pairs(s2s_sessions) do
+               local remotehost, localhost, direction;
+               if session.direction == "outgoing" then
+                       direction = "->";
+                       count_out = count_out + 1;
+                       remotehost, localhost = session.to_host or "?", session.from_host or "?";
+               else
+                       direction = "<-";
+                       count_in = count_in + 1;
+                       remotehost, localhost = session.from_host or "?", session.to_host or "?";
+               end
+               local sess_lines = { l = localhost, r = remotehost,
+                       session_flags(session, { "", direction, remotehost or "?",
+                               "["..session.type..tostring(session):match("[a-f0-9]*$").."]" })};
+
+               if (not match_jid) or remotehost:match(match_jid) or localhost:match(match_jid) then
+                       table.insert(s2s_list, sess_lines);
+                       local print = function (s) table.insert(sess_lines, "        "..s); end
+                       if session.sendq then
+                               print("There are "..#session.sendq.." queued outgoing stanzas for this connection");
+                       end
+                       if session.type == "s2sout_unauthed" then
+                               if session.connecting then
+                                       print("Connection not yet established");
+                                       if not session.srv_hosts then
+                                               if not session.conn then
+                                                       print("We do not yet have a DNS answer for this host's SRV records");
+                                               else
+                                                       print("This host has no SRV records, using A record instead");
                                                end
-                                       elseif session.notopen then
-                                               print("        The <stream> has not yet been opened");
-                                       elseif not session.dialback_key then
-                                               print("        Dialback has not been initiated yet");
-                                       elseif session.dialback_key then
-                                               print("        Dialback has been requested, but no result received");
+                                       elseif session.srv_choice then
+                                               print("We are on SRV record "..session.srv_choice.." of "..#session.srv_hosts);
+                                               local srv_choice = session.srv_hosts[session.srv_choice];
+                                               print("Using "..(srv_choice.target or ".")..":"..(srv_choice.port or 5269));
                                        end
+                               elseif session.notopen then
+                                       print("The <stream> has not yet been opened");
+                               elseif not session.dialback_key then
+                                       print("Dialback has not been initiated yet");
+                               elseif session.dialback_key then
+                                       print("Dialback has been requested, but no result received");
                                end
                        end
-               end     
-               local subhost_filter = function (h)
-                               return (match_jid and h:match(match_jid));
-                       end
-               for session in pairs(incoming_s2s) do
-                       if session.to_host == host and ((not match_jid) or host:match(match_jid)
-                               or (session.from_host and session.from_host:match(match_jid))
-                               -- Pft! is what I say to list comprehensions
-                               or (session.hosts and #array.collect(keys(session.hosts)):filter(subhost_filter)>0)) then
-                               count_in = count_in + 1;
-                               print(session_flags(session, {"   ", host, "<-", session.from_host or "(unknown)"}));
-                               if session.type == "s2sin_unauthed" then
-                                               print("        Connection not yet authenticated");
-                               end
+                       if session.type == "s2sin_unauthed" then
+                               print("Connection not yet authenticated");
+                       elseif session.type == "s2sin" then
                                for name in pairs(session.hosts) do
                                        if name ~= session.from_host then
-                                               print("        also hosts "..tostring(name));
+                                               print("also hosts "..tostring(name));
                                        end
                                end
                        end
                end
-               
-               print = _print;
        end
-       
-       for session in pairs(incoming_s2s) do
-               if not session.to_host and ((not match_jid) or session.from_host and session.from_host:match(match_jid)) then
-                       count_in = count_in + 1;
-                       print("Other incoming s2s connections");
-                       print("    (unknown) <- "..(session.from_host or "(unknown)"));                 
-               end
+
+       -- Sort by local host, then remote host
+       table.sort(s2s_list, function(a,b)
+               if a.l == b.l then return a.r < b.r; end
+               return a.l < b.l;
+       end);
+       local lasthost;
+       for _, sess_lines in ipairs(s2s_list) do
+               if sess_lines.l ~= lasthost then print(sess_lines.l); lasthost=sess_lines.l end
+               for _, line in ipairs(sess_lines) do print(line); end
        end
-       
        return true, "Total: "..count_out.." outgoing, "..count_in.." incoming connections";
 end
 
@@ -665,14 +683,9 @@ end
 function def_env.s2s:showcert(domain)
        local ser = require "util.serialization".serialize;
        local print = self.session.print;
-       local domain_sessions = set.new(array.collect(keys(incoming_s2s)))
-               /function(session) return session.from_host == domain and session or nil; end;
-       for local_host in values(prosody.hosts) do
-               local s2sout = local_host.s2sout;
-               if s2sout and s2sout[domain] then
-                       domain_sessions:add(s2sout[domain]);
-               end
-       end
+       local s2s_sessions = module:shared"/*/s2s/sessions";
+       local domain_sessions = set.new(array.collect(values(s2s_sessions)))
+               /function(session) return (session.to_host == domain or session.from_host == domain) and session or nil; end;
        local cert_set = {};
        for session in domain_sessions do
                local conn = session.conn;
@@ -763,76 +776,38 @@ end
 
 function def_env.s2s:close(from, to)
        local print, count = self.session.print, 0;
+       local s2s_sessions = module:shared"/*/s2s/sessions";
        
-       if not (from and to) then
+       local match_id;
+       if from and not to then
+               match_id, from = from;
+       elseif not to then
                return false, "Syntax: s2s:close('from', 'to') - Closes all s2s sessions from 'from' to 'to'";
        elseif from == to then
                return false, "Both from and to are the same... you can't do that :)";
        end
        
-       if hosts[from] and not hosts[to] then
-               -- Is an outgoing connection
-               local session = hosts[from].s2sout[to];
-               if not session then
-                       print("No outgoing connection from "..from.." to "..to)
-               else
+       for _, session in pairs(s2s_sessions) do
+               local id = session.type..tostring(session):match("[a-f0-9]+$");
+               if (match_id and match_id == id)
+               or (session.from_host == from and session.to_host == to) then
+                       print(("Closing connection from %s to %s [%s]"):format(session.from_host, session.to_host, id));
                        (session.close or s2smanager.destroy_session)(session);
-                       count = count + 1;
-                       print("Closed outgoing session from "..from.." to "..to);
+                       count = count + 1 ;
                end
-       elseif hosts[to] and not hosts[from] then
-               -- Is an incoming connection
-               for session in pairs(incoming_s2s) do
-                       if session.to_host == to and session.from_host == from then
-                               (session.close or s2smanager.destroy_session)(session);
-                               count = count + 1;
                        end
-               end
-               
-               if count == 0 then
-                       print("No incoming connections from "..from.." to "..to);
-               else
-                       print("Closed "..count.." incoming session"..((count == 1 and "") or "s").." from "..from.." to "..to);
-               end
-       elseif hosts[to] and hosts[from] then
-               return false, "Both of the hostnames you specified are local, there are no s2s sessions to close";
-       else
-               return false, "Neither of the hostnames you specified are being used on this server";
-       end
-       
        return true, "Closed "..count.." s2s session"..((count == 1 and "") or "s");
 end
 
 function def_env.s2s:closeall(host)
         local count = 0;
-
-        if not host or type(host) ~= "string" then return false, "wrong syntax: please use s2s:closeall('hostname.tld')"; end
-        if hosts[host] then
-                for session in pairs(incoming_s2s) do
-                        if session.to_host == host then
-                                (session.close or s2smanager.destroy_session)(session);
+       local s2s_sessions = module:shared"/*/s2s/sessions";
+       for _,session in pairs(s2s_sessions) do
+               if not host or session.from_host == host or session.to_host == host then
+                       session:close();
                                 count = count + 1;
                         end
                 end
-                for _, session in pairs(hosts[host].s2sout) do
-                        (session.close or s2smanager.destroy_session)(session);
-                        count = count + 1;
-                end
-        else
-                for session in pairs(incoming_s2s) do
-                       if session.from_host == host then
-                               (session.close or s2smanager.destroy_session)(session);
-                               count = count + 1;
-                       end
-               end
-               for _, h in pairs(hosts) do
-                       if h.s2sout[host] then
-                               (h.s2sout[host].close or s2smanager.destroy_session)(h.s2sout[host]);
-                               count = count + 1;
-                       end
-               end
-        end
-
        if count == 0 then return false, "No sessions to close.";
        else return true, "Closed "..count.." s2s session"..((count == 1 and "") or "s"); end
 end
@@ -922,6 +897,8 @@ end
 
 function def_env.muc:create(room_jid)
        local room, host = check_muc(room_jid);
+       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
 
@@ -956,7 +933,7 @@ function def_env.user:delete(jid)
        local username, host = jid_split(jid);
        if not hosts[host] then
                return nil, "No such host: "..host;
-       elseif um.user_exists(username, host) then
+       elseif not um.user_exists(username, host) then
                return nil, "No such user";
        end
        local ok, err = um.delete_user(username, host);
@@ -971,7 +948,7 @@ function def_env.user:password(jid, password)
        local username, host = jid_split(jid);
        if not hosts[host] then
                return nil, "No such host: "..host;
-       elseif um.user_exists(username, host) then
+       elseif not um.user_exists(username, host) then
                return nil, "No such user";
        end
        local ok, err = um.set_password(username, password, host);