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