--[==========================================================================[ telnet.lua: VLM interface plugin --[==========================================================================[ Copyright (C) 2007 the VideoLAN team $Id$ Authors: Antoine Cellerier This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. --]==========================================================================] description= [============================================================================[ VLM Interface plugin Copy (features wise) of the original VLC modules/control/telnet.c module. Differences are: * it's in Lua * 'lock' command to lock the telnet promt * possibility to listen on different hosts including stdin for example: listen on stdin: vlc -I lua --lua-intf telnet --lua-config "telnet={host='*console'}" listen on stdin + 2 ports on localhost: vlc -I lua --lua-intf telnet --lua-config "telnet={hosts={'localhost:4212','localhost:5678','*console'}}" Configuration options setable throught the --lua-config option are: * hosts: A list of hosts to listen on (see examples above). * host: A host to listen on. (won't be used if `hosts' is set) * password: The password used for remote clients. * prompt: The prompt. ]============================================================================] require "host" --[[ Some telnet command special characters ]] WILL = "\251" -- Indicates the desire to begin performing, or confirmation that you are now performing, the indicated option. WONT = "\252" -- Indicates the refusal to perform, or continue performing, the indicated option. DO = "\253" -- Indicates the request that the other party perform, or confirmation that you are expecting the other party to perform, the indicated option. DONT = "\254" -- Indicates the demand that the other party stop performing, or confirmation that you are no longer expecting the other party to perform, the indicated option. IAC = "\255" -- Interpret as command ECHO = "\001" --[[ Client status change callbacks ]] function on_password( client ) if client.type == host.client_type.net then client:send( "Password: " ..IAC..WILL..ECHO ) else -- no authentication needed on stdin client:switch_status( host.status.read ) end end function on_read( client ) client:send( config.prompt and tostring(config.prompt) or "> " ) end function on_write( client ) end --[[ Misc functions ]] function telnet_commands( client ) -- remove telnet command replies from the client's data client.buffer = string.gsub( client.buffer, IAC.."["..DO..DONT..WILL..WONT.."].", "" ) end function vlm_message_to_string(client,message,prefix) local prefix = prefix or "" if message.value then client:append(prefix .. message.name .. " : " .. message.value) return else client:append(prefix .. message.name) if message.children then for i,c in ipairs(message.children) do vlm_message_to_string(client,c,prefix.." ") end end return end end --[[ Configure the host ]] h = host.host() h.status_callbacks[host.status.password] = on_password h.status_callbacks[host.status.read] = on_read h.status_callbacks[host.status.write] = on_write h:listen( config.hosts or config.host or "localhost:4212" ) password = config.password or "admin" --[[ Launch vlm ]] vlm = vlc.vlm() --[[ Commands ]] function shutdown(client) h:broadcast("Shutting down.\r\n") vlc.msg.err("shutdown requested") vlc.misc.quit() return true end function logout(client) client:del() return true end function quit(client) if client.type == host.client_type.net then return logout(client) else return shutdown(client) end end function lock(client) client:send("\r\n") client:switch_status( host.status.password ) client.buffer = "" return false end function print_text(text) return function(client) client:append(string.gsub(text,"\r?\n","\r\n")) return true end end function help(client) client:append(" Telnet Specific Commands:") for c,t in pairs(commands) do client:append(" "..c.." : "..t.help) end return true end commands = { ["shutdown"] = { func = shutdown, help = "shutdown VLC" }, ["quit"] = { func = quit, help = "logout from telnet/shutdown VLC from local shell" }, ["logout"] = { func = logout, help = "logout" }, ["lock"] = { func = lock, help = "lock the telnet prompt" }, ["description"] = { func = print_text(description), help = "describe this module" }, ["license"] = { func = print_text(vlc.misc.license()), help = "print VLC's license message" }, ["help"] = { func = help, help = "show this help", dovlm = true }, } function client_command( client ) local cmd = client.buffer client.buffer = "" if not commands[cmd] or not commands[cmd].func or commands[cmd].dovlm then -- if it's not an interface specific command, it has to be a VLM command local message, vlc_err = vlm:execute_command( cmd ) vlm_message_to_string( client, message ) if not commands[cmd] or not commands[cmd].func and not commands[cmd].dovlm then if vlc_err ~= 0 then client:append( "Type `help' for help." ) end return true end end ok, msg = pcall( commands[cmd].func, client ) if not ok then client:append( "Error in `"..cmd.."' "..msg ) return true end return msg end --[[ The main loop ]] while not vlc.misc.should_die() do local w, r = h:accept_and_select() -- Handle writes for _, client in pairs(w) do local len = client:send() client.buffer = string.sub(client.buffer,len+1) if client.buffer == "" then client:switch_status( host.status.read ) end end -- Handle reads for _, client in pairs(r) do local str = client:recv(1000) local done = false if not str then -- the telnet client program has leave client.buffer = "quit" done = true elseif string.match(str,"\n$") then client.buffer = string.gsub(client.buffer..str,"\r?\n$","") done = true elseif client.buffer == "" and ((client.type == host.client_type.stdio and str == "") or (client.type == host.client_type.net and str == "\004")) then -- Caught a ^D client.buffer = "quit" done = true else client.buffer = client.buffer .. str end if client.type == host.client_type.net then telnet_commands( client ) end if done then if client.status == host.status.password then if client.buffer == password then client:send( IAC..WONT..ECHO.."\r\nWelcome, Master\r\n" ) client.buffer = "" client:switch_status( host.status.write ) else client:send( "\r\nWrong password\r\nPassword: " ) client.buffer = "" end elseif client_command( client ) then client:switch_status( host.status.write ) end end end end --[[ Clean up ]] vlm = nil