]> git.sesse.net Git - vlc/blob - share/lua/intf/telnet.lua
Fix lua telnet vlm_message_to_string
[vlc] / share / lua / intf / telnet.lua
1 --[==========================================================================[
2  telnet.lua: VLM interface plugin
3 --[==========================================================================[
4  Copyright (C) 2007 the VideoLAN team
5  $Id$
6
7  Authors: Antoine Cellerier <dionoea at videolan dot org>
8
9  This program is free software; you can redistribute it and/or modify
10  it under the terms of the GNU General Public License as published by
11  the Free Software Foundation; either version 2 of the License, or
12  (at your option) any later version.
13
14  This program is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  GNU General Public License for more details.
18
19  You should have received a copy of the GNU General Public License
20  along with this program; if not, write to the Free Software
21  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 --]==========================================================================]
23
24 description=
25 [============================================================================[
26  VLM Interface plugin
27
28  Copy (features wise) of the original VLC modules/control/telnet.c module.
29
30  Differences are:
31     * it's in Lua
32     * 'lock' command to lock the telnet promt
33     * possibility to listen on different hosts including stdin
34       for example:
35         listen on stdin: vlc -I lua --lua-intf telnet --lua-config "telnet={host='*console'}"
36         listen on stdin + 2 ports on localhost: vlc -I lua --lua-intf telnet --lua-config "telnet={hosts={'localhost:4212','localhost:5678','*console'}}"
37  
38  Configuration options setable throught the --lua-config option are:
39     * hosts: A list of hosts to listen on (see examples above).
40     * host: A host to listen on. (won't be used if `hosts' is set)
41     * password: The password used for remote clients.
42     * prompt: The prompt.
43 ]============================================================================]
44
45 require "host"
46
47 --[[ Some telnet command special characters ]]
48 WILL = "\251" -- Indicates the desire to begin performing, or confirmation that you are now performing, the indicated option.
49 WONT = "\252" -- Indicates the refusal to perform, or continue performing, the indicated option.
50 DO   = "\253" -- Indicates the request that the other party perform, or confirmation that you are expecting the other party to perform, the indicated option.
51 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.
52 IAC  = "\255" -- Interpret as command
53
54 ECHO = "\001"
55
56 --[[ Client status change callbacks ]]
57 function on_password( client )
58     if client.type == host.client_type.net then
59         client:send( "Password: " ..IAC..WILL..ECHO )
60     else
61         -- no authentication needed on stdin
62         client:switch_status( host.status.read )
63     end
64 end
65 function on_read( client )
66     client:send( config.prompt and tostring(config.prompt) or "> " )
67 end
68 function on_write( client )
69 end
70
71 --[[ Misc functions ]]
72 function telnet_commands( client )
73     -- remove telnet command replies from the client's data
74     client.buffer = string.gsub( client.buffer, IAC.."["..DO..DONT..WILL..WONT.."].", "" )
75 end
76
77 function vlm_message_to_string(client,message,prefix)
78     local prefix = prefix or ""
79     if message.value then
80         client:append(prefix .. message.name .. " : " .. message.value)
81     else
82         client:append(prefix .. message.name)
83     end
84     if message.children then
85         for i,c in ipairs(message.children) do
86             vlm_message_to_string(client,c,prefix.."    ")
87         end
88     end
89 end
90
91 --[[ Configure the host ]]
92 h = host.host()
93
94 h.status_callbacks[host.status.password] = on_password
95 h.status_callbacks[host.status.read] = on_read
96 h.status_callbacks[host.status.write] = on_write
97
98 h:listen( config.hosts or config.host or "localhost:4212" )
99 password = config.password or "admin"
100
101 --[[ Launch vlm ]]
102 vlm = vlc.vlm()
103
104 --[[ Commands ]]
105 function shutdown(client)
106     h:broadcast("Shutting down.\r\n")
107     vlc.msg.err("shutdown requested")
108     vlc.misc.quit()
109     return true
110 end
111 function logout(client)
112     client:del()
113     return true
114 end
115 function quit(client)
116     if client.type == host.client_type.net then
117         return logout(client)
118     else
119         return shutdown(client)
120     end
121 end
122 function lock(client)
123     client:send("\r\n")
124     client:switch_status( host.status.password )
125     client.buffer = ""
126     return false
127 end
128 function print_text(text)
129     return function(client)
130         client:append(string.gsub(text,"\r?\n","\r\n"))
131         return true
132     end
133 end
134 function help(client)
135     client:append("    Telnet Specific Commands:")
136     for c,t in pairs(commands) do
137         client:append("        "..c.." : "..t.help)
138     end
139     return true
140 end
141 commands = {
142     ["shutdown"]    = { func = shutdown, help = "shutdown VLC" },
143     ["quit"]        = { func = quit, help = "logout from telnet/shutdown VLC from local shell" },
144     ["logout"]      = { func = logout, help = "logout" },
145     ["lock"]        = { func = lock, help = "lock the telnet prompt" },
146     ["description"] = { func = print_text(description), help = "describe this module" },
147     ["license"]     = { func = print_text(vlc.misc.license()), help = "print VLC's license message" },
148     ["help"]        = { func = help, help = "show this help", dovlm = true },
149     }
150
151 function client_command( client )
152     local cmd = client.buffer
153     client.buffer = ""
154     if not commands[cmd] or not commands[cmd].func or commands[cmd].dovlm then
155         -- if it's not an interface specific command, it has to be a VLM command
156         local message, vlc_err = vlm:execute_command( cmd )
157         vlm_message_to_string( client, message )
158         if not commands[cmd] or not commands[cmd].func and not commands[cmd].dovlm then
159             if vlc_err ~= 0 then client:append( "Type `help' for help." ) end
160             return true
161         end
162     end
163     ok, msg = pcall( commands[cmd].func, client )
164     if not ok then
165         client:append( "Error in `"..cmd.."' "..msg )
166         return true
167     end
168     return msg
169 end
170
171 --[[ The main loop ]]
172 while not vlc.misc.should_die() do
173     local w, r = h:accept_and_select()
174
175     -- Handle writes
176     for _, client in pairs(w) do
177         local len = client:send()
178         client.buffer = string.sub(client.buffer,len+1)
179         if client.buffer == "" then client:switch_status( host.status.read ) end
180     end
181
182     -- Handle reads
183     for _, client in pairs(r) do
184         local str = client.cmds .. string.gsub(client:recv(1000), "\r", "\n")
185
186         if not str then -- the telnet client program has leave
187             client.cmds = "quit"
188         elseif string.match(str,"\n") then
189             client.cmds = str
190         elseif client.buffer == ""
191            and ((client.type == host.client_type.stdio and str == "")
192            or  (client.type == host.client_type.net and str == "\004")) then
193             -- Caught a ^D
194             client.cmds = "quit"
195         end
196         if client.type == host.client_type.net then
197             telnet_commands( client )
198         end
199
200         client.buffer = ""
201         -- split the command at the first '\n'
202         while string.find(client.cmds, "\n") do
203             -- save the buffer to send to the client
204             local saved_buffer = client.buffer
205
206             -- get the next command
207             local index = string.find(client.cmds, "\n")
208             client.buffer = string.gsub(string.sub(client.cmds, 0, index - 1), "^%s*(.-)%s*$", "%1")
209             client.cmds = string.sub(client.cmds, index + 1)
210
211             if client.status == host.status.password then
212                 if client.buffer == password then
213                     client:send( IAC..WONT..ECHO.."\r\nWelcome, Master\r\n" )
214                     client.buffer = ""
215                     client:switch_status( host.status.write )
216                 else
217                     client:send( "\r\nWrong password\r\nPassword: " )
218                     client.buffer = ""
219                 end
220             elseif client_command( client ) then
221                 client:switch_status( host.status.write )
222             end
223             client.buffer = saved_buffer .. client.buffer
224         end
225     end
226 end
227
228 --[[ Clean up ]]
229 vlm = nil