]> git.sesse.net Git - vlc/blob - modules/lua/intf.c
contrib: tremor: fix CFLAGS and factor
[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_host = var_CreateGetNonEmptyString( p_intf, "http-host" );
207             char *psz_http_src = var_CreateGetNonEmptyString( p_intf, "http-src" );
208             bool b_http_index = var_CreateGetBool( p_intf, "http-index" );
209             if( psz_http_host )
210             {
211                 char *psz_esc = config_StringEscape( psz_http_host );
212                 asprintf( &psz_config, "http={host='%s'", psz_esc );
213                 free( psz_esc );
214                 free( psz_http_host );
215             }
216             if( psz_http_src )
217             {
218                 char *psz_esc = config_StringEscape( psz_http_src );
219                 if( psz_config )
220                 {
221                     char *psz_tmp;
222                     asprintf( &psz_tmp, "%s,dir='%s'", psz_config, psz_esc );
223                     free( psz_config );
224                     psz_config = psz_tmp;
225                 }
226                 else
227                     asprintf( &psz_config, "http={dir='%s'", psz_esc );
228                 free( psz_esc );
229                 free( psz_http_src );
230             }
231             if( psz_config )
232             {
233                 char *psz_tmp;
234                 asprintf( &psz_tmp, "%s,no_index=%s}", psz_config, b_http_index ? "true" : "false" );
235                 free( psz_config );
236                 psz_config = psz_tmp;
237             }
238             else
239                 asprintf( &psz_config, "http={no_index=%s}", b_http_index ? "true" : "false" );
240         }
241         else if( !strcmp( name, "telnet" ) )
242         {
243             char *psz_telnet_host = var_CreateGetString( p_intf, "telnet-host" );
244             if( !strcmp( psz_telnet_host, "*console" ) )
245                 ;
246             else
247             {
248                 vlc_url_t url;
249                 vlc_UrlParse( &url, psz_telnet_host, 0 );
250                 int i_telnet_port = var_CreateGetInteger( p_intf, "telnet-port" );
251                 if ( url.i_port != 0 )
252                 {
253                     if ( i_telnet_port == TELNETPORT_DEFAULT )
254                         i_telnet_port = url.i_port;
255                     else if ( url.i_port != i_telnet_port )
256                         msg_Warn( p_intf, "ignoring port %d (using %d)", url.i_port, i_telnet_port );
257                 }
258
259                 char *psz_esc_host = config_StringEscape( url.psz_host );
260                 free( psz_telnet_host );
261                 vlc_UrlClean( &url );
262
263                 asprintf( &psz_telnet_host, "telnet://%s:%d", psz_esc_host ? psz_esc_host : "", i_telnet_port );
264                 free( psz_esc_host );
265             }
266
267             char *psz_telnet_passwd = var_CreateGetString( p_intf, "telnet-password" );
268
269             char *psz_esc_passwd = config_StringEscape( psz_telnet_passwd );
270
271             asprintf( &psz_config, "telnet={host='%s',password='%s'}", psz_telnet_host, psz_esc_passwd );
272
273             free( psz_esc_passwd );
274             free( psz_telnet_passwd );
275             free( psz_telnet_host );
276         }
277         else if( !strcmp( name, "cli" ) )
278         {
279             char *psz_rc_host = var_CreateGetNonEmptyString( p_intf, "rc-host" );
280             if( !psz_rc_host )
281                 psz_rc_host = var_CreateGetNonEmptyString( p_intf, "cli-host" );
282             if( psz_rc_host )
283             {
284                 char *psz_esc_host = config_StringEscape( psz_rc_host );
285                 asprintf( &psz_config, "cli={host='%s'}", psz_esc_host );
286
287                 free( psz_esc_host );
288                 free( psz_rc_host );
289             }
290         }
291     }
292
293     if( psz_config )
294     {
295         char *psz_buffer;
296         if( asprintf( &psz_buffer, "config={%s}", psz_config ) != -1 )
297         {
298             char *psz_log = StripPasswords( psz_buffer );
299             if( psz_log != NULL )
300             {
301                 msg_Dbg( p_intf, "Setting config variable: %s", psz_log );
302                 free( psz_log );
303             }
304
305             if( luaL_dostring( L, psz_buffer ) == 1 )
306                 msg_Err( p_intf, "Error while parsing \"lua-config\"." );
307             free( psz_buffer );
308             lua_getglobal( L, "config" );
309             if( lua_istable( L, -1 ) )
310             {
311                 if( !strcmp( name, "cli" ) )
312                 {
313                     lua_getfield( L, -1, "rc" );
314                     if( lua_istable( L, -1 ) )
315                     {
316                         /* msg_Warn( p_intf, "The `rc' lua interface script "
317                                           "was renamed `cli', please update "
318                                           "your configuration!" ); */
319                         lua_setfield( L, -2, "cli" );
320                     }
321                     else
322                         lua_pop( L, 1 );
323                 }
324                 lua_getfield( L, -1, name );
325                 if( lua_istable( L, -1 ) )
326                 {
327                     lua_setglobal( L, "config" );
328                     b_config_set = true;
329                 }
330             }
331         }
332         free( psz_config );
333     }
334
335     if( !b_config_set )
336     {
337         lua_newtable( L );
338         lua_setglobal( L, "config" );
339     }
340
341     /* Wrapper for legacy telnet config */
342     if ( !strcmp( name, "telnet" ) )
343     {
344         /* msg_Warn( p_intf, "The `telnet' lua interface script was replaced "
345                           "by `cli', please update your configuration!" ); */
346
347         char *wrapped_file = vlclua_find_file( p_this, "intf", "cli" );
348         if( !wrapped_file )
349         {
350             msg_Err( p_intf, "Couldn't find lua interface script \"cli\", "
351                              "needed by telnet wrapper" );
352             lua_close( p_sys->L );
353             goto error;
354         }
355         lua_pushstring( L, wrapped_file );
356         lua_setglobal( L, "wrapped_file" );
357         free( wrapped_file );
358     }
359
360     p_sys->L = L;
361
362     /* Cleaned up by vlc_object_release() */
363     p_intf->psz_header = strdup( name );
364
365     vlc_mutex_init( &p_sys->lock );
366     vlc_cond_init( &p_sys->wait );
367     p_sys->exiting = false;
368
369     if( vlc_clone( &p_sys->thread, Run, p_intf, VLC_THREAD_PRIORITY_LOW ) )
370     {
371         free( p_intf->psz_header );
372         p_intf->psz_header = NULL;
373         vlc_cond_destroy( &p_sys->wait );
374         vlc_mutex_destroy( &p_sys->lock );
375         lua_close( p_sys->L );
376         goto error;
377     }
378
379     return VLC_SUCCESS;
380 error:
381     free( p_sys->psz_filename );
382     free( p_sys );
383     return VLC_EGENERIC;
384 }
385
386 void Close_LuaIntf( vlc_object_t *p_this )
387 {
388     intf_thread_t *p_intf = (intf_thread_t*)p_this;
389     intf_sys_t *p_sys = p_intf->p_sys;
390
391     vlc_cancel( p_sys->thread );
392
393     vlc_mutex_lock( &p_sys->lock );
394     p_sys->exiting = true;
395     vlc_cond_signal( &p_sys->wait );
396     vlc_mutex_unlock( &p_sys->lock );
397     vlc_join( p_sys->thread, NULL );
398     vlc_cond_destroy( &p_sys->wait );
399     vlc_mutex_destroy( &p_sys->lock );
400
401     lua_close( p_sys->L );
402
403     free( p_sys->psz_filename );
404     free( p_sys );
405 }
406
407 static void *Run( void *data )
408 {
409     intf_thread_t *p_intf = data;
410     intf_sys_t *p_sys = p_intf->p_sys;
411     lua_State *L = p_sys->L;
412
413     if( luaL_dofile( L, p_sys->psz_filename ) )
414     {
415         msg_Err( p_intf, "Error loading script %s: %s", p_sys->psz_filename,
416                  lua_tostring( L, lua_gettop( L ) ) );
417         lua_pop( L, 1 );
418     }
419     return NULL;
420 }
421
422 int Open_LuaIntf( vlc_object_t *p_this )
423 {
424     char *name = var_InheritString( p_this, "lua-intf" );
425     if( unlikely(name == NULL) )
426         return VLC_EGENERIC;
427
428     int ret = Start_LuaIntf( p_this, name );
429     free( name );
430     return ret;
431 }
432
433 int Open_LuaHTTP( vlc_object_t *p_this )
434 {
435     return Start_LuaIntf( p_this, "http" );
436 }
437
438 int Open_LuaCLI( vlc_object_t *p_this )
439 {
440     return Start_LuaIntf( p_this, "cli" );
441 }
442
443 int Open_LuaTelnet( vlc_object_t *p_this )
444 {
445     return Start_LuaIntf( p_this, "telnet" );
446 }