]> git.sesse.net Git - vlc/blob - modules/lua/intf.c
ade62d30d49127502f9cdd9292403b65a4940859
[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     vlclua_set_intf( L, p_sys );
242
243     luaL_openlibs( L );
244
245     /* register our functions */
246     luaL_register( L, "vlc", p_reg );
247
248     /* register submodules */
249     luaopen_config( L );
250     luaopen_volume( L );
251     luaopen_httpd( L );
252     luaopen_input( L );
253     luaopen_msg( L );
254     luaopen_misc( L );
255     luaopen_net( L );
256     luaopen_object( L );
257     luaopen_osd( L );
258     luaopen_playlist( L );
259     luaopen_sd( L );
260     luaopen_stream( L );
261     luaopen_strings( L );
262     luaopen_variables( L );
263     luaopen_video( L );
264     luaopen_vlm( L );
265     luaopen_volume( L );
266     luaopen_gettext( L );
267     luaopen_xml( L );
268     luaopen_equalizer( L );
269 #if defined(_WIN32) && !VLC_WINSTORE_APP
270     luaopen_win( L );
271 #endif
272
273     /* clean up */
274     lua_pop( L, 1 );
275
276     /* Setup the module search path */
277     if( vlclua_add_modules_path( L, p_sys->psz_filename ) )
278     {
279         msg_Warn( p_intf, "Error while setting the module search path for %s",
280                   p_sys->psz_filename );
281         lua_close( L );
282         goto error;
283     }
284
285     /*
286      * Get the lua-config string.
287      * If the string is empty, try with the old http-* or telnet-* options
288      * and build the right configuration line
289      */
290     bool b_config_set = false;
291     char *psz_config = var_InheritString( p_intf, "lua-config" );
292     if( !psz_config )
293         psz_config = MakeConfig( p_intf, name );
294
295     if( psz_config )
296     {
297         char *psz_buffer;
298         if( asprintf( &psz_buffer, "config={%s}", psz_config ) != -1 )
299         {
300             char *psz_log = StripPasswords( psz_buffer );
301             if( psz_log != NULL )
302             {
303                 msg_Dbg( p_intf, "Setting config variable: %s", psz_log );
304                 free( psz_log );
305             }
306
307             if( luaL_dostring( L, psz_buffer ) == 1 )
308                 msg_Err( p_intf, "Error while parsing \"lua-config\"." );
309             free( psz_buffer );
310             lua_getglobal( L, "config" );
311             if( lua_istable( L, -1 ) )
312             {
313                 if( !strcmp( name, "cli" ) )
314                 {
315                     lua_getfield( L, -1, "rc" );
316                     if( lua_istable( L, -1 ) )
317                     {
318                         /* msg_Warn( p_intf, "The `rc' lua interface script "
319                                           "was renamed `cli', please update "
320                                           "your configuration!" ); */
321                         lua_setfield( L, -2, "cli" );
322                     }
323                     else
324                         lua_pop( L, 1 );
325                 }
326                 lua_getfield( L, -1, name );
327                 if( lua_istable( L, -1 ) )
328                 {
329                     lua_setglobal( L, "config" );
330                     b_config_set = true;
331                 }
332             }
333         }
334         free( psz_config );
335     }
336
337     if( !b_config_set )
338     {
339         lua_newtable( L );
340         lua_setglobal( L, "config" );
341     }
342
343     /* Wrapper for legacy telnet config */
344     if ( !strcmp( name, "telnet" ) )
345     {
346         /* msg_Warn( p_intf, "The `telnet' lua interface script was replaced "
347                           "by `cli', please update your configuration!" ); */
348
349         char *wrapped_file = vlclua_find_file( "intf", "cli" );
350         if( !wrapped_file )
351         {
352             msg_Err( p_intf, "Couldn't find lua interface script \"cli\", "
353                              "needed by telnet wrapper" );
354             lua_close( p_sys->L );
355             goto error;
356         }
357         lua_pushstring( L, wrapped_file );
358         lua_setglobal( L, "wrapped_file" );
359         free( wrapped_file );
360     }
361
362     p_sys->L = L;
363
364     if( vlc_clone( &p_sys->thread, Run, p_intf, VLC_THREAD_PRIORITY_LOW ) )
365     {
366         lua_close( p_sys->L );
367         goto error;
368     }
369
370     return VLC_SUCCESS;
371 error:
372     free( p_sys->psz_filename );
373     free( p_sys );
374     free( p_intf->psz_header );
375     p_intf->psz_header = NULL;
376     return VLC_EGENERIC;
377 }
378
379 void Close_LuaIntf( vlc_object_t *p_this )
380 {
381     intf_thread_t *p_intf = (intf_thread_t*)p_this;
382     intf_sys_t *p_sys = p_intf->p_sys;
383
384     vlc_join( p_sys->thread, NULL );
385     lua_close( p_sys->L );
386
387     free( p_sys->psz_filename );
388     free( p_sys );
389 }
390
391 static void *Run( void *data )
392 {
393     intf_thread_t *p_intf = data;
394     intf_sys_t *p_sys = p_intf->p_sys;
395     lua_State *L = p_sys->L;
396
397     if( luaL_dofile( L, p_sys->psz_filename ) )
398     {
399         msg_Err( p_intf, "Error loading script %s: %s", p_sys->psz_filename,
400                  lua_tostring( L, lua_gettop( L ) ) );
401         lua_pop( L, 1 );
402     }
403     return NULL;
404 }
405
406 int Open_LuaIntf( vlc_object_t *p_this )
407 {
408     return Start_LuaIntf( p_this, NULL );
409 }
410
411 int Open_LuaHTTP( vlc_object_t *p_this )
412 {
413     return Start_LuaIntf( p_this, "http" );
414 }
415
416 int Open_LuaCLI( vlc_object_t *p_this )
417 {
418     return Start_LuaIntf( p_this, "cli" );
419 }
420
421 int Open_LuaTelnet( vlc_object_t *p_this )
422 {
423     char *pw = var_CreateGetNonEmptyString( p_this, "telnet-password" );
424     if( pw == NULL )
425     {
426         msg_Err( p_this, "password not configured" );
427         msg_Info( p_this, "Please specify the password in the preferences." );
428         return VLC_EGENERIC;
429     }
430     free( pw );
431     return Start_LuaIntf( p_this, "telnet" );
432 }