mod_admin_messageconsole.lua
branch0.8
changeset 2 e193f80521cc
parent 1 7265595dbc3b
equal deleted inserted replaced
1:7265595dbc3b 2:e193f80521cc
     1 -- Prosody IM
     1 -- Prosody IM
     2 --
     2 --
     3 -- This module depends on Prosody's admin_telnet module
     3 -- This module depends on Prosody's admin_telnet module
       
     4 -- and some code (redirect_output() and onincoming() from console_listener)
       
     5 -- is duplicated here...
     4 --
     6 --
     5 -- Copyright (C) 2008-2010 Matthew Wild
     7 -- Copyright (C) 2008-2010 Matthew Wild
     6 -- Copyright (C) 2008-2010 Waqas Hussain
     8 -- Copyright (C) 2008-2010 Waqas Hussain
     7 -- Copyright (C) 2012 Mikael Berthe
     9 -- Copyright (C) 2012 Mikael Berthe
     8 --
    10 --
    11 --
    13 --
    12 
    14 
    13 local st = require "util.stanza";
    15 local st = require "util.stanza";
    14 local um_is_admin = require "core.usermanager".is_admin;
    16 local um_is_admin = require "core.usermanager".is_admin;
    15 
    17 
    16 local admin_telnet = module:depends("admin_telnet");
    18 local telnet_def_env;
    17 local telnet_def_env = module:shared("/*/admin_telnet/env");
    19 local default_env_mt;
    18 local telnet_commands = module:shared("/*/admin_telnet/commands");
    20 local telnet_commands;
    19 local default_env_mt = { __index = telnet_def_env };
       
    20 
    21 
    21 local host = module.host;
    22 local host = module.host;
    22 
    23 
    23 -- Create our own session.  print() will store the results in a text
    24 -- Create our own session.  print() will store the results in a text
    24 -- string.  send(), quit(), disconnect() are no-op.
    25 -- string.  send(), quit(), disconnect() are no-op.
    26 	local session = {
    27 	local session = {
    27 			send	    = function ()  end;
    28 			send	    = function ()  end;
    28 			quit	    = function ()  end;
    29 			quit	    = function ()  end;
    29 			disconnect  = function ()  end;
    30 			disconnect  = function ()  end;
    30 			};
    31 			};
       
    32 
       
    33 	-- Initialization
       
    34 	-- Hack for 0.8 - Let's get dependencies now as we're almost
       
    35 	-- certain the admin_telnet module has been loaded by now.
       
    36 	if not telnet_def_env then
       
    37 		local prosody = _G.prosody;
       
    38 		local console = prosody.console;
       
    39 
       
    40 		telnet_def_env = console.env;
       
    41 		default_env_mt = { __index = telnet_def_env };
       
    42 
       
    43 		telnet_commands = console.commands;
       
    44 	end
    31 
    45 
    32 	session.print = function (...)
    46 	session.print = function (...)
    33 		local t = {};
    47 		local t = {};
    34 		for i=1,select("#", ...) do
    48 		for i=1,select("#", ...) do
    35 			t[i] = tostring(select(i, ...));
    49 			t[i] = tostring(select(i, ...));
    53 	end
    67 	end
    54 
    68 
    55 	return session;
    69 	return session;
    56 end
    70 end
    57 
    71 
    58 local function on_message(event)
    72 -- This function is 100% duplicated from mod_admin_telnet.
       
    73 function onincoming(session, data)
       
    74 
       
    75 	local commands = telnet_commands;
       
    76 	local partial = session.partial_data;
       
    77 	if partial then
       
    78 		data = partial..data;
       
    79 	end
       
    80 
       
    81 	local function redirect_output(_G, session)
       
    82 		local env = setmetatable({ print = session.print },
       
    83 			     { __index = function (t, k) return rawget(_G, k); end });
       
    84 		env.dofile = function(name)
       
    85 			local f, err = loadfile(name);
       
    86 			if not f then return f, err; end
       
    87 			return setfenv(f, env)();
       
    88 		end;
       
    89 		return env;
       
    90 	end
       
    91 
       
    92 	for line in data:gmatch("[^\n]*[\n\004]") do
       
    93 		-- Handle data (loop allows us to break to add \0 after response)
       
    94 		repeat
       
    95 			local useglobalenv;
       
    96 
       
    97 			if line:match("^>") then
       
    98 				line = line:gsub("^>", "");
       
    99 				useglobalenv = true;
       
   100 			elseif line == "\004" then
       
   101 				commands["bye"](session, line);
       
   102 				break;
       
   103 			else
       
   104 				local command = line:lower();
       
   105 				command = line:match("^%w+") or line:match("%p");
       
   106 				if commands[command] then
       
   107 					commands[command](session, line);
       
   108 					break;
       
   109 				end
       
   110 			end
       
   111 
       
   112 			session.env._ = line;
       
   113 
       
   114 			local chunkname = "=console";
       
   115 			local chunk, err = loadstring("return "..line, chunkname);
       
   116 			if not chunk then
       
   117 				chunk, err = loadstring(line, chunkname);
       
   118 				if not chunk then
       
   119 					err = err:gsub("^%[string .-%]:%d+: ", "");
       
   120 					err = err:gsub("^:%d+: ", "");
       
   121 					err = err:gsub("'<eof>'", "the end of the line");
       
   122 					session.print("Sorry, I couldn't understand that... "..err);
       
   123 					break;
       
   124 				end
       
   125 			end
       
   126 
       
   127 			setfenv(chunk, (useglobalenv and redirect_output(_G, session)) or session.env or nil);
       
   128 
       
   129 			local ranok, taskok, message = pcall(chunk);
       
   130 
       
   131 			if not (ranok or message or useglobalenv) and commands[line:lower()] then
       
   132 				commands[line:lower()](session, line);
       
   133 				break;
       
   134 			end
       
   135 
       
   136 			if not ranok then
       
   137 				session.print("Fatal error while running command, it did not complete");
       
   138 				session.print("Error: "..taskok);
       
   139 				break;
       
   140 			end
       
   141 
       
   142 			if not message then
       
   143 				session.print("Result: "..tostring(taskok));
       
   144 				break;
       
   145 			elseif (not taskok) and message then
       
   146 				session.print("Command completed with a problem");
       
   147 				session.print("Message: "..tostring(message));
       
   148 				break;
       
   149 			end
       
   150 
       
   151 			session.print("OK: "..tostring(message));
       
   152 		until true
       
   153 
       
   154 		session.send(string.char(0));
       
   155 	end
       
   156 	session.partial_data = data:match("[^\n]+$");
       
   157 end
       
   158 
       
   159 function on_message(event)
    59 	-- Check the type of the incoming stanza to avoid loops:
   160 	-- Check the type of the incoming stanza to avoid loops:
    60 	if event.stanza.attr.type == "error" then
   161 	if event.stanza.attr.type == "error" then
    61 		return; -- We do not want to reply to these, so leave.
   162 		return; -- We do not want to reply to these, so leave.
    62 	end
   163 	end
    63 
   164 
    78 
   179 
    79 	-- Create a session in order to use an admin_telnet-like environment
   180 	-- Create a session in order to use an admin_telnet-like environment
    80 	local session = new_session();
   181 	local session = new_session();
    81 
   182 
    82 	-- Process the message using admin_telnet's onincoming function
   183 	-- Process the message using admin_telnet's onincoming function
    83 	admin_telnet.console_incoming_message(session, body.."\n");
   184 	onincoming(session, body.."\n");
    84 
   185 
    85 	-- Strip trailing blank line
   186 	-- Strip trailing blank line
    86 	session.fulltext = tostring(session.fulltext):gsub("\n\|%s*$", "")
   187 	session.fulltext = tostring(session.fulltext):gsub("\n\|%s*$", "")
    87 
   188 
    88 	-- Send the reply stanza
   189 	-- Send the reply stanza
    89 	local reply_stanza = st.message({ from = host, to = userjid,
   190 	local reply_stanza = st.message({ from = host, to = userjid,
    90 					type = "chat" });
   191 					type = "chat" });
    91 	reply_stanza = reply_stanza:body(session.fulltext);
   192 	reply_stanza = reply_stanza:body(session.fulltext);
    92 	module:send(reply_stanza);
   193 	core_post_stanza(hosts[module.host], reply_stanza);
    93 
   194 
    94 	return true;
   195 	return true;
    95 end
   196 end
    96 
   197 
    97 local function on_presence(event)
   198 local function on_presence(event)
   112 		send_presence = true;
   213 		send_presence = true;
   113 		-- Send a subscription ack
   214 		-- Send a subscription ack
   114 		local presence_stanza = st.presence({ from = host,
   215 		local presence_stanza = st.presence({ from = host,
   115 					to = userjid, type = "subscribed",
   216 					to = userjid, type = "subscribed",
   116 					id = event.stanza.attr.id });
   217 					id = event.stanza.attr.id });
   117 		module:send(presence_stanza);
   218 		core_post_stanza(hosts[module.host], presence_stanza);
   118 	elseif (event.stanza.attr.type == "probe") then
   219 	elseif (event.stanza.attr.type == "probe") then
   119 		send_presence = true;
   220 		send_presence = true;
   120 	elseif (event.stanza.attr.type == "unsubscribe") then
   221 	elseif (event.stanza.attr.type == "unsubscribe") then
   121 		-- For information only...
   222 		-- For information only...
   122 		module:log("info", "Unsubscription request from %s", userjid);
   223 		module:log("info", "Unsubscription request from %s", userjid);
   123 	end
   224 	end
   124 
   225 
   125 	if (send_presence == true) then
   226 	if (send_presence == true) then
   126 		-- Send a presence stanza
   227 		-- Send a presence stanza
   127 		module:send(st.presence({ from = host, to = userjid }));
   228 		local presence_stanza = st.presence({ from = host,
       
   229 						      to = userjid });
       
   230 		core_post_stanza(hosts[module.host], presence_stanza);
   128 	end
   231 	end
   129 	return true;
   232 	return true;
   130 end
   233 end
   131 
   234 
   132 module:hook("message/bare", on_message);
   235 module:hook("message/bare", on_message);