# HG changeset patch # User Mikael Berthe # Date 1422211959 -3600 # Node ID 620cc035ae1e414dea68389f34ca5e5d72d384e9 # Parent d9f3c66ea9388e831d82a94b01b3b42de000d0e4 mod_admin_message: New IM-based administration console This module provides a console over XMPP. All the commands of the mod_admin_telnet module are available to Prosody admins from an XMPP client. diff -r d9f3c66ea938 -r 620cc035ae1e mod_admin_message/mod_admin_message.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_admin_message/mod_admin_message.lua Sun Jan 25 19:52:39 2015 +0100 @@ -0,0 +1,135 @@ +-- Prosody IM +-- +-- mod_admin_message -- Console-over-XMPP implementation. +-- +-- This module depends on Prosody's admin_telnet module +-- +-- Copyright (C) 2008-2010 Matthew Wild +-- Copyright (C) 2008-2010 Waqas Hussain +-- Copyright (C) 2012-2013 Mikael Berthe +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +local st = require "util.stanza"; +local um_is_admin = require "core.usermanager".is_admin; + +local admin_telnet = module:depends("admin_telnet"); +local telnet_def_env = module:shared("/*/admin_telnet/env"); +local telnet_commands = module:shared("/*/admin_telnet/commands"); +local default_env_mt = { __index = telnet_def_env }; + +local host = module.host; + +-- Create our own session. print() will store the results in a text +-- string. send(), quit(), disconnect() are no-op. +local function new_session () + local session = { + send = function () end; + quit = function () end; + disconnect = function () end; + }; + + session.print = function (...) + local t = {}; + for i=1,select("#", ...) do + t[i] = tostring(select(i, ...)); + end + local text = "| "..table.concat(t, "\t"); + if session.fulltext then + session.fulltext = session.fulltext .. "\n" .. text; + else + session.fulltext = text; + end + end + + session.env = setmetatable({}, default_env_mt); + + -- Load up environment with helper objects + for name, t in pairs(telnet_def_env) do + if type(t) == "table" then + session.env[name] = setmetatable({ session = session }, + { __index = t }); + end + end + + return session; +end + +local function on_message(event) + -- Check the type of the incoming stanza to avoid loops: + if event.stanza.attr.type == "error" then + return; -- We do not want to reply to these, so leave. + end + + local userjid = event.stanza.attr.from; + local bodytag = event.stanza:get_child("body"); + local body = bodytag and bodytag:get_text() or ""; + if not body or body == "" then + -- We do not reply to empty messages (chatstates, etc.) + return true; + end + + -- Check the requester is an admin user + if not um_is_admin(userjid, module.host) then + module:log("info", "Ignored request from non-admin: %s", + userjid); + return; + end + + -- Create a session in order to use an admin_telnet-like environment + local session = new_session(); + + -- Process the message using admin_telnet's onincoming function + admin_telnet.console:process_line(session, body.."\n"); + + -- Strip trailing blank line + session.fulltext = tostring(session.fulltext):gsub("\n\|%s*$", "") + + -- Send the reply stanza + local reply_stanza = st.message({ from = host, to = userjid, + type = "chat" }); + reply_stanza = reply_stanza:body(session.fulltext); + module:send(reply_stanza); + + return true; +end + +local function on_presence(event) + + local send_presence = false; + + local userjid = event.stanza.attr.from; + + -- Check the requester is an admin user + if not um_is_admin(userjid, module.host) then + module:log("info", "Ignored presence from non-admin: %s", + userjid); + return; + end + + if (event.stanza.attr.type == "subscribe") then + module:log("info", "Subscription request from %s", userjid); + send_presence = true; + -- Send a subscription ack + local presence_stanza = st.presence({ from = host, + to = userjid, type = "subscribed", + id = event.stanza.attr.id }); + module:send(presence_stanza); + elseif (event.stanza.attr.type == "probe") then + send_presence = true; + elseif (event.stanza.attr.type == "unsubscribe") then + -- For information only... + module:log("info", "Unsubscription request from %s", userjid); + end + + if (send_presence == true) then + -- Send a presence stanza + module:send(st.presence({ from = host, to = userjid })); + end + return true; +end + +module:hook("message/bare", on_message); +module:hook("presence/bare", on_presence);