]> git.sesse.net Git - vlc/blob - share/luaintf/telnet.lua
Add a new type of VLC Lua module: Interfaces.
[vlc] / share / luaintf / 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 --[==========================================================================[
25  Copy (features wise) of the original VLC modules/control/telnet.c module.
26
27  Differences are:
28     * it's in Lua
29     * 'lock' command to lock the telnet promt
30     * possibility to listen on different hosts including stdin
31       for example:
32         listen on stdin: vlc -I lua --lua-intf telnet --lua-config "telnet={host='*console'}"
33         listen on stdin + 2 ports on localhost: vlc -I lua --lua-intf telnet --lua-config "telnet={hosts={'localhost:4212','localhost:5678','*console'}}"
34 --]==========================================================================]
35
36 require "host"
37
38 --[[ Some telnet command special characters ]]
39 WILL = "\251" -- Indicates the desire to begin performing, or confirmation that you are now performing, the indicated option.
40 WONT = "\252" -- Indicates the refusal to perform, or continue performing, the indicated option.
41 DO   = "\253" -- Indicates the request that the other party perform, or confirmation that you are expecting the other party to perform, the indicated option.
42 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.
43 IAC  = "\255" -- Interpret as command
44
45 ECHO = "\001"
46
47 --[[ Client status change callbacks ]]
48 function on_password( client )
49     if client.type == host.client_type.net then
50         client:send( "Password: " ..IAC..WILL..ECHO )
51     else
52         -- no authentification needed on stdin
53         client:switch_status( host.status.read )
54     end
55 end
56 function on_read( client )
57     client:send( "> " )
58 end
59 function on_write( client )
60 end
61
62 --[[ Misc functions ]]
63 function telnet_commands( client )
64     -- remove telnet command replies from the client's data
65     client.buffer = string.gsub( client.buffer, IAC.."["..DO..DONT..WILL..WONT.."].", "" )
66 end
67
68 function vlm_message_to_string(client,message,prefix)
69     local prefix = prefix or ""
70     if message.value then
71         client:append(prefix .. message.name .. " : " .. message.value)
72         return
73     else
74         client:append(prefix .. message.name)
75         if message.children then
76             for i,c in ipairs(message.children) do
77                 vlm_message_to_string(client,c,prefix.."    ")
78             end
79         end
80         return
81     end
82 end
83
84 --[[ Configure the host ]]
85 h = host.host()
86
87 h.status_callbacks[host.status.password] = on_password
88 h.status_callbacks[host.status.read] = on_read
89 h.status_callbacks[host.status.write] = on_write
90
91 h:listen( config.hosts or config.host or "localhost:4212" )
92 password = config.password or "admin"
93
94 --[[ Launch vlm ]]
95 vlm = vlc.vlm.new()
96
97 --[[ Commands ]]
98 function shutdown(client)
99     h:broadcast("Shutting down.\r\n")
100     vlc.msg.err("shutdown requested")
101     vlc.quit()
102     return true
103 end
104 function logout(client)
105     client:del()
106     return true
107 end
108 function quit(client)
109     if client.type == host.client_type.net then
110         return logout(client)
111     else
112         return shutdown(client)
113     end
114 end
115 function lock(client)
116     client:send("\r\n")
117     client:switch_status( host.status.password )
118     client.buffer = ""
119     return false
120 end
121 function help(client)
122     client:append("    Telnet Specific Commands:")
123     for c,t in pairs(commands) do
124         client:append("        "..c.." : "..t.help)
125     end
126     return true
127 end
128 commands = {
129     ["shutdown"] = { func = shutdown, help = "shutdown VLC" },
130     ["quit"]     = { func = quit, help = "logout from telnet/shutdown VLC from local shell" },
131     ["logout"]   = { func = logout, help = "logout" },
132     ["lock"]     = { func = lock, help = "lock the telnet prompt" },
133     ["help"]     = { func = help, help = "show this help", dovlm = true },
134     }
135
136 function client_command( client )
137     local cmd = client.buffer
138     client.buffer = ""
139     if not commands[cmd] or not commands[cmd].func or commands[cmd].dovlm then
140         -- if it's not an interface specific command, it has to be a VLM command
141         message = vlc.vlm.execute_command( vlm, cmd )
142         vlm_message_to_string( client, message )
143         if not commands[cmd] or not commands[cmd].func and not commands[cmd].dovlm then
144             return true
145         end
146     end
147     ok, msg = pcall( commands[cmd].func, client )
148     if not ok then
149         client:append( "Error in `"..cmd.."' "..msg )
150         return true
151     end
152     return msg
153 end
154
155 --[[ The main loop ]]
156 while not vlc.should_die() do
157     h:accept()
158     local w, r = h:select( 0.1 )
159
160     -- Handle writes
161     for _, client in pairs(w) do
162         local len = client:send()
163         client.buffer = string.sub(client.buffer,len+1)
164         if client.buffer == "" then client:switch_status( host.status.read ) end
165     end
166
167     -- Handle reads
168     for _, client in pairs(r) do
169         local str = client:recv(1000)
170         local done = false
171         if string.match(str,"\n$") then
172             client.buffer = string.gsub(client.buffer..str,"\r?\n$","")
173             done = true
174         elseif client.buffer == ""
175            and ((client.type == host.client_type.stdio and str == "")
176            or  (client.type == host.client_type.net and str == "\004")) then
177             -- Caught a ^D
178             client.buffer = "quit"
179             done = true
180         else
181             client.buffer = client.buffer .. str
182         end
183         if client.type == host.client_type.net then
184             telnet_commands( client )
185         end
186         if done then
187             if client.status == host.status.password then
188                 if client.buffer == password then
189                     client:send( IAC..WONT..ECHO.."\r\nWelcome, Master\r\n" )
190                     client.buffer = ""
191                     client:switch_status( host.status.write )
192                 else
193                     client:send( "\r\nWrong password\r\nPassword: " )
194                     client.buffer = ""
195                 end
196             elseif client_command( client ) then
197                 client:switch_status( host.status.write )
198             end
199         end
200     end
201 end
202
203 --[[ Clean up ]]
204 vlc.vlm.delete( vlm )