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