]> git.sesse.net Git - vlc/blob - modules/lua/intf.c
x265: update for API 6
[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     vlclua_fd_init( p_sys );
227
228     p_sys->psz_filename = vlclua_find_file( "intf", name );
229     if( !p_sys->psz_filename )
230     {
231         msg_Err( p_intf, "Couldn't find lua interface script \"%s\".",
232                  name );
233         goto error;
234     }
235     msg_Dbg( p_intf, "Found lua interface script: %s", p_sys->psz_filename );
236
237     L = luaL_newstate();
238     if( !L )
239     {
240         msg_Err( p_intf, "Could not create new Lua State" );
241         goto error;
242     }
243
244     vlclua_set_this( L, p_intf );
245     vlclua_set_playlist_internal( L, pl_Get(p_intf) );
246
247     luaL_openlibs( L );
248
249     /* register our functions */
250     luaL_register( L, "vlc", p_reg );
251
252     /* register submodules */
253     luaopen_config( L );
254     luaopen_httpd( L );
255     luaopen_input( L );
256     luaopen_msg( L );
257     luaopen_misc( L );
258     luaopen_net_intf( L );
259     luaopen_object( L );
260     luaopen_osd( L );
261     luaopen_playlist( L );
262     luaopen_sd( L );
263     luaopen_stream( L );
264     luaopen_strings( L );
265     luaopen_variables( L );
266     luaopen_video( L );
267     luaopen_vlm( L );
268     luaopen_volume( L );
269     luaopen_gettext( L );
270     luaopen_xml( L );
271     luaopen_equalizer( L );
272 #if defined(_WIN32) && !VLC_WINSTORE_APP
273     luaopen_win( L );
274 #endif
275
276     /* clean up */
277     lua_pop( L, 1 );
278
279     /* Setup the module search path */
280     if( vlclua_add_modules_path( L, p_sys->psz_filename ) )
281     {
282         msg_Warn( p_intf, "Error while setting the module search path for %s",
283                   p_sys->psz_filename );
284         lua_close( L );
285         goto error;
286     }
287
288     /*
289      * Get the lua-config string.
290      * If the string is empty, try with the old http-* or telnet-* options
291      * and build the right configuration line
292      */
293     bool b_config_set = false;
294     char *psz_config = var_InheritString( p_intf, "lua-config" );
295     if( !psz_config )
296         psz_config = MakeConfig( p_intf, name );
297
298     if( psz_config )
299     {
300         char *psz_buffer;
301         if( asprintf( &psz_buffer, "config={%s}", psz_config ) != -1 )
302         {
303             char *psz_log = StripPasswords( psz_buffer );
304             if( psz_log != NULL )
305             {
306                 msg_Dbg( p_intf, "Setting config variable: %s", psz_log );
307                 free( psz_log );
308             }
309
310             if( luaL_dostring( L, psz_buffer ) == 1 )
311                 msg_Err( p_intf, "Error while parsing \"lua-config\"." );
312             free( psz_buffer );
313             lua_getglobal( L, "config" );
314             if( lua_istable( L, -1 ) )
315             {
316                 if( !strcmp( name, "cli" ) )
317                 {
318                     lua_getfield( L, -1, "rc" );
319                     if( lua_istable( L, -1 ) )
320                     {
321                         /* msg_Warn( p_intf, "The `rc' lua interface script "
322                                           "was renamed `cli', please update "
323                                           "your configuration!" ); */
324                         lua_setfield( L, -2, "cli" );
325                     }
326                     else
327                         lua_pop( L, 1 );
328                 }
329                 lua_getfield( L, -1, name );
330                 if( lua_istable( L, -1 ) )
331                 {
332                     lua_setglobal( L, "config" );
333                     b_config_set = true;
334                 }
335             }
336         }
337         free( psz_config );
338     }
339
340     if( !b_config_set )
341     {
342         lua_newtable( L );
343         lua_setglobal( L, "config" );
344     }
345
346     /* Wrapper for legacy telnet config */
347     if ( !strcmp( name, "telnet" ) )
348     {
349         /* msg_Warn( p_intf, "The `telnet' lua interface script was replaced "
350                           "by `cli', please update your configuration!" ); */
351
352         char *wrapped_file = vlclua_find_file( "intf", "cli" );
353         if( !wrapped_file )
354         {
355             msg_Err( p_intf, "Couldn't find lua interface script \"cli\", "
356                              "needed by telnet wrapper" );
357             lua_close( p_sys->L );
358             goto error;
359         }
360         lua_pushstring( L, wrapped_file );
361         lua_setglobal( L, "wrapped_file" );
362         free( wrapped_file );
363     }
364
365     p_sys->L = L;
366
367 #ifndef _WIN32
368     if( vlc_pipe( p_sys->fd ) )
369     {
370         lua_close( p_sys->L );
371         goto error;
372     }
373 #else
374 # define close(fd) (void)0
375 #endif
376
377     if( vlc_clone( &p_sys->thread, Run, p_intf, VLC_THREAD_PRIORITY_LOW ) )
378     {
379         close( p_sys->fd[1] );
380         close( p_sys->fd[0] );
381         lua_close( p_sys->L );
382         goto error;
383     }
384
385     return VLC_SUCCESS;
386 error:
387     free( p_sys->psz_filename );
388     free( p_sys );
389     free( p_intf->psz_header );
390     p_intf->psz_header = NULL;
391     return VLC_EGENERIC;
392 }
393
394 void Close_LuaIntf( vlc_object_t *p_this )
395 {
396     intf_thread_t *p_intf = (intf_thread_t*)p_this;
397     intf_sys_t *p_sys = p_intf->p_sys;
398
399     close( p_sys->fd[1] );
400     vlc_join( p_sys->thread, NULL );
401
402     lua_close( p_sys->L );
403     close( p_sys->fd[0] );
404     vlclua_fd_destroy( p_sys );
405     free( p_sys->psz_filename );
406     free( p_sys );
407 }
408
409 static void *Run( void *data )
410 {
411     intf_thread_t *p_intf = data;
412     intf_sys_t *p_sys = p_intf->p_sys;
413     lua_State *L = p_sys->L;
414
415     if( luaL_dofile( L, p_sys->psz_filename ) )
416     {
417         msg_Err( p_intf, "Error loading script %s: %s", p_sys->psz_filename,
418                  lua_tostring( L, lua_gettop( L ) ) );
419         lua_pop( L, 1 );
420     }
421     return NULL;
422 }
423
424 int Open_LuaIntf( vlc_object_t *p_this )
425 {
426     return Start_LuaIntf( p_this, NULL );
427 }
428
429 int Open_LuaHTTP( vlc_object_t *p_this )
430 {
431     return Start_LuaIntf( p_this, "http" );
432 }
433
434 int Open_LuaCLI( vlc_object_t *p_this )
435 {
436     return Start_LuaIntf( p_this, "cli" );
437 }
438
439 int Open_LuaTelnet( vlc_object_t *p_this )
440 {
441     char *pw = var_CreateGetNonEmptyString( p_this, "telnet-password" );
442     if( pw == NULL )
443     {
444         msg_Err( p_this, "password not configured" );
445         msg_Info( p_this, "Please specify the password in the preferences." );
446         return VLC_EGENERIC;
447     }
448     free( pw );
449     return Start_LuaIntf( p_this, "telnet" );
450 }