]> git.sesse.net Git - vlc/blob - modules/lua/intf.c
Lua: remove write-only set_intf()
[vlc] / modules / lua / intf.c
1 /*****************************************************************************
2  * intf.c: Generic lua interface functions
3  *****************************************************************************
4  * Copyright (C) 2007-2008 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Antoine Cellerier <dionoea at videolan tod 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  * Preamble
26  *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <sys/types.h>
32 #include <sys/stat.h>
33
34 #include <vlc_common.h>
35 #include <vlc_interface.h>
36
37 #include "vlc.h"
38 #include "libs.h"
39
40 /*****************************************************************************
41  * Prototypes
42  *****************************************************************************/
43 static void *Run( void * );
44
45 static const char * const ppsz_intf_options[] = { "intf", "config", NULL };
46
47 /*****************************************************************************
48  *
49  *****************************************************************************/
50 static inline void luaL_register_submodule( lua_State *L, const char *psz_name,
51                                             const luaL_Reg *l )
52 {
53     lua_newtable( L );
54     luaL_register( L, NULL, l );
55     lua_setfield( L, -2, psz_name );
56 }
57
58 static char *MakeConfig( intf_thread_t *p_intf, const char *name )
59 {
60     char *psz_config = NULL;
61
62     if( !strcmp( name, "http" ) )
63     {
64         char *psz_http_src = var_InheritString( p_intf, "http-src" );
65         bool b_http_index = var_InheritBool( p_intf, "http-index" );
66         if( psz_http_src )
67         {
68             char *psz_esc = config_StringEscape( psz_http_src );
69
70             if( asprintf( &psz_config, "http={dir='%s',no_index=%s}", psz_esc,
71                           b_http_index ? "true" : "false" ) == -1 )
72                 psz_config = NULL;
73             free( psz_esc );
74             free( psz_http_src );
75         }
76         else
77         {
78             if( asprintf( &psz_config, "http={no_index=%s}",
79                           b_http_index ? "true" : "false" ) == -1 )
80                 psz_config = NULL;
81         }
82     }
83     else if( !strcmp( name, "telnet" ) )
84     {
85         char *psz_host = var_InheritString( p_intf, "telnet-host" );
86         if( !strcmp( psz_host, "*console" ) )
87             ;
88         else
89         {
90             vlc_url_t url;
91             vlc_UrlParse( &url, psz_host, 0 );
92             unsigned i_port = var_InheritInteger( p_intf, "telnet-port" );
93             if ( url.i_port != 0 )
94             {
95                 if ( i_port == TELNETPORT_DEFAULT )
96                     i_port = url.i_port;
97                 else if ( url.i_port != i_port )
98                     msg_Warn( p_intf, "ignoring port %d (using %d)",
99                               url.i_port, i_port );
100             }
101
102             char *psz_esc_host = config_StringEscape( url.psz_host );
103             free( psz_host );
104             vlc_UrlClean( &url );
105
106             if( asprintf( &psz_host, "telnet://%s:%d",
107                           psz_esc_host ? psz_esc_host : "", i_port ) == -1 )
108                 psz_host = NULL;
109             free( psz_esc_host );
110         }
111
112         char *psz_passwd = var_InheritString( p_intf, "telnet-password" );
113
114         char *psz_esc_passwd = config_StringEscape( psz_passwd );
115
116         if( asprintf( &psz_config, "telnet={host='%s',password='%s'}",
117                       psz_host, psz_esc_passwd ) == -1 )
118             psz_config = NULL;
119
120         free( psz_esc_passwd );
121         free( psz_passwd );
122         free( psz_host );
123     }
124     else if( !strcmp( name, "cli" ) )
125     {
126         char *psz_rc_host = var_InheritString( p_intf, "rc-host" );
127         if( !psz_rc_host )
128             psz_rc_host = var_InheritString( p_intf, "cli-host" );
129         if( psz_rc_host )
130         {
131             char *psz_esc_host = config_StringEscape( psz_rc_host );
132
133             if( asprintf( &psz_config, "cli={host='%s'}", psz_esc_host ) == -1 )
134                 psz_config = NULL;
135             free( psz_esc_host );
136             free( psz_rc_host );
137         }
138     }
139
140     return psz_config;
141 }
142
143 static char *StripPasswords( const char *psz_config )
144 {
145     unsigned n = 0;
146     const char *p = psz_config;
147     while ((p = strstr(p, "password=")) != NULL)
148     {
149         n++;
150         p++;
151     }
152     if (n == 0)
153         return strdup(psz_config);
154  
155     char *psz_log = malloc(strlen(psz_config) + n * strlen("******") + 1);
156     if (psz_log == NULL)
157         return NULL;
158     psz_log[0] = '\0';
159
160     for (p = psz_config; ; )
161     {
162         const char *pwd = strstr(p, "password=");
163         if (pwd == NULL)
164         {
165             /* Copy the last, ending bit */
166             strcat(psz_log, p);
167             break;
168         }
169         pwd += strlen("password=");
170
171         char delim[3] = ",}";
172         if (*pwd == '\'' || *pwd == '"')
173         {
174             delim[0] = *pwd++;
175             delim[1] = '\0';
176         }
177
178         strncat(psz_log, p, pwd - p);
179         strcat(psz_log, "******");
180
181         /* Advance to the delimiter at the end of the password */
182         p = pwd - 1;
183         do
184         {
185             p = strpbrk(p + 1, delim);
186             if (p == NULL)
187                 /* Oops, unbalanced quotes or brackets */
188                 return psz_log;
189         }
190         while (*(p - 1) == '\\');
191     }
192     return psz_log;
193 }
194
195 static const luaL_Reg p_reg[] = { { NULL, NULL } };
196
197 static int Start_LuaIntf( vlc_object_t *p_this, const char *name )
198 {
199     intf_thread_t *p_intf = (intf_thread_t*)p_this;
200     intf_sys_t *p_sys;
201     lua_State *L;
202
203     config_ChainParse( p_intf, "lua-", ppsz_intf_options, p_intf->p_cfg );
204
205     if( name == NULL )
206     {
207         char *n = var_InheritString( p_this, "lua-intf" );
208         if( unlikely(n == NULL) )
209             return VLC_EGENERIC;
210         name = p_intf->psz_header = n;
211     }
212     else
213         /* Cleaned up by vlc_object_release() */
214         p_intf->psz_header = strdup( name );
215
216     p_intf->p_sys = (intf_sys_t*)malloc( sizeof(intf_sys_t) );
217     if( !p_intf->p_sys )
218     {
219         free( p_intf->psz_header );
220         p_intf->psz_header = NULL;
221         return VLC_ENOMEM;
222     }
223     p_sys = p_intf->p_sys;
224     p_sys->psz_filename = vlclua_find_file( "intf", name );
225     if( !p_sys->psz_filename )
226     {
227         msg_Err( p_intf, "Couldn't find lua interface script \"%s\".",
228                  name );
229         goto error;
230     }
231     msg_Dbg( p_intf, "Found lua interface script: %s", p_sys->psz_filename );
232
233     L = luaL_newstate();
234     if( !L )
235     {
236         msg_Err( p_intf, "Could not create new Lua State" );
237         goto error;
238     }
239
240     vlclua_set_this( L, p_intf );
241
242     luaL_openlibs( L );
243
244     /* register our functions */
245     luaL_register( L, "vlc", p_reg );
246
247     /* register submodules */
248     luaopen_config( L );
249     luaopen_volume( L );
250     luaopen_httpd( L );
251     luaopen_input( L );
252     luaopen_msg( L );
253     luaopen_misc( L );
254     luaopen_net( L );
255     luaopen_object( L );
256     luaopen_osd( L );
257     luaopen_playlist( L );
258     luaopen_sd( L );
259     luaopen_stream( L );
260     luaopen_strings( L );
261     luaopen_variables( L );
262     luaopen_video( L );
263     luaopen_vlm( L );
264     luaopen_volume( L );
265     luaopen_gettext( L );
266     luaopen_xml( L );
267     luaopen_equalizer( L );
268 #if defined(_WIN32) && !VLC_WINSTORE_APP
269     luaopen_win( L );
270 #endif
271
272     /* clean up */
273     lua_pop( L, 1 );
274
275     /* Setup the module search path */
276     if( vlclua_add_modules_path( L, p_sys->psz_filename ) )
277     {
278         msg_Warn( p_intf, "Error while setting the module search path for %s",
279                   p_sys->psz_filename );
280         lua_close( L );
281         goto error;
282     }
283
284     /*
285      * Get the lua-config string.
286      * If the string is empty, try with the old http-* or telnet-* options
287      * and build the right configuration line
288      */
289     bool b_config_set = false;
290     char *psz_config = var_InheritString( p_intf, "lua-config" );
291     if( !psz_config )
292         psz_config = MakeConfig( p_intf, name );
293
294     if( psz_config )
295     {
296         char *psz_buffer;
297         if( asprintf( &psz_buffer, "config={%s}", psz_config ) != -1 )
298         {
299             char *psz_log = StripPasswords( psz_buffer );
300             if( psz_log != NULL )
301             {
302                 msg_Dbg( p_intf, "Setting config variable: %s", psz_log );
303                 free( psz_log );
304             }
305
306             if( luaL_dostring( L, psz_buffer ) == 1 )
307                 msg_Err( p_intf, "Error while parsing \"lua-config\"." );
308             free( psz_buffer );
309             lua_getglobal( L, "config" );
310             if( lua_istable( L, -1 ) )
311             {
312                 if( !strcmp( name, "cli" ) )
313                 {
314                     lua_getfield( L, -1, "rc" );
315                     if( lua_istable( L, -1 ) )
316                     {
317                         /* msg_Warn( p_intf, "The `rc' lua interface script "
318                                           "was renamed `cli', please update "
319                                           "your configuration!" ); */
320                         lua_setfield( L, -2, "cli" );
321                     }
322                     else
323                         lua_pop( L, 1 );
324                 }
325                 lua_getfield( L, -1, name );
326                 if( lua_istable( L, -1 ) )
327                 {
328                     lua_setglobal( L, "config" );
329                     b_config_set = true;
330                 }
331             }
332         }
333         free( psz_config );
334     }
335
336     if( !b_config_set )
337     {
338         lua_newtable( L );
339         lua_setglobal( L, "config" );
340     }
341
342     /* Wrapper for legacy telnet config */
343     if ( !strcmp( name, "telnet" ) )
344     {
345         /* msg_Warn( p_intf, "The `telnet' lua interface script was replaced "
346                           "by `cli', please update your configuration!" ); */
347
348         char *wrapped_file = vlclua_find_file( "intf", "cli" );
349         if( !wrapped_file )
350         {
351             msg_Err( p_intf, "Couldn't find lua interface script \"cli\", "
352                              "needed by telnet wrapper" );
353             lua_close( p_sys->L );
354             goto error;
355         }
356         lua_pushstring( L, wrapped_file );
357         lua_setglobal( L, "wrapped_file" );
358         free( wrapped_file );
359     }
360
361     p_sys->L = L;
362
363     if( vlc_clone( &p_sys->thread, Run, p_intf, VLC_THREAD_PRIORITY_LOW ) )
364     {
365         lua_close( p_sys->L );
366         goto error;
367     }
368
369     return VLC_SUCCESS;
370 error:
371     free( p_sys->psz_filename );
372     free( p_sys );
373     free( p_intf->psz_header );
374     p_intf->psz_header = NULL;
375     return VLC_EGENERIC;
376 }
377
378 void Close_LuaIntf( vlc_object_t *p_this )
379 {
380     intf_thread_t *p_intf = (intf_thread_t*)p_this;
381     intf_sys_t *p_sys = p_intf->p_sys;
382
383     vlc_join( p_sys->thread, NULL );
384     lua_close( p_sys->L );
385
386     free( p_sys->psz_filename );
387     free( p_sys );
388 }
389
390 static void *Run( void *data )
391 {
392     intf_thread_t *p_intf = data;
393     intf_sys_t *p_sys = p_intf->p_sys;
394     lua_State *L = p_sys->L;
395
396     if( luaL_dofile( L, p_sys->psz_filename ) )
397     {
398         msg_Err( p_intf, "Error loading script %s: %s", p_sys->psz_filename,
399                  lua_tostring( L, lua_gettop( L ) ) );
400         lua_pop( L, 1 );
401     }
402     return NULL;
403 }
404
405 int Open_LuaIntf( vlc_object_t *p_this )
406 {
407     return Start_LuaIntf( p_this, NULL );
408 }
409
410 int Open_LuaHTTP( vlc_object_t *p_this )
411 {
412     return Start_LuaIntf( p_this, "http" );
413 }
414
415 int Open_LuaCLI( vlc_object_t *p_this )
416 {
417     return Start_LuaIntf( p_this, "cli" );
418 }
419
420 int Open_LuaTelnet( vlc_object_t *p_this )
421 {
422     char *pw = var_CreateGetNonEmptyString( p_this, "telnet-password" );
423     if( pw == NULL )
424     {
425         msg_Err( p_this, "password not configured" );
426         msg_Info( p_this, "Please specify the password in the preferences." );
427         return VLC_EGENERIC;
428     }
429     free( pw );
430     return Start_LuaIntf( p_this, "telnet" );
431 }