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