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