1-- Prosody IM
2--
3-- mod_admin_message -- Console-over-XMPP implementation.
4--
5-- This module depends on Prosody's admin_telnet module
6--
7-- Copyright (C) 2008-2010 Matthew Wild
8-- Copyright (C) 2008-2010 Waqas Hussain
9-- Copyright (C) 2012-2013 Mikael Berthe
10--
11-- This project is MIT/X11 licensed. Please see the
12-- COPYING file in the source package for more information.
13--
14
15local st = require "util.stanza";
16local um_is_admin = require "core.usermanager".is_admin;
17
18local admin_telnet = module:depends("admin_telnet");
19local telnet_def_env = module:shared("/*/admin_telnet/env");
20local telnet_commands = module:shared("/*/admin_telnet/commands");
21local default_env_mt = { __index = telnet_def_env };
22
23local host = module.host;
24
25-- Create our own session.  print() will store the results in a text
26-- string.  send(), quit(), disconnect() are no-op.
27local function new_session ()
28	local session = {
29			send        = function ()  end;
30			quit        = function ()  end;
31			disconnect  = function ()  end;
32			};
33
34	session.print = function (...)
35		local t = {};
36		for i=1,select("#", ...) do
37			t[i] = tostring(select(i, ...));
38		end
39		local text = "| "..table.concat(t, "\t");
40		if session.fulltext then
41		    session.fulltext = session.fulltext .. "\n" .. text;
42		else
43		    session.fulltext = text;
44		end
45	end
46
47	session.env = setmetatable({}, default_env_mt);
48
49	-- Load up environment with helper objects
50	for name, t in pairs(telnet_def_env) do
51		if type(t) == "table" then
52			session.env[name] = setmetatable({ session = session },
53							 { __index = t });
54		end
55	end
56
57	return session;
58end
59
60local function on_message(event)
61	-- Check the type of the incoming stanza to avoid loops:
62	if event.stanza.attr.type == "error" then
63		return; -- We do not want to reply to these, so leave.
64	end
65
66	local userjid = event.stanza.attr.from;
67	local bodytag = event.stanza:get_child("body");
68	local body = bodytag and bodytag:get_text() or "";
69	if not body or body == "" then
70		-- We do not reply to empty messages (chatstates, etc.)
71		return true;
72	end
73
74	-- Check the requester is an admin user
75	if not um_is_admin(userjid, module.host) then
76		module:log("info", "Ignored request from non-admin: %s",
77			   userjid);
78		return;
79	end
80
81	-- Create a session in order to use an admin_telnet-like environment
82	local session = new_session();
83
84	-- Process the message using admin_telnet's onincoming function
85	admin_telnet.console:process_line(session, body.."\n");
86
87	-- Strip trailing blank line
88	session.fulltext = tostring(session.fulltext):gsub("\n|%s*$", "")
89
90	-- Send the reply stanza
91	local reply_stanza = st.message({ from = host, to = userjid,
92					type = "chat" }, session.fulltext);
93	module:send(reply_stanza);
94
95	return true;
96end
97
98local function on_presence(event)
99
100	local send_presence = false;
101
102	local userjid = event.stanza.attr.from;
103
104	-- Check the requester is an admin user
105	if not um_is_admin(userjid, module.host) then
106		module:log("info", "Ignored presence from non-admin: %s",
107			   userjid);
108		return;
109	end
110
111	if (event.stanza.attr.type == "subscribe") then
112		module:log("info", "Subscription request from %s", userjid);
113		send_presence = true;
114		-- Send a subscription ack
115		local presence_stanza = st.presence({ from = host,
116					to = userjid, type = "subscribed",
117					id = event.stanza.attr.id });
118		module:send(presence_stanza);
119	elseif (event.stanza.attr.type == "probe") then
120		send_presence = true;
121	elseif (event.stanza.attr.type == "unsubscribe") then
122		-- For information only...
123		module:log("info", "Unsubscription request from %s", userjid);
124	end
125
126	if (send_presence == true) then
127		-- Send a presence stanza
128		module:send(st.presence({ from = host, to = userjid }));
129	end
130	return true;
131end
132
133module:hook("message/bare", on_message);
134module:hook("presence/bare", on_presence);
135